odepkg-0.8.5/COPYING0000644000000000000000000004307712526637474012211 0ustar 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; either version 2 of the License, or (at your option) any later version. 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 . Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. odepkg-0.8.5/DESCRIPTION0000644000000000000000000000053412526637474012653 0ustar 00000000000000Name: OdePkg Version: 0.8.5 Date: 2015-05-19 Author: Thomas Treichl Maintainer: Jacopo Corno Title: OdePkg Description: A package for solving ordinary differential equations and more. Categories: Differential Equations Depends: octave (>= 3.8.0) License: GPLv2+ Url: http://octave.sf.net odepkg-0.8.5/INDEX0000644000000000000000000000165012526637474011737 0ustar 00000000000000odepkg >> OdePkg OdePkg Tutorial odepkg OdePkg ODE Solver Functions ode23 ode23s ode45 ode54 ode78 OdePkg DAE Solver Functions ode2r ode5r odebda odebwe oders odesx OdePkg IDE Solver Functions odebdi odekdi OdePkg DDE Solver Functions ode23d ode45d ode54d ode78d OdePkg Options Functions odeset odeget OdePkg Output Functions odeplot odeprint odephas2 odephas3 OdePkg Example Functions odeexamples odepkg_examples_dae odepkg_examples_dde odepkg_examples_ide odepkg_examples_ode OdePkg Testsuite Functions odepkg_testsuite_calcscd odepkg_testsuite_calcmescd odepkg_testsuite_chemakzo odepkg_testsuite_hires odepkg_testsuite_implakzo odepkg_testsuite_implrober odepkg_testsuite_impltrans odepkg_testsuite_oregonator odepkg_testsuite_pollution odepkg_testsuite_robertson odepkg_testsuite_transistor OdePkg Internal Functions odepkg_event_handle odepkg_structure_check OdePkg Other Functions bvp4codepkg-0.8.5/Makefile0000644000000000000000000000116712526637474012610 0ustar 00000000000000# Filename: Makefile # Description: Makefile for OdePkg # ChangeLog: 20070222, this Makefile was originally be created # from the Makefile of the comm package. Modifications have # been done to create OdePkg. sinclude ../../Makeconf PKG_FILES = COPYING DESCRIPTION INDEX $(wildcard src/*) $(wildcard inst/*) \ doc/odepkg.pdf doc/Makefile $(wildcard doc/*.texi) SUBDIRS = doc/ .PHONY : $(SUBDIRS) pre-pkg:: @for _dir in $(SUBDIRS); do \ $(MAKE) -C $$_dir all; \ done clean : @for _dir in $(SUBDIRS); do \ $(MAKE) -C $$_dir $(MAKECMDGOALS); \ done $(RM) *~ octave-core realclean : clean distclean : clean odepkg-0.8.5/NEWS0000644000000000000000000000165212526637474011646 0ustar 00000000000000Summary of important user-visible changes for releases of the odepkg package ==================================================================================== odepkg-0.8.5 Release Date: 2015-05-19 Release Manager: Jacopo Corno ==================================================================================== ** enable to work on octave 4.0 (thanks to Tatsuro Matsuoka) See discussuion on the octave-maintainers list http://octave.1599824.n4.nabble.com/Octave-Forge-Octave-4-0-call-for-packages-td4669204i20.html#a4669709 =============================================================================== odepkg-0.8.4 Release Date: 2013-02-22 Release Manager: Thomas Treichl =============================================================================== ** Added new function ode23s. ** Makefile fixed to work with non-standard linker options e.g on Apple. ** Package is no longer automatically loaded. odepkg-0.8.5/doc/Makefile0000644000000000000000000000450212526637474013351 0ustar 00000000000000# Filename: Makefile # Description: Makefile for the doc directory of the OdePkg # ChangeLog: 20070222, this Makefile was originally be created # from the Makefile of the comm package. Modifications have # been done to create the documentation of the OdePkg. sinclude ../../../Makeconf # Fill in the variables as it makes testing the package manager easier ifeq ($(MKDOC),) MKDOC = ../../../admin/mkdoc MKTEXI = ../../../admin/mktexi MAKEINFO = makeinfo --no-split --document-language=en TEXI2DVI = texi2dvi --clean DVIPS = dvips LN_S = ln -s endif INFODOC = odepkg.info PSDOC = $(patsubst %.info, %.ps, $(INFODOC)) PDFDOC = $(patsubst %.info, %.pdf, $(INFODOC)) HTMLDOC = $(patsubst %.info, %.html, $(INFODOC)) TEXIDOC = $(patsubst %.info, %.txi, $(INFODOC)) DOCS = $(INFODOC) $(PDFDOC) DOCSTRINGS = DOCSTRINGS INDEX = ../INDEX TMPDELETES = *.log *.dvi $(DOCSTRINGS) $(TEXIDOC) *~ DELETES = $(TMPDELETES) *.ps *.pdf *.info $(DOCS) *.html odepkg/ html/ all : $(PDFDOC) $(HTMLDOC) ../inst/doc.info .PHONY : all ../inst/doc.info : $(INFODOC) cp -f $(INFODOC) ../inst/doc.info ifeq (,$(TEXI2PDF)) %.pdf : %.dvi @if test "x$(TEXI2DVI)" != "x" && test "x$(DVIPDF)" != "x"; then \ echo "Making pdf $@"; \ $(DVIPDF) $< ; \ fi %.dvi : %.texi @if test "x$(TEXI2DVI)" != "x"; then \ echo "Making dvi $@"; \ TEXINPUTS="./:$../../..:$(TEXINPUTS):"; \ export TEXINPUTS; \ $(TEXI2DVI) $< ; \ fi %.ps : %.dvi @if test "x$(TEXI2DVI)" != "x" && test "x$(DVIPS)" != "x"; then \ echo "Making postscript $@"; \ $(DVIPS) -o $@ $< ; \ fi else %.pdf : %.texi @if test "x$(TEXI2PDF)" != "x"; then \ echo "Making pdf $@"; \ TEXINPUTS="./:../../..:$(TEXINPUTS):"; \ export TEXINPUTS; \ $(TEXI2PDF) $< ; \ fi endif %.info : %.texi @if test "x$(MAKEINFO)" != "x"; then \ echo "Making info $@"; \ $(MAKEINFO) -I./ -I../../../ $< ; \ fi %.html : %.texi @if test "x$(MAKEINFO)" != "x"; then \ echo "Making html $@"; \ $(MAKEINFO) --html -I./ -I../../../ $< ; \ fi %.texi : %.txi $(RM) -f $(DOCSTRINGS); \ $(MKDOC) ../ > $(DOCSTRINGS); \ $(MKTEXI) $< $(DOCSTRINGS) $(INDEX) > $@ ; \ $(RM) -f $(DOCSTRINGS); clean : @echo "Cleaning..."; \ $(RM) -fr $(DELETES) ../inst/doc.info; .PHONY : clean realclean : clean .PHONY : realclean distclean : clean .PHONY : distclean dist : all .PHONY : dist odepkg-0.8.5/doc/dldfunref.texi0000644000000000000000000004527312526637474014567 0ustar 00000000000000@deftypefn {Command} {[@var{}] =} odebda (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} odebda (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odebda (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff or stiff ordinary differential equations (ODEs) and non--stiff or stiff differential algebraic equations (DAEs). This function file is a wrapper file that uses Jeff Cash's Fortran solver @file{mebdfdae.f}. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, @example function y = odepkg_equations_lorenz (t, x) y = [10 * (x(2) - x(1)); x(1) * (28 - x(3)); x(1) * x(2) - 8/3 * x(3)]; endfunction vopt = odeset ("InitialStep", 1e-3, "MaxStep", 1e-1, \\ "OutputFcn", @@odephas3, "Refine", 5); odebda (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt); @end example @end deftypefn @deftypefn {Command} {[@var{}] =} odebdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} odebdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odebdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}]) This function file can be used to solve a set of non--stiff and stiff implicit differential equations (IDEs). This function file is a wrapper file that uses Jeff Cash's Fortran solver @file{mebdfi.f}. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of IDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{y0} is a double vector that defines the initial values of the states, @var{dy0} is a double vector that defines the initial values of the derivatives, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of IDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, @example function res = odepkg_equations_ilorenz (t, y, yd) res = [10 * (y(2) - y(1)) - yd(1); y(1) * (28 - y(3)) - yd(2); y(1) * y(2) - 8/3 * y(3) - yd(3)]; endfunction vopt = odeset ("InitialStep", 1e-3, "MaxStep", 1e-1, \\ "OutputFcn", @@odephas3, "Refine", 5); odebdi (@@odepkg_equations_ilorenz, [0, 25], [3 15 1], \\ [120 81 42.333333], vopt); @end example @end deftypefn @deftypefn {Command} {[@var{}] =} odekdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} odekdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odekdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}]) This function file can be used to solve a set of non--stiff or stiff implicit differential equations (IDEs). This function file is a wrapper file that uses the direct method (not the Krylov method) of Petzold's, Brown's, Hindmarsh's and Ulrich's Fortran solver @file{ddaskr.f}. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of IDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{y0} is a double vector that defines the initial values of the states, @var{dy0} is a double vector that defines the initial values of the derivatives, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of IDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, @example function res = odepkg_equations_ilorenz (t, y, yd) res = [10 * (y(2) - y(1)) - yd(1); y(1) * (28 - y(3)) - yd(2); y(1) * y(2) - 8/3 * y(3) - yd(3)]; endfunction vopt = odeset ("InitialStep", 1e-3, "MaxStep", 1e-1, \\ "OutputFcn", @@odephas3, "Refine", 5); odekdi (@@odepkg_equations_ilorenz, [0, 25], [3 15 1], \\ [120 81 42.333333], vopt); @end example @end deftypefn @deftypefn {Command} {[@var{}] =} ode2r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode2r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode2r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff or stiff ordinary differential equations (ODEs) and non--stiff or stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{radau.f}. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, @example function y = odepkg_equations_lorenz (t, x) y = [10 * (x(2) - x(1)); x(1) * (28 - x(3)); x(1) * x(2) - 8/3 * x(3)]; endfunction vopt = odeset ("InitialStep", 1e-3, "MaxStep", 1e-1, \\ "OutputFcn", @@odephas3, "Refine", 5); ode2r (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt); @end example @end deftypefn @deftypefn {Command} {[@var{}] =} ode5r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode5r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode5r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff or stiff ordinary differential equations (ODEs) and non--stiff or stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{radau5.f}. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, @example function y = odepkg_equations_lorenz (t, x) y = [10 * (x(2) - x(1)); x(1) * (28 - x(3)); x(1) * x(2) - 8/3 * x(3)]; endfunction vopt = odeset ("InitialStep", 1e-3, "MaxStep", 1e-1, \\ "OutputFcn", @@odephas3, "Refine", 5); ode5r (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{}] =} oders (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} oders (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} oders (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff or stiff ordinary differential equations (ODEs) and non--stiff or stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{rodas.f}. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, @example function y = odepkg_equations_lorenz (t, x) y = [10 * (x(2) - x(1)); x(1) * (28 - x(3)); x(1) * x(2) - 8/3 * x(3)]; endfunction vopt = odeset ("InitialStep", 1e-3, "MaxStep", 1e-1, \\ "OutputFcn", @@odephas3, "Refine", 5); oders (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt); @end example @end deftypefn @deftypefn {Command} {[@var{}] =} odesx (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} odesx (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odesx (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of stiff or non--stiff ordinary differential equations (ODEs) and non--stiff or stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{seulex.f}. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, @example function y = odepkg_equations_lorenz (t, x) y = [10 * (x(2) - x(1)); x(1) * (28 - x(3)); x(1) * x(2) - 8/3 * x(3)]; endfunction vopt = odeset ("InitialStep", 1e-3, "MaxStep", 1e-1, \\ "OutputFcn", @@odephas3, "Refine", 5); odesx (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt); @end example @end deftypefn odepkg-0.8.5/doc/mfunref.texi0000644000000000000000000014171112526637474014252 0ustar 00000000000000@deftypefn {Function File} {[@var{}] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (2,3). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, solve an anonymous implementation of the Van der Pol equation @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ "NormControl", "on", "OutputFcn", @@odeplot); ode23 (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{}] =} ode23d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode23d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode23d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (2,3). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. In other words, this function will solve a problem of the form @example dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) y(slot(1)) = init y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} @end example If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example: @itemize @minus @item the following code solves an anonymous implementation of a chaotic behavior @example fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; vopt = odeset ("NormControl", "on", "RelTol", 1e-3); vsol = ode23d (fcao, [0, 100], 0.5, 2, 0.5, vopt); vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); plot (vsol.y, vlag); legend ("fcao (t,y,z)"); @end example @item to solve the following problem with two delayed state variables @example d y1(t)/dt = -y1(t) d y2(t)/dt = -y2(t) + y1(t-5) d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) @end example one might do the following @example function f = fun (t, y, yd) f(1) = -y(1); %% y1' = -y1(t) f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) endfunction T = [0,20] res = ode23d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); @end example @end itemize @end deftypefn @deftypefn {Function File} {[@var{}] =} ode45 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode45 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode45 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (4,5). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, solve an anonymous implementation of the Van der Pol equation @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ "NormControl", "on", "OutputFcn", @@odeplot); ode45 (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{}] =} ode45d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode45d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode45d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (4,5). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. In other words, this function will solve a problem of the form @example dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) y(slot(1)) = init y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} @end example If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example: @itemize @minus @item the following code solves an anonymous implementation of a chaotic behavior @example fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; vopt = odeset ("NormControl", "on", "RelTol", 1e-3); vsol = ode45d (fcao, [0, 100], 0.5, 2, 0.5, vopt); vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); plot (vsol.y, vlag); legend ("fcao (t,y,z)"); @end example @item to solve the following problem with two delayed state variables @example d y1(t)/dt = -y1(t) d y2(t)/dt = -y2(t) + y1(t-5) d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) @end example one might do the following @example function f = fun (t, y, yd) f(1) = -y(1); %% y1' = -y1(t) f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) endfunction T = [0,20] res = ode45d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); @end example @end itemize @end deftypefn @deftypefn {Function File} {[@var{}] =} ode54 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode54 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode54 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (5,4). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, solve an anonymous implementation of the Van der Pol equation @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ "NormControl", "on", "OutputFcn", @@odeplot); ode54 (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{}] =} ode54d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode54d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode54d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (2,3). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. In other words, this function will solve a problem of the form @example dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) y(slot(1)) = init y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} @end example If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example: @itemize @minus @item the following code solves an anonymous implementation of a chaotic behavior @example fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; vopt = odeset ("NormControl", "on", "RelTol", 1e-3); vsol = ode54d (fcao, [0, 100], 0.5, 2, 0.5, vopt); vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); plot (vsol.y, vlag); legend ("fcao (t,y,z)"); @end example @item to solve the following problem with two delayed state variables @example d y1(t)/dt = -y1(t) d y2(t)/dt = -y2(t) + y1(t-5) d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) @end example one might do the following @example function f = fun (t, y, yd) f(1) = -y(1); %% y1' = -y1(t) f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) endfunction T = [0,20] res = ode54d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); @end example @end itemize @end deftypefn @deftypefn {Function File} {[@var{}] =} ode78 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode78 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode78 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (7,8). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, solve an anonymous implementation of the Van der Pol equation @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ "NormControl", "on", "OutputFcn", @@odeplot); ode78 (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{}] =} ode78d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} ode78d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode78d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (7,8). If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. In other words, this function will solve a problem of the form @example dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) y(slot(1)) = init y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} @end example If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example: @itemize @minus @item the following code solves an anonymous implementation of a chaotic behavior @example fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; vopt = odeset ("NormControl", "on", "RelTol", 1e-3); vsol = ode78d (fcao, [0, 100], 0.5, 2, 0.5, vopt); vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); plot (vsol.y, vlag); legend ("fcao (t,y,z)"); @end example @item to solve the following problem with two delayed state variables @example d y1(t)/dt = -y1(t) d y2(t)/dt = -y2(t) + y1(t-5) d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) @end example one might do the following @example function f = fun (t, y, yd) f(1) = -y(1); %% y1' = -y1(t) f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) endfunction T = [0,20] res = ode78d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); @end example @end itemize @end deftypefn @deftypefn {Function File} {[@var{}] =} odebwe (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{sol}] =} odebwe (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odebwe (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) This function file can be used to solve a set of stiff ordinary differential equations (stiff ODEs) or stiff differential algebraic equations (stiff DAEs) with the Backward Euler method. If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. For example, solve an anonymous implementation of the Van der Pol equation @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vjac = @@(vt,vy) [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ "NormControl", "on", "OutputFcn", @@odeplot, \ "Jacobian",vjac); odebwe (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{}] =} odeexamples (@var{}) Open the differential equations examples menu and allow the user to select a submenu of ODE, DAE, IDE or DDE examples. @end deftypefn @deftypefn {Function File} {[@var{value}] =} odeget (@var{odestruct}, @var{option}, [@var{default}]) @deftypefnx {Command} {[@var{values}] =} odeget (@var{odestruct}, @{@var{opt1}, @var{opt2}, @dots{}@}, [@{@var{def1}, @var{def2}, @dots{}@}]) If this function is called with two input arguments and the first input argument @var{odestruct} is of type structure array and the second input argument @var{option} is of type string then return the option value @var{value} that is specified by the option name @var{option} in the OdePkg option structure @var{odestruct}. Optionally if this function is called with a third input argument then return the default value @var{default} if @var{option} is not set in the structure @var{odestruct}. If this function is called with two input arguments and the first input argument @var{odestruct} is of type structure array and the second input argument @var{option} is of type cell array of strings then return the option values @var{values} that are specified by the option names @var{opt1}, @var{opt2}, @dots{} in the OdePkg option structure @var{odestruct}. Optionally if this function is called with a third input argument of type cell array then return the default value @var{def1} if @var{opt1} is not set in the structure @var{odestruct}, @var{def2} if @var{opt2} is not set in the structure @var{odestruct}, @dots{} Run examples with the command @example demo odeget @end example @end deftypefn @deftypefn {Function File} {[@var{ret}] =} odephas2 (@var{t}, @var{y}, @var{flag}) Open a new figure window and plot the first result from the variable @var{y} that is of type double column vector over the second result from the variable @var{y} while solving. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is @table @option @item @code{"init"} then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, @item @code{""} then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', @item @code{"done"} then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. @end table This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. For example, solve an anonymous implementation of the "Van der Pol" equation and display the results while solving in a 2D plane @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vopt = odeset ('OutputFcn', @@odephas2, 'RelTol', 1e-6); vsol = ode45 (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{ret}] =} odephas3 (@var{t}, @var{y}, @var{flag}) Open a new figure window and plot the first result from the variable @var{y} that is of type double column vector over the second and the third result from the variable @var{y} while solving. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is @table @option @item @code{"init"} then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, @item @code{""} then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', @item @code{"done"} then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. @end table This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. For example, solve the "Lorenz attractor" and display the results while solving in a 3D plane @example function vyd = florenz (vt, vx) vyd = [10 * (vx(2) - vx(1)); vx(1) * (28 - vx(3)); vx(1) * vx(2) - 8/3 * vx(3)]; endfunction vopt = odeset ('OutputFcn', @@odephas3); vsol = ode23 (@@florenz, [0:0.01:7.5], [3 15 1], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{}] =} odepkg () OdePkg is part of the GNU Octave Repository (the Octave--Forge project). The package includes commands for setting up various options, output functions etc. before solving a set of differential equations with the solver functions that are also included. At this time OdePkg is under development with the main target to make a package that is mostly compatible to proprietary solver products. If this function is called without any input argument then open the OdePkg tutorial in the Octave window. The tutorial can also be opened with the following command @example doc odepkg @end example @end deftypefn @deftypefn {Function File} {[@var{sol}] =} odepkg_event_handle (@var{@@fun}, @var{time}, @var{y}, @var{flag}, [@var{par1}, @var{par2}, @dots{}]) Return the solution of the event function that is specified as the first input argument @var{@@fun} in form of a function handle. The second input argument @var{time} is of type double scalar and specifies the time of the event evaluation, the third input argument @var{y} either is of type double column vector (for ODEs and DAEs) and specifies the solutions or is of type cell array (for IDEs and DDEs) and specifies the derivatives or the history values, the third input argument @var{flag} is of type string and can be of the form @table @option @item @code{"init"} then initialize internal persistent variables of the function @command{odepkg_event_handle} and return an empty cell array of size 4, @item @code{"calc"} then do the evaluation of the event function and return the solution @var{sol} as type cell array of size 4, @item @code{"done"} then cleanup internal variables of the function @command{odepkg_event_handle} and return an empty cell array of size 4. @end table Optionally if further input arguments @var{par1}, @var{par2}, @dots{} of any type are given then pass these parameters through @command{odepkg_event_handle} to the event function. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. @end deftypefn @deftypefn {Function File} {[@var{}] =} odepkg_examples_dae (@var{}) Open the DAE examples menu and allow the user to select a demo that will be evaluated. @end deftypefn @deftypefn {Function File} {[@var{}] =} odepkg_examples_dde (@var{}) Open the DDE examples menu and allow the user to select a demo that will be evaluated. @end deftypefn @deftypefn {Function File} {[@var{}] =} odepkg_examples_ide (@var{}) Open the IDE examples menu and allow the user to select a demo that will be evaluated. @end deftypefn @deftypefn {Function File} {[@var{}] =} odepkg_examples_ode (@var{}) Open the ODE examples menu and allow the user to select a demo that will be evaluated. @end deftypefn @deftypefn {Function File} {[@var{newstruct}] =} odepkg_structure_check (@var{oldstruct}, [@var{"solver"}]) If this function is called with one input argument of type structure array then check the field names and the field values of the OdePkg structure @var{oldstruct} and return the structure as @var{newstruct} if no error is found. Optionally if this function is called with a second input argument @var{"solver"} of type string taht specifies the name of a valid OdePkg solver then a higher level error detection is performed. The function does not modify any of the field names or field values but terminates with an error if an invalid option or value is found. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. Run examples with the command @example demo odepkg_structure_check @end example @end deftypefn @deftypefn {Function File} {[@var{mescd}] =} odepkg_testsuite_calcmescd (@var{solution}, @var{reference}, @var{abstol}, @var{reltol}) If this function is called with four input arguments of type double scalar or column vector then return a normalized value for the minimum number of correct digits @var{mescd} that is calculated from the solution at the end of an integration interval @var{solution} and a set of reference values @var{reference}. The input arguments @var{abstol} and @var{reltol} are used to calculate a reference solution that depends on the relative and absolute error tolerances. Run examples with the command @example demo odepkg_testsuite_calcmescd @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{scd}] =} odepkg_testsuite_calcscd (@var{solution}, @var{reference}, @var{abstol}, @var{reltol}) If this function is called with four input arguments of type double scalar or column vector then return a normalized value for the minimum number of correct digits @var{scd} that is calculated from the solution at the end of an integration interval @var{solution} and a set of reference values @var{reference}. The input arguments @var{abstol} and @var{reltol} are unused but present because of compatibility to the function @command{odepkg_testsuite_calcmescd}. Run examples with the command @example demo odepkg_testsuite_calcscd @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_chemakzo (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the chemical AKZO Nobel testsuite of differential algebraic equations after solving (DAE--test). Run examples with the command @example demo odepkg_testsuite_chemakzo @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_hires (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the HIRES testsuite of ordinary differential equations after solving (ODE--test). Run examples with the command @example demo odepkg_testsuite_hires @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_implakzo (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the chemical AKZO Nobel testsuite of implicit differential algebraic equations after solving (IDE--test). Run examples with the command @example demo odepkg_testsuite_implakzo @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_implrober (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the implicit form of the modified ROBERTSON testsuite of implicit differential algebraic equations after solving (IDE--test). Run examples with the command @example demo odepkg_testsuite_implrober @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_oregonator (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the OREGONATOR testsuite of ordinary differential equations after solving (ODE--test). Run examples with the command @example demo odepkg_testsuite_oregonator @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_pollution (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return the cell array @var{solution} with performance informations about the POLLUTION testsuite of ordinary differential equations after solving (ODE--test). Run examples with the command @example demo odepkg_testsuite_pollution @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_robertson (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the modified ROBERTSON testsuite of differential algebraic equations after solving (DAE--test). Run examples with the command @example demo odepkg_testsuite_robertson @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_transistor (@var{@@solver}, @var{reltol}) If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return the cell array @var{solution} with performance informations about the TRANSISTOR testsuite of differential algebraic equations after solving (DAE--test). Run examples with the command @example demo odepkg_testsuite_transistor @end example This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. @end deftypefn @deftypefn {Function File} {[@var{ret}] =} odeplot (@var{t}, @var{y}, @var{flag}) Open a new figure window and plot the results from the variable @var{y} of type column vector over time while solving. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is @table @option @item @code{"init"} then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, @item @code{""} then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', @item @code{"done"} then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. @end table This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. For example, solve an anonymous implementation of the "Van der Pol" equation and display the results while solving @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vopt = odeset ('OutputFcn', @@odeplot, 'RelTol', 1e-6); vsol = ode45 (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{ret}] =} odeprint (@var{t}, @var{y}, @var{flag}) Display the results of the set of differential equations in the Octave window while solving. The first column of the screen output shows the actual time stamp that is given with the input arguemtn @var{t}, the following columns show the results from the function evaluation that are given by the column vector @var{y}. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is @table @option @item @code{"init"} then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, @item @code{""} then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', @item @code{"done"} then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. @end table This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. For example, solve an anonymous implementation of the "Van der Pol" equation and print the results while solving @example fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; vopt = odeset ('OutputFcn', @@odeprint, 'RelTol', 1e-6); vsol = ode45 (fvdb, [0 20], [2 0], vopt); @end example @end deftypefn @deftypefn {Function File} {[@var{odestruct}] =} odeset () @deftypefnx {Command} {[@var{odestruct}] =} odeset (@var{"field1"}, @var{value1}, @var{"field2"}, @var{value2}, @dots{}) @deftypefnx {Command} {[@var{odestruct}] =} odeset (@var{oldstruct}, @var{"field1"}, @var{value1}, @var{"field2"}, @var{value2}, @dots{}) @deftypefnx {Command} {[@var{odestruct}] =} odeset (@var{oldstruct}, @var{newstruct}) If this function is called without an input argument then return a new OdePkg options structure array that contains all the necessary fields and sets the values of all fields to default values. If this function is called with string input arguments @var{"field1"}, @var{"field2"}, @dots{} identifying valid OdePkg options then return a new OdePkg options structure with all necessary fields and set the values of the fields @var{"field1"}, @var{"field2"}, @dots{} to the values @var{value1}, @var{value2}, @dots{} If this function is called with a first input argument @var{oldstruct} of type structure array then overwrite all values of the options @var{"field1"}, @var{"field2"}, @dots{} of the structure @var{oldstruct} with new values @var{value1}, @var{value2}, @dots{} and return the modified structure array. If this function is called with two input argumnets @var{oldstruct} and @var{newstruct} of type structure array then overwrite all values in the fields from the structure @var{oldstruct} with new values of the fields from the structure @var{newstruct}. Empty values of @var{newstruct} will not overwrite values in @var{oldstruct}. For a detailed explanation about valid fields and field values in an OdePkg structure aaray have a look at the @file{odepkg.pdf}, Section 'ODE/DAE/IDE/DDE options' or run the command @command{doc odepkg} to open the tutorial. Run examples with the command @example demo odeset @end example @end deftypefn odepkg-0.8.5/doc/odepkg.texi0000644000000000000000000017645212526637474014073 0ustar 00000000000000\input texinfo @c -*-texinfo-*- @c Copyright (c) 2006-2012, Thomas Treichl @c OdePkg - A package for solving ordinary differential equations and more @c For manually generating the documentation use @c LANGUAGE=en makeinfo --html --no-split odepkg.texi @c %*** Start of HEADER @setfilename odepkg.info @settitle OdePkg - A package for solving ordinary differential equations and more @afourpaper @set VERSION 0.8.5 @c @afourwide @c %*** End of the HEADER @c %*** Start of TITLEPAGE @titlepage @title OdePkg @value{VERSION} @subtitle A package for solving ordinary differential equations and more @c @subtitle @b{OdePkg and this document currently are under development} @author by Thomas Treichl @page @vskip 0pt plus 1filll Copyright @copyright{} 2006-2012, Thomas Treichl Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the same conditions as for modified versions. @end titlepage @c %*** End of TITLEPAGE @c %*** Start of BODY @contents @ifnottex @node Top, Beginners Guide, (dir), (dir) @top Copyright Copyright @copyright{} 2006-2012, Thomas Treichl Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the same conditions as for modified versions. @end ifnottex @menu * Beginners Guide:: Manual for users who are completely new to OdePkg * Users Guide:: Manual for users who are already familiar with OdePkg * Programmers Guide:: Manual for users who want to make changes to OdePkg * Function Index:: Reference about all functions from this package * Index:: OdePkg Reference @end menu @c %*** Start of first chapter: Beginners Guide @node Beginners Guide, Users Guide, Top, Top @chapter Beginners Guide @cindex Beginners guide The ``Beginners Guide'' is intended for users who are new to OdePkg and who want to solve differential equations with the Octave language and the package OdePkg. In this section it will be explained what OdePkg is about in @ref{About OdePkg} and how OdePkg grew up from the beginning in @ref{OdePkg history and roadmap}. In @ref{Installation and deinstallation} it is explained how OdePkg can be installed in Octave and how it can later be removed from Octave if it is not needed anymore. If you encounter problems while using OdePkg then have a look at @ref{Reporting Bugs} how these bugs can be reported. In the @ref{The "foo" example} a first example is explained. @menu * About OdePkg:: An introduction about OdePkg * OdePkg history and roadmap:: From the first OdePkg release to the future * Installation and deinstallation:: Setting up OdePkg on your system * Reporting Bugs:: Writing comments and bugs to the help list * The "foo" example:: A first example and where to go from here @end menu @node About OdePkg, OdePkg history and roadmap, Beginners Guide, Beginners Guide @section About OdePkg @cindex About OdePkg OdePkg is part of the @b{Octave Repository} (resp. the Octave--Forge project) that was initiated by Matthew W. Roberts and Paul Kienzle in the year 2000 and that is hosted at @url{http://octave.sourceforge.net}. Since then a lot of contributors joined this project and added a lot more packages and functions to further extend the capabilities of GNU Octave. @c The email from Matthew W. Roberts about Octave--forge can be found here: @c http://velveeta.che.wisc.edu/octave/lists/archive/octave-sources.2000/msg00110.html OdePkg includes commands for setting up various options, output functions etc. before solving a set of differential equations with the solver functions that are included. The package formerly was initiated in autumn 2006 to solve ordinary differential equations (ODEs) only but there are already improvements so that differential algebraic equations (DAEs) in explicit form and in implicit form (IDEs) and delay differential equations (DDEs) can also be solved. The goal of OdePkg is to have a package for solving differential equations that is mostly compatible to proprietary solver products. @node OdePkg history and roadmap, Installation and deinstallation, About OdePkg, Beginners Guide @section OdePkg history and roadmap @cindex history @cindex roadmap @multitable @columnfractions .25 .75 @item OdePkg Version 0.0.1 @tab The initial release was a modification of the old ``ode'' package that is hosted at Octave--Forge and that was written by Marc Compere somewhen between 2000 and 2001. The four variable step--size Runge--Kutta algorithms in three solver files and the three fixed step--size solvers have been merged. It was possible to set some options for these solvers. The four output--functions (@command{odeprint}, @command{odeplot}, @command{odephas2} and @command{odephas3}) have been added along with other examples that initially have not been there. @item OdePkg Version 0.1.x @tab The major milestone along versions 0.1.x was that four stable solvers have been implemented (ie. @command{ode23}, @command{ode45}, @command{ode54} and @command{ode78}) supporting all options that can be set for these kind of solvers and also all necessary functions for setting their options (eg. @command{odeset}, @command{odepkg_structure_check, odepkg_event_handle}). Since version 0.1.3 there is also source code available that interfaces the Fortran solver @file{dopri5.f} (that is written by Ernst Hairer and Gerhard Wanner, cf. @file{odepkg_mexsolver_dopri5.c} and the helper files @file{odepkgext.c} and @file{odepkgmex.c}). @item OdePkg Version 0.2.x @tab The main work along version 0.2.x was to make the interface functions for the non--stiff and stiff solvers from Ernst Hairer and Gerhard Wanner enough stable so that they could be compiled and installed by default. Wrapper functions have been added to the package containing a help text and test functions (eg. @command{ode2r}, @command{ode5r}, @command{oders}). Six testsuite functions have been added to check the performance of the different solvers (eg. @command{odepkg_testsuite_chemakzo}, @command{odepkg_testsuite_oregonator}). @item OdePkg Version 0.3.x @tab Fixed some minor bugs along version 0.3.x. Thanks to Jeff Cash, who released his Fortran @command{mebdfX} solvers under the GNU GPL V2 after some discussion. The first IDE solver @command{odebdi} appeared that is an interface function for Cash's @command{mebdfi} Fortran core solver. With version 0.3.5 of OdePkg a first new interface function was created based on Octave's C++ @code{DEFUN_DLD} interface to achieve the highest performance available. Added more examples and testsuite functions (eg. @command{odepkg_equations_ilorenz}, @command{odepkg_testsuite_implrober}). Porting all at this time present Mex--file solvers to Octave's C++ @code{DEFUN_DLD} interface. Ongoing work with this manual. @item OdePkg Version 0.4.x @tab Added a new solver function @command{odekdi} for the direct method (not the Krylov method) of the @file{daskr.f} solver from the authors Peter N. Brown, Alan C. Hindmarsh, Linda R. Petzold and Clement W. Ulrich that is available under a modified BSD license (without advertising clause). Ongoing work with this manual. @item OdePkg Version 0.5.x @tab Added new solver functions @command{ode23d}, @command{ode45d}, @command{ode54d} and @command{ode78d} for solving non--stiff delay differential equations (non-stiff DDEs). These solvers are based on the Runge--Kutta solvers @command{ode23}..@command{ode78}. Tests and demos have been included for this type of solvers. Added new functions @command{odeexamples}, @command{odepkg_examples_ode}, @command{odepkg_examples_dae}, @command{odepkg_examples_ide} and @command{odepkg_examples_ide}. Ongoing work with this manual. @item OdePkg Version 0.6.x @tab A lot of compatibility tests, improvements, bugfixes, etc. @item @b{(current)} Version 0.8.x @tab Final releases before version 1.0.0. @item @b{(future)} Version 1.0.0 @tab Completed OdePkg release 1.0.0 with M--solvers and DLD--solvers. @end multitable @node Installation and deinstallation, Reporting Bugs, OdePkg history and roadmap, Beginners Guide @section Installation and deinstallation @cindex installation @cindex deinstallation OdePkg can be installed easily using the @command{pkg} command in Octave. To install OdePkg download the latest release of OdePkg from the Octave--Forge download site, then get into that directory where the downloaded release of OdePkg has been saved, start Octave and type @example pkg install odepkg-x.x.x.tar.gz @end example where @file{x.x.x} in the name of the @file{*.tar.gz} file is the current release number of OdePkg that is available. If you want to deinstall resp. remove OdePkg then simply type @example pkg uninstall odepkg @end example and make sure that OdePkg has been removed completely and does not appear in the list of installed packages anymore with the following command @example pkg list @end example @node Reporting Bugs, The "foo" example, Installation and deinstallation, Beginners Guide @section Reporting Bugs @cindex bugs If you encounter problems during the installation process of OdePkg with the @command{pkg} command or if you have an OdePkg that seems to be broken or if you encounter problems while using OdePkg or if you find bugs in the source codes then please report all of that via email at the Octave--Forge mailing--list using the email address @ifnothtml @email{octave-dev@@lists.sourceforge.net}. @end ifnothtml @ifhtml @email{octave-dev @{at] lists.sourceforge.net} (replace @{at] with @@). @end ifhtml Not only bugs are welcome but also any kind of comments are welcome (eg. if you think that OdePkg is absolutely useful or even unnecessary). @node The "foo" example, , Reporting Bugs, Beginners Guide @section The "foo" example @cindex foo example Have a look at the first ordinary differential equation with the name ``@command{foo}''. The @command{foo} equation of second order may be of the form @ifhtml @example @math{y''(t) + C1 y'(t) + C2 y(t) = C3} @end example @end ifhtml @ifnothtml @math{y''(t) + C_1 y'(t) + C_2 y(t) = C_3}. @end ifnothtml With the substitutions @ifhtml @example @math{y1(t) = y(t)} @math{y2(t) = y'(t)} @end example @end ifhtml @ifnothtml @math{y_1(t) = y(t)} and @math{y_2(t) = y'(t)} @end ifnothtml this differential equation of second order can be split into two differential equations of first order, ie. @ifhtml @example @math{y1'(t) = y2(t)} @math{y2'(t) = - C1 y2(t) - C2 y1(t) + C3} @end example @end ifhtml @ifnothtml @math{y'_1(t) = y_2(t)} and @math{y'_2(t) = - C_1 y_2(t) - C_2 y_1(t) + C_3}. @end ifnothtml Next the numerical values for the constants need to be defined, ie. @ifhtml @example @math{C1 = 2.0} @math{C2 = 5.0} @math{C3 = 10.0} @end example @end ifhtml @ifnothtml @math{C_1 = 2.0}, @math{C_2 = 5.0}, @math{C_3 = 10.0}. @end ifnothtml This set of ordinary differential equations can then be written as an Octave M--file function like @example function vdy = foo (vt, vy, varargin) vdy(1,1) = vy(2); vdy(2,1) = - 2.0 * vy(2) - 5.0 * vy(1) + 10.0; endfunction @end example It can be seen that this ODEs do not depend on time, nevertheless the first input argument of this function needs to be defined as the time argument @var{vt} followed by a solution array argument @command{vy} as the second input argument and a variable size input argument @command{varargin} that can be used to set up user defined constants or control variables. As it is known that @command{foo} is a set of @i{ordinary} differential equations we can choose one of the four Runge--Kutta solvers (cf. @ref{Solver families}). It is also known that the time period of interest may be between @ifhtml @example @math{t0 = 0.0} @math{te = 5.0} @end example @end ifhtml @ifnothtml @math{t_0 = 0.0} and @math{t_e = 5.0} @end ifnothtml as well as that the initial values of the ODEs are @ifhtml @example @math{y1(t=0) = 0.0} @math{y2(t=0) = 0.0} @end example @end ifhtml @ifnothtml @math{y_1(t=0) = 0.0} and @math{y_2(t=0) = 0.0}. @end ifnothtml Solving this set of ODEs can be done by typing the following commands in Octave @example ode45 (@@foo, [0 5], [0 0]); @end example A figure window opens and it can be seen how this ODEs are solved over time. For some of the solvers that come with OdePkg it is possible to define exact time stamps for which an solution is required. Then the example can be called eg.@example ode45 (@@foo, [0:0.1:5], [0 0]); @end example If it is not wanted that a figure window is opened while solving then output arguments have to be used to catch the results of the solving process and to not pass the results to the figure window, eg. @example [t, y] = ode45 (@@foo, [0 5], [0 0]); @end example Results can also be obtained in form of an Octave structure if one output argument is used like in the following example. Then the results are stored in the fields @command{S.x} and @command{S.y}. @example S = ode45 (@@foo, [0 5], [0 0]); @end example As noticed before, a function for the ordinary differential equations must not be rewritten all the time if some of the parameters are going to change. That's what the input argument @command{varargin} can be used for. So rewrite the function @command{foo} into @command{newfoo} the following way @example function vdy = newfoo (vt, vy, varargin) vdy(1,1) = vy(2); vdy(2,1) = -varargin@{1@}*vy(2)-varargin@{2@}*vy(1)+varargin@{3@}; endfunction @end example There is nothing said anymore about the constant values but if using the following caller routine in the Octave window then the same results can be obtained with the new function @command{newfoo} as before with the function @command{foo} (ie. the parameters are directly feed through from the caller routine @command{ode45} to the function @command{newfoo}) @example ode45 (@@newfoo, [0 5], [0 0], 2.0, 5.0, 10.0); @end example OdePkg can do much more while solving differential equations, eg. setting up other output functions instead of the function @command{odeplot} or setting up other tolerances for the solving process etc. As a last example in this beginning chapter it is shown how this can be done, ie. with the command @command{odeset} @example A = odeset ('OutputFcn', @@odeprint); ode45 (@@newfoo, [0 5], [0 0], A, 2.0, 5.0, 10.0); @end example or @example A = odeset ('OutputFcn', @@odeprint, 'AbsTol', 1e-5, \ 'RelTol', 1e-5, 'NormControl', 'on'); ode45 (@@newfoo, [0 5], [0 0], A, 2.0, 5.0, 10.0); @end example The options structure @command{A} that can be set up with with the command @command{odeset} must always be the fourth input argument when using the ODE solvers and the DAE solvers but if you are using an IDE solver then @command{A} must be the fifth input argument (cf. @ref{Solver families}). The various options that can be set with the command @command{odeset} are described in @ref{ODE/DAE/IDE/DDE options}. Further examples have also been implemented. These example files and functions are of the form @command{odepkg_examples_*}. Different testsuite examples have been added that are stored in files with filenames @command{odepkg_testsuite_*}. Before reading the next chapter note that nearly every function that comes with OdePkg has its own help text and its own examples. Look for yourself how the different functions, options and combinations can be used. If you want to have a look at the help description of a special function then type @example help fcnname @end example in the Octave window where @command{fcnname} is the name of the function for the help text to be viewed. Type @example demo fcnname @end example in the Octave window where @command{fcnname} is the name of the function for the demo to run. Finally write @example doc odepkg @end example for opening this manual in the texinfo reader of the Octave window. @c %*** End of first chapter: Beginners Guide @c %*** Start of second chapter: Users Guide @node Users Guide, Programmers Guide, Beginners Guide, Top @chapter Users Guide @cindex Users guide The ``Users Guide'' is intended for trained users who already know in principal how to solve differential equations with the Octave language and OdePkg. In this chapter it will be explained which equations can be solved with OdePkg in @ref{Differential Equations}. It will be explained which solvers can be used for the different kind of equations in @ref{Solver families} and which options can be set for the optimization of the solving process in @ref{ODE/DAE/IDE/DDE options}. The help text of all M--file functions and all Oct--file functions have been extracted and are displayed in the sections @ref{M-File Function Reference} and @ref{Oct-File Function Reference}. @menu * Differential Equations:: The different kind of problems that can be solved with OdePkg * Solver families:: The different kind of solvers within OdePkg * ODE/DAE/IDE/DDE options:: The options that can be set for a solving process * M-File Function Reference:: The description about all @file{*.m}-file functions * Oct-File Function Reference:: The description about all DLD-functions from @file{*.oct}-files @end menu @node Differential Equations, Solver families, Users Guide, Users Guide @section Differential Equations @cindex differential equations In this section the different kind of differential equations that can be solved with OdePkg are explained. The formulation of ordinary differential equations is described in section @ref{ODE equations} followed by the description of explicetly formulated differential algebraic equations in section @ref{DAE equations}, implicetely formulated differential algebraic equations in section @ref{IDE equations} and delay differential algebraic equations in section @ref{DDE equations}. @menu * ODE equations:: Ordinary differential equations * DAE equations:: Differential algebraic equations in explicit form * IDE equations:: Differential algebraic equations in implicit form * DDE equations:: Delay differential equations @end menu @node ODE equations, DAE equations, Differential Equations, Differential Equations @subsection ODE equations @cindex ode equations ODE equations in general are of the form @ifhtml @example @math{y'(t) = f(t,y)} @end example @end ifhtml @ifnothtml @math{y'(t) = f(t,y)} @end ifnothtml where @math{y'(t)} may be a scalar or vector of derivatives. The variable @math{t} always is a scalar describing one point of time and the variable @math{y(t)} is a scalar or vector of solutions from the last time step of the set of ordinary differential equations. If the equation is non--stiff then the @ref{Runge-Kutta solvers} can be used to solve such kind of differential equations but if the equation is stiff then it is recommended to use the @ref{Hairer-Wanner solvers}. An ODE equation definition in Octave must look like @example function [dy] = ODEequation (t, y, varargin) @end example @node DAE equations, IDE equations, ODE equations, Differential Equations @subsection DAE equations @cindex dae equations DAE equations in general are of the form @ifhtml @example @math{M(t,y) y'(t) = f(t,y)} @end example @end ifhtml @ifnothtml @math{M(t,y) \cdot y'(t) = f(t,y)} @end ifnothtml where @math{y'(t)} may be a scalar or vector of derivatives. The variable @math{t} always is a scalar describing one point of time and the variable @math{y(t)} is a scalar or vector of solutions from the set of differential algebraic equations. The variable @math{M(t,y)} is the squared @i{singular} mass matrix that may depend on @math{y} and @math{t}. If @math{M(t,y)} is not @i{singular} then the set of equations from above can normally also be written as an ODE equation. If it does not depend on time then it can be defined as a constant matrix or a function. If it does depend on time then it must be defined as a function. Use the command @command{odeset} to pass the mass matrix information to the solver function (cf. @ref{ODE/DAE/IDE/DDE options}). If the equation is non--stiff then the @ref{Runge-Kutta solvers} can be used to solve such kind of differential equations but if the equation is stiff then it is recommended to use the @ref{Hairer-Wanner solvers}. A DAE equation definition in Octave must look like @example function [dy] = DAEequation (t, y, varargin) @end example and the mass matrix definition can either be a constant mass matrix or a valid function handle to a mass matrix calculation function that can be set with the command @command{odeset} (cf. option @code{Mass} of section @ref{ODE/DAE/IDE/DDE options}). @node IDE equations, DDE equations, DAE equations, Differential Equations @subsection IDE equations @cindex ide equations IDE equations in general are of the form @ifhtml @example @math{y'(t) + f(t,y) = 0} @end example @end ifhtml @ifnothtml @math{y'(t) + f(t,y) = 0} @end ifnothtml where @math{y'(t)} may be a scalar or vector of derivatives. The variable @math{t} always is a scalar describing one point of time and the variable @math{y(t)} is a scalar or vector of solutions from the set of implicit differential equations. Only IDE solvers from section @ref{Cash modified BDF solvers} or section @ref{DDaskr direct method solver} can be used to solve such kind of differential equations. A DAE equation definition in Octave must look like @example function [residual] = IDEequation (t, y, dy, varargin) @end example @node DDE equations, , IDE equations, Differential Equations @subsection DDE equations @cindex dde equations DDE equations in general are of the form @ifhtml @example @math{y'(t) = f(t,y(t),y(t-tau_1),...,y(t-tau_n))} @end example @end ifhtml @ifnothtml @math{y'(t) = f(t,y(t),y(t-\tau_1),...,y(t-\tau_n))} @end ifnothtml where @math{y'(t)} may be a scalar or vector of derivatives. The variable @math{t} always is a scalar describing one point of time and the variables @ifhtml @math{y(t-tau_i)} @end ifhtml @ifnothtml @math{y(t-\tau_i)} @end ifnothtml are scalars or vectors from the past. Only DDE solvers from section @ref{Modified Runge-Kutta solvers} can be used to solve such kind of differential equations. A DDE equation definition in Octave must look like @example function [dy] = DDEequation (t, y, z, varargin) @end example @b{NOTE:} Only DDEs with constant delays @ifhtml @math{y(t-tau_i)} @end ifhtml @ifnothtml @math{y(t-\tau_i)} @end ifnothtml can be solved with OdePkg. @node Solver families, ODE/DAE/IDE/DDE options, Differential Equations, Users Guide @section Solver families @cindex solver families In this section the different kind of solvers are introduced that have been implemented in OdePkg. This section starts with the basic Runge--Kutta solvers in section @ref{Runge-Kutta solvers} and is continued with the Mex--file Hairer--Wanner solvers in section @ref{Hairer-Wanner solvers}. @c Other solvers are described in section @ref{Other solvers}. Performance tests have also been added to the OdePkg. Some of these performance results have been added to section @ref{ODE solver performances}. @menu * Runge-Kutta solvers:: ODE solvers written as @file{*.m} files * Hairer-Wanner solvers:: DAE solvers interfaced by @file{*.cc} files * Cash modified BDF solvers:: A DAE and an IDE solver interfaced by @file{*.cc} files * DDaskr direct method solver:: An IDE solver interfaced by a @file{*.cc} file * Modified Runge-Kutta solvers:: DDE solvers written as @file{*.m} files * ODE solver performances:: Cross math-engine performance tests @end menu @node Runge-Kutta solvers, Hairer-Wanner solvers, Solver families, Solver families @subsection Runge--Kutta solvers @cindex Runge--Kutta The Runge--Kutta solvers are written in the Octave language and that are saved as @file{m}--files. There have been implemented four different solvers with a very similiar structure, ie. @command{ode23}, @command{ode45}, @command{ode54} and @command{ode78}@footnote{The descriptions for these Runge--Kutta solvers have been taken from the help texts of the initial Runge--Kutta solvers that were written by Marc Compere, he also pointed out that ''a relevant discussion on step size choice can be found on page 90ff in U.M. Ascher, L.R. Petzold, Computer Methods for Ordinary Differential Equations and Differential--Agebraic Equations, Society for Industrial and Applied Mathematics (SIAM), Philadelphia, 1998''.}. The Runge--Kutta solvers have been added to the OdePkg to solve non--stiff ODEs and DAEs, stiff equations of that form cannot be solved with these solvers. The order of all of the following Runge--Kutta methods is the order of the local truncation error, which is the principle error term in the portion of the Taylor series expansion that gets dropped, or intentionally truncated. This is different from the local error which is the difference between the estimated solution and the actual, or true solution. The local error is used in stepsize selection and may be approximated by the difference between two estimates of different order, @ifhtml @example @math{l(h) = x(O(h+1)) - x(O(h))} @end example @end ifhtml @ifnothtml @math{l(h) = x(O(h+1)) - x(O(h))}. @end ifnothtml With this definition, the local error will be as large as the error in the lower order method. The local truncation error is within the group of terms that gets multipled by @math{h} when solving for a solution from the general Runge--Kutta method. Therefore, the order--p solution created by the Runge--Kunge method will be roughly accurate to @ifhtml @example @math{O(h^{(p+1)})} @end example @end ifhtml @ifnothtml @math{O(h^{(p+1)})} @end ifnothtml since the local truncation error shows up in the solution as @ifhtml @example @math{e = h d} @end example @end ifhtml @ifnothtml @math{e = h\cdot d} @end ifnothtml which is @math{h}--times an @math{O(h^p)}--term, or rather @math{O(h^{(p+1)})}. @multitable @columnfractions .075 .925 @item @command{ode23} @tab Integrates a system of non--stiff ordinary differential equations (non--stiff ODEs and DAEs) using second and third order Runge--Kutta formulas. This particular third order method reduces to Simpson's @math{1/3} rule and uses the third order estimation for the output solutions. Third order accurate Runge--Kutta methods have local and global errors of @math{O(h^4)} and @math{O(h^3)} respectively and yield exact results when the solution is a cubic (the variable @math{h} is the step size from one integration step to another integration step). This solver requires three function evaluations per integration step.@* @item @command{ode45} @tab Integrates a system of non--stiff ordinary differential equations (non--stiff ODEs and DAEs) using fourth and fifth order embedded formulas from Fehlberg. This is a fourth--order accurate integrator therefore the local error normally expected is @math{O(h^5)}. However, because this particular implementation uses the fifth--order estimate for @math{x_{out}} (ie. local extrapolation) moving forward with the fifth--order estimate should yield local error of @math{O(h^6)}. This solver requires six function evaluations per integration step.@* @item @command{ode54} @tab Integrates a system of non--stiff ordinary differential equations (non--stiff ODEs and DAEs) using fifth and fourth order Runge--Kutta formulas. The Fehlberg @math{4(5)} of the @command{ode45} pair is established and works well, however, the Dormand--Prince @math{5(4)} pair minimizes the local truncation error in the fifth--order estimate which is what is used to step forward (local extrapolation). Generally it produces more accurate results and costs roughly the same computationally. This solver requires seven function evaluations per integration step.@* @item @command{ode78} @tab Integrates a system of non--stiff ordinary differential equations (non--stiff ODEs and DAEs) using seventh and eighth order Runge--Kutta formulas. This is a seventh--order accurate integrator therefore the local error normally expected is @math{O(h^8)}. However, because this particular implementation uses the eighth--order estimate for @math{x_{out}} moving forward with the eighth--order estimate will yield errors on the order of @math{O(h^9)}. This solver requires thirteen function evaluations per integration step. @end multitable @node Hairer-Wanner solvers, Cash modified BDF solvers, Runge-Kutta solvers, Solver families @subsection Hairer--Wanner solvers @cindex Hairer--Wanner The Hairer--Wanner solvers have been written by Ernst Hairer and Gerhard Wanner. They are written in the Fortran language (hosted at @url{http://www.unige.ch/~hairer}) and that have been added to the OdePkg as a compressed file with the name @file{hairer.tgz}. Papers and other details about these solvers can be found at the adress given before. The licence of these solvers is a modified BSD license (without advertising clause and therefore are GPL compatible) and can be found as @file{licence.txt} file in the @file{hairer.tgz} package. The Hairer--Wanner solvers have been added to the OdePkg to solve non--stiff and stiff ODEs and DAEs that cannot be solved with any of the Runge--Kutta solvers. Interface functions for these solvers have been created and have been added to the OdePkg. Their names are @file{odepkg_octsolver_XXX.cc} where @file{XXX} is the name of the Fortran file that is interfaced. The file @file{dldsolver.oct} is created automatically when installing OdePkg with the command @command{pkg}, but developers can also build each solver manually with the instructions given as a preamble of every @file{odepkg_octsolver_XXX.cc} file. To provide a short name and to circumvent from the syntax of the original solver function wrapper functions have been added, eg. the command @command{ode2r} calls the solver @command{radau} from the Fortran file @file{radau.f}. The other wrapper functions for the Hairer--Wanner solvers are @command{ode5r} for the @command{radau5} solver, @command{oders} for the @command{rodas} solver and @command{odesx} for the @command{seulex} solver. The help text of all these solver functions can be diaplyed by calling @command{help wrapper} where wrapper is one of @command{ode2r}, @command{ode5r}, @command{oders} or @command{odesx}. @node Cash modified BDF solvers, DDaskr direct method solver, Hairer-Wanner solvers, Solver families @subsection Cash modified BDF solvers @cindex BDF solver @cindex Cash modified BDF The backward differentiation algorithm solvers have been written by Jeff Cash in the Fortran language and that are hosted at @url{http://pitagora.dm.uniba.it/~testset}. They have been added to the OdePkg as a compressed file with the name @file{cash.tgz}. The license of these solvers is a General Public License V2 that can be found as a preamble of each Fortran solver source file. Papers and other details about these solvers can be found at the host adress given before and also at Jeff Cash's homepage at @url{http://www.ma.ic.ac.uk/~jcash}. Jeff Cash's modified BDF solvers have been added to the OdePkg to solve non--stiff and stiff ODEs and DAEs and also IDEs that cannot be solved with any of the Runge--Kutta solvers. Interface functions for these solvers have been created and have been added to the OdePkg. Their names are @file{odepkg_octsolver_XXX.cc} where @file{XXX} is the name of the Fortran file that is interfaced. The file @file{dldsolver.oct} is created automatically when installing OdePkg with the command @command{pkg}, but developers can also build each solver manually with the instructions given as a preamble of every @file{odepkg_octsolver_XXX.cc} file. To provide a short name and to circumvent from the syntax of the original solver function wrapper functions have been added. The command @command{odebda} calls the solver @command{mebdfdae} from the Fortran file @file{mebdf.f} and the @command{odebdi} calls the solver @command{mebdfi} from the Fortran file @file{mebdfi.f}. @node DDaskr direct method solver, Modified Runge-Kutta solvers, Cash modified BDF solvers, Solver families @subsection DDaskr direct method solver @cindex ddaskr solver The direct method from the Krylov solver file @file{ddaskr.f} has been written by Peter N. Brown, Alan C. Hindmarsh, Linda R. Petzold and Clement W. Ulrich in the Fortran language and that is hosted at @url{http://www.netlib.org}. @b{The Krylov method has not been implemented within OdePkg, only the direct method has been implemented.} The solver and further files for the interface have been added to the OdePkg as a compressed package with the name @file{ddaskr.tgz}. The license of these solvers is a modfied BSD license (without advertising clause) that can be found inside of the compressed package. Other details about this solver can be found as a preamble in the source file @file{ddaskr.f}. The direct method solver of the file @file{ddaskr.f} has been added to the OdePkg to solve non--stiff and stiff IDEs. An interface function for this solver has been created and has been added to the OdePkg. The source file name is @file{odepkg_octsolver_ddaskr.cc}. The binary function can be found in the file @file{dldsolver.oct} that is created automatically when installing OdePkg with the command @command{pkg}, but developers can also build the solver wrapper manually with the instructions given as a preamble of the @file{odepkg_octsolver_ddaskr.cc} file. To provide a short name and to circumvent from the syntax of the original solver function a wrapper function has been added. The command @command{odekdi} calls the direct method of the solver @command{ddaskr} from the Fortran file @file{ddaskr.f}. @node Modified Runge-Kutta solvers, ODE solver performances, DDaskr direct method solver, Solver families @subsection Modified Runge--Kutta solvers @cindex Runge--Kutta modified The modified Runge--Kutta solvers are written in the Octave language and that are saved as m--files. There have been implemented four different solvers that do have a very similiar structure to that solvers found in section @ref{Runge-Kutta solvers}. Their names are @command{ode23d}, @command{ode45d}, @command{ode54d} and @command{ode78d}. The modified Runge--Kutta solvers have been added to the OdePkg to solve non--stiff DDEs with constant delays only, stiff equations of that form cannot be solved with these solvers. For further information about the error estimation of these solvers cf. section @ref{Runge-Kutta solvers}. @b{Note:} The four DDE solvers of OdePkg are not syntax compatible to propietary solvers. The reason is that the input arguments of the propietary DDE--solvers are completely mixed up in comparison to ODE, DAE and IDE propietary solvers. The DDE solvers of OdePkg have been implemented in form of a syntax compatible way to the other family solvers, eg. propietary solver calls look like @example ode23 (@@fode, vt, vy) %# for solving an ODE ode15i (@@fide, vt, vy, vdy) %# for solving an IDE dde23 (@@fdde, vlag, vhist, vt) %# for solving a DDE @end example whereas in OdePkg the same examples would look like @example ode23 (@@fode, vt, vy) %# for solving an ODE odebdi (@@fide, vt, vy, vdy) %# for solving an IDE ode23d (@@fdde, vt, vy, vlag, vhist) %# for solving a DDE @end example Further, the commands @command{ddeset} and @command{ddeget} have not been implemented in OdePkg. Use the functions @command{odeset} and @command{odeget} for setting and returning DDE options instead. @node ODE solver performances, , Modified Runge-Kutta solvers, Solver families @subsection ODE solver performances @cindex performance The following tables give an overview about the performance of the OdePkg ODE/DAE solvers in comparison to propietary solvers when running the HIRES function from the OdePkg testsuite (non--stiff ODE test). @smallexample >> odepkg ('odepkg_performance_mathires'); ----------------------------------------------------------------------------------------- Solver RelTol AbsTol Init Mescd Scd Steps Accept FEval JEval LUdec Time ----------------------------------------------------------------------------------------- ode113 1e-007 1e-007 1e-009 7.57 5.37 24317 21442 45760 11.697 ode23 1e-007 1e-007 1e-009 7.23 5.03 13876 13862 41629 2.634 ode45 1e-007 1e-007 1e-009 7.91 5.70 11017 10412 66103 2.994 ode15s 1e-007 1e-007 1e-009 7.15 4.95 290 273 534 8 59 0.070 ode23s 1e-007 1e-007 1e-009 6.24 4.03 702 702 2107 702 702 0.161 ode23t 1e-007 1e-007 1e-009 6.00 3.79 892 886 1103 5 72 0.180 ode23tb 1e-007 1e-007 1e-009 5.85 3.65 735 731 2011 5 66 0.230 ----------------------------------------------------------------------------------------- @end smallexample @smallexample octave:1> odepkg ('odepkg_performance_octavehires'); ----------------------------------------------------------------------------------------- Solver RelTol AbsTol Init Mescd Scd Steps Accept FEval JEval LUdec Time ----------------------------------------------------------------------------------------- ode23 1e-07 1e-07 1e-09 7.86 5.44 17112 13369 51333 138.071 ode45 1e-07 1e-07 1e-09 8.05 5.63 9397 9393 56376 92.065 ode54 1e-07 1e-07 1e-09 8.25 5.83 9300 7758 65093 84.319 ode78 1e-07 1e-07 1e-09 8.54 6.12 7290 6615 94757 97.746 ode2r 1e-07 1e-07 1e-09 7.69 5.27 50 50 849 50 59 0.624 ode5r 1e-07 1e-07 1e-09 7.55 5.13 71 71 671 71 81 0.447 oders 1e-07 1e-07 1e-09 7.08 4.66 138 138 828 138 138 0.661 odesx 1e-07 1e-07 1e-09 6.56 4.13 30 26 1808 26 205 1.057 odebda 1e-07 1e-07 1e-09 6.53 4.11 401 400 582 42 42 0.378 ----------------------------------------------------------------------------------------- @end smallexample The following tables give an overview about the performance of the OdePkg ODE/DAE solvers in comparison to propietary solvers when running the chemical AKZO--NOBEL function from the OdePkg testsuite (non--stiff ODE test). @smallexample >> odepkg ('odepkg_performance_matchemakzo'); ----------------------------------------------------------------------------------------- Solver RelTol AbsTol Init Mescd Scd Steps Accept FEval JEval LUdec Time ----------------------------------------------------------------------------------------- ode113 1e-007 1e-007 1e-007 NaN Inf - - - - - - ode23 1e-007 1e-007 1e-007 NaN Inf 15 15 47 0.431 ode45 1e-007 1e-007 1e-007 NaN Inf 15 15 92 0.170 ode15s 1e-007 1e-007 1e-007 7.04 6.20 161 154 4 35 0.521 ode23s 1e-007 1e-007 1e-007 7.61 6.77 1676 1676 5029 1676 1677 2.704 ode23t 1e-007 1e-007 1e-007 5.95 5.11 406 404 3 39 0.611 ode23tb 1e-007 1e-007 1e-007 NaN Inf 607 3036 1 608 6.730 ----------------------------------------------------------------------------------------- @end smallexample @smallexample octave:1> odepkg ('odepkg_performance_octavechemakzo'); ----------------------------------------------------------------------------------------- Solver RelTol AbsTol Init Mescd Scd Steps Accept FEval JEval LUdec Time ----------------------------------------------------------------------------------------- ode23 1e-07 1e-07 1e-07 2.95 2.06 424 385 1269 1.270 ode45 1e-07 1e-07 1e-07 2.95 2.06 256 218 1530 1.281 ode54 1e-07 1e-07 1e-07 2.95 2.06 197 195 1372 1.094 ode78 1e-07 1e-07 1e-07 2.95 2.06 184 156 2379 1.933 ode2r 1e-07 1e-07 1e-07 8.50 7.57 39 39 372 39 43 0.280 ode5r 1e-07 1e-07 1e-07 8.50 7.57 39 39 372 39 43 0.238 oders 1e-07 1e-07 1e-07 7.92 7.04 67 66 401 66 67 0.336 odesx 1e-07 1e-07 1e-07 7.19 6.26 19 19 457 19 82 0.248 odebda 1e-07 1e-07 1e-07 7.47 6.54 203 203 307 25 25 0.182 ----------------------------------------------------------------------------------------- @end smallexample Other testsuite functions have been added to the OdePkg that can be taken for further performance tests and syntax checks on your own hardware. These functions all have a name @file{odepkg_testsuite_XXX.m} with @file{XXX} being the name of the testsuite equation that has been implemented. @node ODE/DAE/IDE/DDE options, M-File Function Reference, Solver families, Users Guide @section ODE/DAE/IDE/DDE options @cindex ode options @cindex dae options @cindex ide options @cindex dde options The default values of an OdePkg options structure can be displayed with the command @command{odeset}. If @command{odeset} is called without any input argument and one output argument then a OdePkg options structure with default values is created, eg. @example A = odeset (); disp (A); @end example There also is an command @command{odeget} which extracts one or more options from an OdePkg options structure. Other values than default values can also be set with the command @command{odeset}. The function description of the commands @command{odeset} and @command{odeget} can be found in the @ref{M-File Function Reference}. The values that can be set with the @command{odeset} command are @table @samp @item RelTol @cindex RelTol option The option @option{RelTol} is used to set the relative error tolerance for the error estimation of the solver that is used while solving. It can either be a positive scalar or a vector with every element of the vector being a positive scalar (this depends on the solver that is used if both variants are supported). The definite error estimation equation also depends on the solver that is used but generalized (eg. for the solvers @command{ode23}, @command{ode45}, @command{ode54} and @command{ode78}) it may be a form like @ifhtml @example @math{e(t) = max (RelTol^T y(t), AbsTol)} @end example @end ifhtml @ifnothtml @math{e(t) = max (r_{tol}^T y(t), a_{tol})}. @end ifnothtml Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("RelTol", 1, "OutputFcn", @@odeplot); ode78 (@@fvanderpol, [0 20], [2 0], A); B = odeset (A, "RelTol", 1e-10); ode78 (@@fvanderpol, [0 20], [2 0], B); @end example @item AbsTol @cindex AbsTol option The option @option{AbsTol} is used to set the absolute error tolerance for the error estimation of the solver that is used while solving. It can either be a positive scalar or a vector with every element of the vector being a positive scalar (it depends on the solver that is used if both variants are supported). The definite error estimation equation also depends on the solver that is used but generalized (eg. for the solvers @command{ode23}, @command{ode45}, @command{ode54} and @command{ode78}) it may be a form like @ifhtml @example @math{e(t) = max (RelTol^T y(t), AbsTol)} @end example @end ifhtml @ifnothtml @math{e(t) = max (r_{tol}^T y(t), a_{tol})}. @end ifnothtml Run the following example to illustrate the effect if this option is used @example ## An anonymous implementation of the Van der Pol equation fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; A = odeset ("AbsTol", 1e-3, "OutputFcn", @@odeplot); ode54 (fvdb, [0 10], [2 0], A); B = odeset (A, "AbsTol", 1e-10); ode54 (fvdb, [0 10], [2 0], B); @end example @item NormControl @cindex NormControl option The option @option{NormControl} is used to set the type of error tolerance calculation of the solver that is used while solving. It can either be the string @command{"on"} or @command{"off"}. At the time the solver starts solving a warning message may be displayed if the solver will ignore the @command{"on"} setting of this option because of an unhandled resp. missing implementation. If set @command{"on"} then the definite error estimation equation also depends on the solver that is used but generalized (eg. for the solvers @command{ode23}, @command{ode45}, @command{ode54} and @command{ode78}) it may be a form like @ifhtml @example @math{e(t) = max (RelTol^T max (norm (y(t), Inf)), AbsTol)} @end example @end ifhtml @ifnothtml @math{e(t) = max (r_{tol}^T max (norm (y(t), \infty{})), a_{tol})}. @end ifnothtml Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("NormControl", "on", "OutputFcn", @@odeplot); ode78 (@@fvanderpol, [0 20], [2 0], A); B = odeset (A, "NormControl", "off"); ode78 (@@fvanderpol, [0 20], [2 0], B); @end example @item MaxStep @cindex MaxStep option The option @option{MaxStep} is used to set the maximum step size for the solver that is used while solving. It can only be a positive scalar. By default this value is set internally by every solver and also may differ when using different solvers. Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("MaxStep", 10, "OutputFcn", @@odeprint); ode78 (@@fvanderpol, [0 20], [2 0], A); B = odeset (A, "MaxStep", 1e-1); ode78 (@@fvanderpol, [0 20], [2 0], B); @end example @item InitialStep @cindex InitialStep option The option @option{InitialStep} is used to set the initial first step size for the solver. It can only be a positive scalar. By default this value is set internally by every solver and also may be different when using different solvers. Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("InitialStep", 1, "OutputFcn", @@odeprint); ode78 (@@fvanderpol, [0 1], [2 0], A); B = odeset (A, "InitialStep", 1e-5); ode78 (@@fvanderpol, [0 1], [2 0], B); @end example @item InitialSlope @cindex InitialSlope option The option @option{InitialSlope} is not handled by any of the solvers by now.@* @item OutputFcn @cindex OutputFcn option The option @option{OutputFcn} can be used to set up an output function for displaying the results of the solver while solving. It must be a function handle to a valid function. There are four predefined output functions available with OdePkg. @command{odeprint} prints the actual time values and results in the Octave window while solving, @command{odeplot} plots the results over time in a new figure window while solving, @command{odephas2} plots the first result over the second result as a two--dimensional plot while solving and @command{odephas3} plots the first result over the second result over the third result as a three--dimensional plot while solving. Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("OutputFcn", @@odeprint); ode78 (@@fvanderpol, [0 2], [2 0], A); @end example User defined output functions can also be used. A typical framework for a self--made output function may then be of the form @example function [vret] = odeoutput (vt, vy, vdeci, varargin) switch vdeci case "init" ## Do everything needed to intialize output function case "calc" ## Do everything needed to create output case "done" ## Do everything needed to clean up output function endswitch endfunction @end example The output function @command{odeplot} is also set automatically if the solver calculation routine is called without any output argument. Run the following example to illustrate the effect if this option is not used and no output argument is given @example ## An anonymous implementation of the Van der Pol equation fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; ode78 (fvdb, [0 20], [2 0]); @end example @item Refine @cindex Refine option The option @option{Refine} is used to set the interpolation factor that is used to increase the quality for the output values if an output function is also set with the option @option{OutputFcn}. It can only be a integer value @math{0<=}@option{Refine}@math{<=5}. Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("Refine", 0, "OutputFcn", @@odeplot); ode78 (@@fvanderpol, [0 20], [2 0], A); B = odeset (A, "Refine", 3); ode78 (@@fvanderpol, [0 20], [2 0], B); @end example @item OutputSel @cindex OutputSel option The option @option{OutputSel} is used to set the components for which output has to be performed if an output function is also set with the option @option{OutputFcn}. It can only be a vector of integer values. Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("OutputSel", [1, 2], "OutputFcn", @@odeplot); ode78 (@@fvanderpol, [0 20], [2 0], A); B = odeset (A, "OutputSel", [2]); ode78 (@@fvanderpol, [0 20], [2 0], B); @end example @item Stats @cindex Stats option The option @option{Stats} is used to print cost statistics about the solving process after solving has been finished. It can either be the string @command{"on"} or @command{"off"}. Run the following example to illustrate the effect if this option is used @example function yd = fvanderpol (vt, vy, varargin) mu = 1; ## Set mu > 10 for higher stiffness yd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction A = odeset ("Stats", "off"); [a, b] = ode78 (@@fvanderpol, [0 2], [2 0], A); B = odeset ("Stats", "on"); [c, d] = ode78 (@@fvanderpol, [0 2], [2 0], B); @end example The cost statistics can also be obtained if the solver calculation routine is called with one output argument. The cost statistics then are in the field @option{stats} of the output arguemnt structure. Run the following example to illustrate the effect if this option is used @example S = ode78 (@@fvanderpol, [0 2], [2 0], B); disp (S); @end example @item Jacobian @cindex Jacobian option The option @option{Jacobian} can be used to set up an external Jacobian function or Jacobian matrix for DAE solvers to achieve faster and better results (ODE Runge--Kutta solvers do not need to handle a Jacobian function handle or Jacobian matrix). It must either be a function handle to a valid function or a full constant matrix of size squared the dimension of the set of differential equations. User defined Jacobian functions must have the form @samp{function [vjac] = fjac (vt, vy, varargin)}. Run the following example to illustrate the effect if this option is used @example function vdy = fpol (vt, vy, varargin) vdy = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; endfunction function vr = fjac (vt, vy, varargin) vr = [0, 1; ... -1-2*vy(1)*vy(2), 1-vy(1)^2]; endfunction A = odeset ("Stats", "on"); B = ode5r (@@fpol, [0 20], [2 0], A); C = odeset (A, "Jacobian", @@fjac); D = ode5r (@@fpol, [0 20], [2 0], C); @end example @b{Note:} The function definition for Jacobian calculations of IDE equations must have the form @samp{function [vjac, vdjc] = fjac (vt, vy, vyd, varargin)}. Run the following example to illustrate the effect if this option is used @example function [vres] = fvanderpol (vt, vy, vyd, varargin) vres = [vy(2) - vyd(1); (1 - vy(1)^2) * vy(2) - vy(1) - vyd(2)]; endfunction function [vjac, vdjc] = fjacobian (vt, vy, vyd, varargin) vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; vdjc = [-1, 0; 0, -1]; endfunction vopt = odeset ("Jacobian", @@fjacobian, "Stats", "on"); vsol = odebdi (@@fvanderpol, [0, 20], [2; 0], [0; -2], vopt, 10); @end example @item JPattern @cindex JPattern option The option @option{JPattern} is not handled by any of the solvers by now.@* @item Vectorized @cindex Vectorized option The option @option{Vectorized} is not handled by any of the solvers by now.@* @item Mass @cindex Mass option The option @option{Mass} can be used to set up an external Mass function or Mass matrix for solving DAE equations. It depends on the solver that is used if @option{Mass} is supported or not. It must either be a function handle to a valid function or a full constant matrix of size squared the dimension of the set of differential equations. User defined Jacobian functions must have the form @samp{function vmas = fmas (vt, vy, varargin)}. Run the following example to illustrate the effect if this option is used @example function vdy = frob (t, y, varargin) vdy(1,1) = -0.04*y(1)+1e4*y(2)*y(3); vdy(2,1) = 0.04*y(1)-1e4*y(2)*y(3)-3e7*y(2)^2; vdy(3,1) = y(1)+y(2)+y(3)-1; endfunction function vmas = fmas (vt, vy, varargin) vmas = [1, 0, 0; 0, 1, 0; 0, 0, 0]; endfunction A = odeset ("Mass", @@fmas); B = ode5r (@@frob, [0 1e8], [1 0 0], A); @end example @b{Note:} The function definition for Mass calculations of DDE equations must have the form @samp{function vmas = fmas (vt, vy, vz, varargin)}.@* @item MStateDependence @cindex MStateDependence option The option @option{MStateDependence} can be used to set up the type of the external Mass function for solving DAE equations if a Mass function handle is set with the option @option{Mass}. It depends on the solver that is used if @option{MStateDependence} is supported or not. It must be a string of the form @command{"none"}, @command{"weak"} or @command{"strong"}. Run the following example to illustrate the effect if this option is used @example function vdy = frob (vt, vy, varargin) vdy(1,1) = -0.04*vy(1)+1e4*vy(2)*vy(3); vdy(2,1) = 0.04*vy(1)-1e4*vy(2)*vy(3)-3e7*vy(2)^2; vdy(3,1) = vy(1)+vy(2)+vy(3)-1; endfunction function vmas = fmas (vt, varargin) vmas = [1, 0, 0; 0, 1, 0; 0, 0, 0]; endfunction A = odeset ("Mass", @@fmas, "MStateDependence", "none"); B = ode5r (@@frob, [0 1e8], [1 0 0], A); @end example User defined Mass functions must have the form as described before (ie. @samp{function vmas = fmas (vt, varargin)} if the option @option{MStateDependence} was set to @command{"none"}, otherwise the user defined Mass function must have the form @samp{function vmas = fmas (vt, vy, varargin)} if the option @option{MStateDependence} was set to either @command{"weak"} or @command{"strong"}.@* @item MvPattern @cindex MvPattern option The option @option{MvPattern} is not handled by any of the solvers by now.@* @item MassSingular @cindex MassSingular option The option @option{MassSingular} is not handled by any of the solvers by now.@* @item NonNegative @cindex NonNegative option The option @option{NonNegative} can be used to set solution variables to zero even if their real solution would be a negative value. It must be a vector describing the positions in the solution vector for which the option @option{NonNegative} should be used. Run the following example to illustrate the effect if this option is used @example vfun = @@(vt,vy) -abs(vy); vopt = odeset ("NonNegative", [1]); [vt1, vy1] = ode78 (vfun, [0 100], [1]); [vt2, vy2] = ode78 (vfun, [0 100], [1], vopt); subplot (2,1,1); plot (vt1, vy1); subplot (2,1,2); plot (vt2, vy2); @end example @item Events @cindex Events option The option @option{Events} can be used to set up an Event function, ie. the Event function can be used to find zero crossings in one of the results. It must either be a function handle to a valid function. Run the following example to illustrate the effect if this option is used @example function vdy = fbal (vt, vy, varargin) vdy(1,1) = vy(2); vdy(2,1) = -9.81; ## m/s² endfunction function [veve, vterm, vdir] = feve (vt, vy, varargin) veve = vy(1); ## Which event component should be tread vterm = 1; ## Terminate if an event is found vdir = -1; ## In which direction, -1 for falling endfunction A = odeset ("Events", @@feve); B = ode78 (@@fbal, [0 1.5], [1 3], A); plot (B.x, B.y(:,1)); @end example @b{Note:} The function definition for Events calculations of DDE equations must have the form @samp{function [veve, vterm, vdir] = feve (vt, vy, vz, varargin)} and the function definition for Events calculations of IDE equations must have the form @samp{function [veve, vterm, vdir] = feve (vt, vy, vyd, varargin)}.@* @item MaxOrder @cindex MaxOrder option The option @option{MaxOrder} can be used to set the maximum order of the backward differentiation algorithm of the @command{odebdi} and @command{odebda} solvers. It must be a scalar integer value between @math{1} and @math{7}. Run the following example to illustrate the effect if this option is used @example function res = fwei (t, y, yp, varargin) res = t*y^2*yp^3 - y^3*yp^2 + t*yp*(t^2 + 1) - t^2*y; endfunction function [dy, dyp] = fjac (t, y, yp, varargin) dy = 2*t*y*yp^3 - 3*y^2*yp^2 - t^2; dyp = 3*t*y^2*yp^2 - 2*y^3*yp + t*(t^2 + 1); endfunction A = odeset ("AbsTol", 1e-6, "RelTol", 1e-6, "Jacobian", @@fjac, ... "Stats", "on", "MaxOrder", 1, "BDF", "on") B = odeset (A, "MaxOrder", 5) C = odebdi (@@fwei, [1 10], 1.2257, 0.8165, A); D = odebdi (@@fwei, [1 10], 1.2257, 0.8165, B); plot (C.x, C.y, "bo-", D.x, D.y, "rx:"); @end example @item BDF @cindex BDF option The option @option{BDF} is only supported by the @command{odebdi} and @command{odebda} solvers. Using these solvers the option @option{BDF} will automatically be set @command{"on"} (even if it was set @command{"off"} before) because the @command{odebdi} and @command{odebda} solvers all use the backward differentiation algorithm to solve the different kind of equations. @item NewtonTol @cindex NewtonTol option TODO @item MaxNewtonIterations @cindex MaxNewtonIterations option TODO @end table @node M-File Function Reference, Oct-File Function Reference, ODE/DAE/IDE/DDE options, Users Guide @section M--File Function Reference @cindex m--file reference The help texts of this section are autogenerated and refer to commands that all can be found in the files @file{*.m}. All commands that are listed below are loaded automatically everytime you launch Octave.@*@* @include mfunref.texi @node Oct-File Function Reference, , M-File Function Reference, Users Guide @section Oct--File Function Reference @cindex oct--file reference The help texts of this section are autogenerated and refer to commands that all can be found in the file @file{dldsolver.oct}. The file @file{dldsolver.oct} is generated automatically if you install OdePkg with the command @command{pkg}. All commands that are listed below are loaded automatically everytime you launch Octave.@*@* @include dldfunref.texi @c %*** End of second chapter: Users Guide @c %*** Start of third chapter: Programmers Guide @node Programmers Guide, Function Index, Users Guide, Top @chapter Programmers Guide @cindex Programmers guide @menu * Missing features:: The TODO-list for missing features @end menu @node Missing features, , Programmers Guide, Programmers Guide @section Missing features @cindex missing features If somebody want to help improving OdePkg then please contact the Octave--Forge developer team sending your modifications via the mailing--list @ifnothtml @email{octave-dev@@lists.sourceforge.net}. @end ifnothtml @ifhtml @email{octave-dev . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{A} =} bvp4c (@var{odefun}, @var{bcfun}, @var{solinit}) ## ## Solves the first order system of non-linear differential equations defined by ## @var{odefun} with the boundary conditions defined in @var{bcfun}. ## ## The structure @var{solinit} defines the grid on which to compute the ## solution (@var{solinit.x}), and an initial guess for the solution (@var{solinit.y}). ## The output @var{sol} is also a structure with the following fields: ## @itemize ## @item @var{sol.x} list of points where the solution is evaluated ## @item @var{sol.y} solution evaluated at the points @var{sol.x} ## @item @var{sol.yp} derivative of the solution evaluated at the ## points @var{sol.x} ## @item @var{sol.solver} = "bvp4c" for compatibility ## @end itemize ## @seealso{odpkg} ## @end deftypefn ## Author: Carlo de Falco ## Created: 2008-09-05 function sol = bvp4c(odefun,bcfun,solinit,options) if (isfield(solinit,"x")) t = solinit.x; else error("bvp4c: missing initial mesh solinit.x"); end if (isfield(solinit,"y")) u_0 = solinit.y; else error("bvp4c: missing initial guess"); end if (isfield(solinit,"parameters")) error("bvp4c: solving for unknown parameters is not yet supported"); end RelTol = 1e-3; AbsTol = 1e-6; if ( nargin > 3 ) if (isfield(options,"RelTol")) RelTol = options.RelTol; endif if (isfield(options,"AbsTol")) AbsTol = options.AbsTol; endif endif Nvar = rows(u_0); Nint = length(t)-1; s = 3; h = diff(t); AbsErr = inf; RelErr = inf; MaxIt = 10; for iter = 1:MaxIt x = [ u_0(:); zeros(Nvar*Nint*s,1) ]; x = __bvp4c_solve__ (t, x, h, odefun, bcfun, Nvar, Nint, s); u = reshape(x(1:Nvar*(Nint+1)),Nvar,Nint+1); for kk=1:Nint+1 du(:,kk) = odefun(t(kk), u(:,kk)); end tm = (t(1:end-1)+t(2:end))/2; um = []; for nn=1:Nvar um(nn,:) = interp1(t,u(nn,:),tm); endfor f_est = []; for kk=1:Nint f_est(:,kk) = odefun(tm(kk), um(:,kk)); end du_est = []; for nn=1:Nvar du_est(nn,:) = diff(u(nn,:))./h; end err = max(abs(f_est-du_est)); AbsErr = max(err) RelErr = AbsErr/norm(du,inf) if ( (AbsErr >= AbsTol) && (RelErr >= RelTol) ) ref_int = find( (err >= AbsTol) & (err./max(max(abs(du))) >= RelTol) ); t_add = tm(ref_int); t_old = t; t = sort([t, t_add]); h = diff(t); u_0 = []; for nn=1:Nvar u_0(nn,:) = interp1(t_old, u(nn,:), t); end Nvar = rows(u_0); Nint = length(t)-1 else break end endfor ## K = reshape(x([1:Nvar*Nint*s]+Nvar*(Nint+1)),Nvar,Nint,s); ## K1 = reshape(K(:,:,1), Nvar, Nint); ## K2 = reshape(K(:,:,2), Nvar, Nint); ## K3 = reshape(K(:,:,3), Nvar, Nint); sol.x = t; sol.y = u; sol.yp= du; sol.parameters = []; sol.solver = 'bvp4c'; endfunction function diff_K = __bvp4c_fun_K__ (t, u, Kin, f, h, s, Nint, Nvar) %% coefficients persistent C = [0 1/2 1 ]; persistent A = [0 0 0; 5/24 1/3 -1/24; 1/6 2/3 1/6]; for jj = 1:s for kk = 1:Nint Y = repmat(u(:,kk),1,s) + ... (reshape(Kin(:,kk,:),Nvar,s) * A.') * h(kk); diff_K(:,kk,jj) = Kin(:,kk,jj) - f (t(kk)+C(jj)*h(kk), Y); endfor endfor endfunction function diff_u = __bvp4c_fun_u__ (t, u, K, h, s, Nint, Nvar) %% coefficients persistent B= [1/6 2/3 1/6 ]; Y = zeros(Nvar, Nint); for jj = 1:s Y += B(jj) * K(:,:,jj); endfor diff_u = u(:,2:end) - u(:,1:end-1) - repmat(h,Nvar,1) .* Y; endfunction function x = __bvp4c_solve__ (t, x, h, odefun, bcfun, Nvar, Nint, s) fun = @( x ) ( [__bvp4c_fun_u__(t, reshape(x(1:Nvar*(Nint+1)),Nvar,(Nint+1)), reshape(x([1:Nvar*Nint*s]+Nvar*(Nint+1)),Nvar,Nint,s), h, s, Nint, Nvar)(:) ; __bvp4c_fun_K__(t, reshape(x(1:Nvar*(Nint+1)),Nvar,(Nint+1)), reshape(x([1:Nvar*Nint*s]+Nvar*(Nint+1)),Nvar,Nint,s), odefun, h, s, Nint, Nvar)(:); bcfun(reshape(x(1:Nvar*(Nint+1)),Nvar,Nint+1)(:,1), reshape(x(1:Nvar*(Nint+1)),Nvar,Nint+1)(:,end)); ] ); x = fsolve ( fun, x ); endfunction %!demo %! a = 0; %! b = 4; %! Nint = 3; %! Nvar = 2; %! s = 3; %! t = linspace(a,b,Nint+1); %! h = diff(t); %! u_1 = ones(1, Nint+1); %! u_2 = 0*u_1; %! u_0 = [u_1 ; u_2]; %! f = @(t,u) [ u(2); -abs(u(1)) ]; %! g = @(ya,yb) [ya(1); yb(1)+2]; %! solinit.x = t; solinit.y=u_0; %! sol = bvp4c(f,g,solinit); %! plot (sol.x,sol.y,'x-') %!demo %! a = 0; %! b = 4; %! Nint = 2; %! Nvar = 2; %! s = 3; %! t = linspace(a,b,Nint+1); %! h = diff(t); %! u_1 = -ones(1, Nint+1); %! u_2 = 0*u_1; %! u_0 = [u_1 ; u_2]; %! f = @(t,u) [ u(2); -abs(u(1)) ]; %! g = @(ya,yb) [ya(1); yb(1)+2]; %! solinit.x = t; solinit.y=u_0; %! sol = bvp4c(f,g,solinit); %! plot (sol.x,sol.y,'x-') odepkg-0.8.5/inst/ode23.m0000644000000000000000000010276212526637474013222 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (2,3). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example, solve an anonymous implementation of the Van der Pol equation %# %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# %# vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ %# "NormControl", "on", "OutputFcn", @@odeplot); %# ode23 (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} %# ChangeLog: %# 20010703 the function file "ode23.m" was written by Marc Compere %# under the GPL for the use with this software. This function has been %# taken as a base for the following implementation. %# 20060810, Thomas Treichl %# This function was adapted to the new syntax that is used by the %# new OdePkg for Octave and is compatible to Matlab's ode23. function [varargout] = ode23 (vfun, vslot, vinit, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode23'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 3) print_usage; elseif ~(isa (vfun, 'function_handle') || isa (vfun, 'inline')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (nargin >= 4) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode23'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode23'); vfunarguments = {}; end else %# if (nargin == 3) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options are set in %# vodeoptions, check if an invalid or unused option is set vslot = vslot(:).'; %# Create a row vector vinit = vinit(:).'; %# Create a row vector if (length (vslot) > 2) %# Step size checking vstepsizefixed = true; else vstepsizefixed = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizefixed) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizefixed) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else vodeoptions.AbsTol = vodeoptions.AbsTol(:); %# Create column vector end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')) vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidArgument', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option OutputSave has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputSave)), vodeoptions.OutputSave = 1; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (vodeoptions.Refine > 0), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizefixed) vodeoptions.InitialStep = (vslot(1,2) - vslot(1,1)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidArgument', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizefixed) vodeoptions.MaxStep = abs (vslot(1,2) - vslot(1,1)) / 10; warning ('OdePkg:InvalidArgument', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidArgument', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidArgument', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidArgument', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidArgument', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidArgument', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode23 vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value %# 20110611, reported by Nils Strunk %# Make it possible to solve equations from negativ to zero, %# eg. vres = ode23 (@(t,y) y, [-2 0], 2); vdirection = sign (vtimestop - vtimestamp); %# Direction flag if (~vstepsizefixed) if (sign (vodeoptions.InitialStep) == vdirection) vstepsize = vodeoptions.InitialStep; else %# Fix wrong direction of InitialStep. vstepsize = - vodeoptions.InitialStep; end vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = vslot(1,2) - vslot(1,1); vminstepsize = sign (vstepsize) * eps; end vretvaltime = vtimestamp; %# first timestamp output vretvalresult = vinit; %# first solution output %# Initialize the OutputFcn if (vhaveoutputfunction) if (vhaveoutputselection) vretout = vretvalresult(vodeoptions.OutputSel); else vretout = vretvalresult; end feval (vodeoptions.OutputFcn, vslot.', ... vretout.', 'init', vfunarguments{:}); end %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vretvalresult.', 'init', vfunarguments{:}); end vpow = 1/3; %# 20071016, reported by Luis Randez va = [ 0, 0, 0; %# The Runge-Kutta-Fehlberg 2(3) coefficients 1/2, 0, 0; %# Coefficients proved on 20060827 -1, 2, 0]; %# See p.91 in Ascher & Petzold vb2 = [0; 1; 0]; %# 2nd and 3rd order vb3 = [1/6; 2/3; 1/6]; %# b-coefficients vc = sum (va, 2); %# The solver main loop - stop if the endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu.' * zeros(1,3); vcntiter = 0; vunhandledtermination = true; vcntsave = 2; while ((vdirection * (vtimestamp) < vdirection * (vtimestop)) && ... (vdirection * (vstepsize) >= vdirection * (vminstepsize))) %# Hit the endpoint of the time slot exactely if (vdirection * (vtimestamp + vstepsize) > vdirection * vtimestop) %# vstepsize = vtimestop - vdirection * vtimestamp; %# 20110611, reported by Nils Strunk %# The endpoint of the time slot must be hit exactly, %# eg. vsol = ode23 (@(t,y) y, [0 -1], 1); vstepsize = vdirection * abs (abs (vtimestop) - abs (vtimestamp)); end %# Estimate the three results when using this solver for j = 1:3 vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu.' + vstepsize * vk(:,1:j-1) * va(j,1:j-1).'; if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); end end %# Compute the 2nd and the 3rd order estimation y2 = vu.' + vstepsize * (vk * vb2); y3 = vu.' + vstepsize * (vk * vb3); if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y2(vodeoptions.NonNegative) = abs (y2(vodeoptions.NonNegative)); y3(vodeoptions.NonNegative) = abs (y3(vodeoptions.NonNegative)); end if (vhaveoutputfunction && vhaverefine) vSaveVUForRefine = vu; end %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizefixed) if (~vnormcontrol) vdelta = abs (y3 - y2); vtau = max (vodeoptions.RelTol * abs (vu.'), vodeoptions.AbsTol); else vdelta = norm (y3 - y2, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu.', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizefixed == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y3.'; %# MC2001: the higher order estimation as "local extrapolation" %# Save the solution every vodeoptions.OutputSave steps if (mod (vcntloop-1,vodeoptions.OutputSave) == 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; vcntsave = vcntsave + 1; end vcntloop = vcntloop + 1; vcntiter = 0; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) for vcnt = 0:vodeoptions.Refine %# Approximation between told and t if (vhaverefine) %# Do interpolation vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine.' + vapproxtime * (vk * vb3); vapproxtime = (vtimestamp - vstepsize) + vapproxtime; else vapproxvals = vu.'; vapproxtime = vtimestamp; end if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end vpltret = feval (vodeoptions.OutputFcn, vapproxtime, ... vapproxvals, [], vfunarguments{:}); if vpltret %# Leave refinement loop break; end end if (vpltret) %# Leave main loop vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu(:), [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizefixed) %# 20080425, reported by Marco Caliari %# vdelta cannot be negative (because of the absolute value that %# has been introduced) but it could be 0, then replace the zeros %# with the maximum value of vdelta vdelta(find (vdelta == 0)) = max (vdelta); %# It could happen that max (vdelta) == 0 (ie. that the original %# vdelta was 0), in that case we double the previous vstepsize vdelta(find (vdelta == 0)) = max (vtau) .* (0.4 ^ (1 / vpow)); if (vdirection == 1) vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); else vstepsize = max (- vodeoptions.MaxStep, ... max (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); end else %# if (vstepsizefixed) if (vcntloop <= vtimelength) vstepsize = vslot(vcntloop) - vslot(vcntloop-1); else %# Get out of the main integration loop break; end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for cost statistics vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vdirection * vtimestamp < vdirection * vtimestop) if (vunhandledtermination == true) error ('OdePkg:InvalidArgument', ... ['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:InvalidArgument', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vu.', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu.', 'done', vfunarguments{:}); end %# Save the last step, if not already saved if (mod (vcntloop-2,vodeoptions.OutputSave) ~= 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 3*(vcntcycles-1); %# number of ode evaluations vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d\n', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d\n', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d\n', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode23'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end end end %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference sol %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidArgument'); %! B = ode23 (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = ode23 (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = ode23 (@flor, [0 25], 1); %!test %# one output argument %! vsol = ode23 (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode23'); %!test %# two output arguments %! [vt, vy] = ode23 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode23 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = ode23 (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed through %! vsol = ode23 (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode23 (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %!test %# Solve vdp in fixed step sizes %! vsol = ode23 (@fpol, [0:0.1:2], [2 0]); %! assert (vsol.x(:), [0:0.1:2]'); %! assert (vsol.y(end,:), fref, 1e-3); %!test %# Solve in backward direction starting at t=0 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode23 (@fpol, [0 -2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve in backward direction starting at t=2 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode23 (@fpol, [2 -2], fref); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve another anonymous function in backward direction %! vref = [-1, 0.367879437558975]; %! vsol = ode23 (@(t,y) y, [0 -1], 1); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# Solve another anonymous function below zero %! vref = [0, 14.77810590694212]; %! vsol = ode23 (@(t,y) y, [-2 0], 2); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, 2, 0], 1e-1); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %!test %# Details of OutputSave can't be tested %! vopt = odeset ('OutputSave', 1, 'OutputSel', 1); %! vsla = ode23 (@fpol, [0 2], [2 0], vopt); %! vopt = odeset ('OutputSave', 2); %! vslb = ode23 (@fpol, [0 2], [2 0], vopt); %! assert (length (vsla.x) > length (vslb.x)) %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode23 (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode23 (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode23 (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie, [2; 1; 2; 1]); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode23 (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-3); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode23 (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-3); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode23 (@fpol, [0 2], [2 0], vopt); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidArgument'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/ode23d.m0000644000000000000000000010363012526637474013361 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode23d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode23d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode23d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (2,3). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# In other words, this function will solve a problem of the form %# @example %# dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) %# y(slot(1)) = init %# y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} %# @end example %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example: %# @itemize @minus %# @item %# the following code solves an anonymous implementation of a chaotic behavior %# %# @example %# fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; %# %# vopt = odeset ("NormControl", "on", "RelTol", 1e-3); %# vsol = ode23d (fcao, [0, 100], 0.5, 2, 0.5, vopt); %# %# vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); %# plot (vsol.y, vlag); legend ("fcao (t,y,z)"); %# @end example %# %# @item %# to solve the following problem with two delayed state variables %# %# @example %# d y1(t)/dt = -y1(t) %# d y2(t)/dt = -y2(t) + y1(t-5) %# d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) %# @end example %# %# one might do the following %# %# @example %# function f = fun (t, y, yd) %# f(1) = -y(1); %% y1' = -y1(t) %# f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) %# f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) %# endfunction %# T = [0,20] %# res = ode23d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); %# @end example %# %# @end itemize %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = ode23d (vfun, vslot, vinit, vlags, vhist, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode23d'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 5) print_usage; elseif (~isa (vfun, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (~isvector (vlags) || ~isnumeric (vlags)) error ('OdePkg:InvalidArgument', ... 'Fourth input argument must be a valid numerical value'); elseif ~(isnumeric (vhist) || isa (vhist, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'Fifth input argument must either be numeric or a function handle'); elseif (nargin >= 6) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode23d'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode23d'); vfunarguments = {}; end else %# if (nargin == 5) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options have been set in %# vodeoptions. Check if an invalid or unused option has been set and %# print warnings. vslot = vslot(:)'; %# Create a row vector vinit = vinit(:)'; %# Create a row vector vlags = vlags(:)'; %# Create a row vector %# Check if the user has given fixed points of time if (length (vslot) > 2), vstepsizegiven = true; %# Step size checking else vstepsizegiven = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizegiven) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); %# This implementation has been added to odepkg_structure_check.m %# elseif (~isscalar (vodeoptions.RelTol) && ~vstepsizegiven) %# error ('OdePkg:InvalidOption', ... %# 'Option "RelTol" must be set to a scalar value for this solver'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizegiven) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else %# create column vector vodeoptions.AbsTol = vodeoptions.AbsTol(:); end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')), vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidOption', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (isequal (vodeoptions.Refine, vodetemp.Refine)), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizegiven) vodeoptions.InitialStep = abs (vslot(1,1) - vslot(1,2)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizegiven) vodeoptions.MaxStep = abs (vslot(1,1) - vslot(1,length (vslot))) / 10; %# vodeoptions.MaxStep = vodeoptions.MaxStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidOption', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidOption', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidOption', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidOption', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidOption', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidOption', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidOption', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidOption', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode23d vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value if (~vstepsizegiven) vstepsize = vodeoptions.InitialStep; vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = abs (vslot(1,1) - vslot(1,2)); vminstepsize = eps; %# vslot(1,2) - vslot(1,1) - eps; end vretvaltime = vtimestamp; %# first timestamp output if (vhaveoutputselection) %# first solution output vretvalresult = vinit(vodeoptions.OutputSel); else vretvalresult = vinit; end %# Initialize the OutputFcn if (vhaveoutputfunction) feval (vodeoptions.OutputFcn, vslot', ... vretvalresult', 'init', vfunarguments{:}); end %# Initialize the History if (isnumeric (vhist)) vhmat = vhist; vhavehistnumeric = true; else %# it must be a function handle for vcnt = 1:length (vlags); vhmat(:,vcnt) = feval (vhist, (vslot(1)-vlags(vcnt)), vfunarguments{:}); end vhavehistnumeric = false; end %# Initialize DDE variables for history calculation vsaveddetime = [vtimestamp - vlags, vtimestamp]'; vsaveddeinput = [vhmat, vinit']'; vsavedderesult = [vhmat, vinit']'; %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult', vhmat}, 'init', vfunarguments{:}); end vpow = 1/3; %# 20071016, reported by Luis Randez va = [ 0, 0, 0; %# The Runge-Kutta-Fehlberg 2(3) coefficients 1/2, 0, 0; %# Coefficients proved on 20060827 -1, 2, 0]; %# See p.91 in Ascher & Petzold vb2 = [0; 1; 0]; %# 2nd and 3rd order vb3 = [1/6; 2/3; 1/6]; %# b-coefficients vc = sum (va, 2); %# The solver main loop - stop if the endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu' * zeros(1,3); vcntiter = 0; vunhandledtermination = true; while ((vtimestamp < vtimestop && vstepsize >= vminstepsize)) %# Hit the endpoint of the time slot exactely if ((vtimestamp + vstepsize) > vtimestop) vstepsize = vtimestop - vtimestamp; end %# Estimate the three results when using this solver for j = 1:3 vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu' + vstepsize * vk(:,1:j-1) * va(j,1:j-1)'; %# Claculate the history values (or get them from an external %# function) that are needed for the next step of solving if (vhavehistnumeric) for vcnt = 1:length (vlags) %# Direct implementation of a 'quadrature cubic Hermite interpolation' %# found at the Faculty for Mathematics of the University of Stuttgart %# http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage1269 vnumb = find (vthetime - vlags(vcnt) >= vsaveddetime); velem = min (vnumb(end), length (vsaveddetime) - 1); vstep = vsaveddetime(velem+1) - vsaveddetime(velem); vdiff = (vthetime - vlags(vcnt) - vsaveddetime(velem)) / vstep; vsubs = 1 - vdiff; %# Calculation of the coefficients for the interpolation algorithm vua = (1 + 2 * vdiff) * vsubs^2; vub = (3 - 2 * vdiff) * vdiff^2; vva = vstep * vdiff * vsubs^2; vvb = -vstep * vsubs * vdiff^2; vhmat(:,vcnt) = vua * vsaveddeinput(velem,:)' + ... vub * vsaveddeinput(velem+1,:)' + ... vva * vsavedderesult(velem,:)' + ... vvb * vsavedderesult(velem+1,:)'; end else %# the history must be a function handle for vcnt = 1:length (vlags) vhmat(:,vcnt) = feval ... (vhist, vthetime - vlags(vcnt), vfunarguments{:}); end end if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); end end %# Compute the 2nd and the 3rd order estimation y2 = vu' + vstepsize * (vk * vb2); y3 = vu' + vstepsize * (vk * vb3); if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y2(vodeoptions.NonNegative) = abs (y2(vodeoptions.NonNegative)); y3(vodeoptions.NonNegative) = abs (y3(vodeoptions.NonNegative)); end vSaveVUForRefine = vu; %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizegiven) if (~vnormcontrol) vdelta = y3 - y2; vtau = max (vodeoptions.RelTol * vu', vodeoptions.AbsTol); else vdelta = norm (y3 - y2, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizegiven == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y3'; %# MC2001: the higher order estimation as "local extrapolation" vretvaltime(vcntloop,:) = vtimestamp; if (vhaveoutputselection) vretvalresult(vcntloop,:) = vu(vodeoptions.OutputSel); else vretvalresult(vcntloop,:) = vu; end vcntloop = vcntloop + 1; vcntiter = 0; %# Update DDE values for next history calculation vsaveddetime(end+1) = vtimestamp; vsaveddeinput(end+1,:) = vtheinput'; vsavedderesult(end+1,:) = vu; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) if (vhaverefine) %# Do interpolation for vcnt = 0:vodeoptions.Refine %# Approximation between told and t vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine' + vapproxtime * (vk * vb3); if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end feval (vodeoptions.OutputFcn, (vtimestamp - vstepsize) + vapproxtime, ... vapproxvals, [], vfunarguments{:}); end end vpltret = feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', [], vfunarguments{:}); if (vpltret), vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vu(:), vhmat}, [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizegiven) %# vdelta may be 0 or even negative - could be an iteration problem vdelta = max (vdelta, eps); vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); elseif (vstepsizegiven) if (vcntloop < vtimelength) vstepsize = vslot(1,vcntloop-1) - vslot(1,vcntloop-2); end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for postprocessing vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vtimestamp < vtimestop) if (vunhandledtermination == true) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:HideWarning', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult(vcntloop-1,:), vhmat}, 'done', vfunarguments{:}); end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 3*(vcntcycles-1); %# number of ode evaluations vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode23d'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end %# else nothing will be returned, varargout{1} undefined end %! # We are using a "pseudo-DDE" implementation for all tests that %! # are done for this function. We also define an Events and a %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn. %!function [vyd] = fexp (vt, vy, vz, varargin) %! vyd(1,1) = exp (- vt) - vz(1); %# The DDEs that are %! vyd(2,1) = vy(1) - vz(2); %# used for all examples %!function [vval, vtrm, vdir] = feve (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = zeros (2,1); %# don't stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vval, vtrm, vdir] = fevn (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = ones (2,1); %# stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vmas] = fmas (vt, vy, vz, varargin) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy, vz, varargin) %! vmas = sparse ([1, 0; 0, 1]); %# A dummy sparse matrix %!function [vref] = fref () %# The reference solution %! vref = [0.12194462133618, 0.01652432423938]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = ode23d (1, [0 5], [1; 0], 1, [1; 0]); %!error %# input argument number two %! B = ode23d (@fexp, 1, [1; 0], 1, [1; 0]); %!error %# input argument number three %! B = ode23d (@fexp, [0 5], 1, 1, [1; 0]); %!error %# input argument number four %! B = ode23d (@fexp, [0 5], [1; 0], [1; 1], [1; 0]); %!error %# input argument number five %! B = ode23d (@fexp, [0 5], [1; 0], 1, 1); %!test %# one output argument %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode23d'); %!test %# two output arguments %! [vt, vy] = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 1e-1); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 1e-1); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! faym = @(vt, vy, vz) [exp(-vt) - vz(1); vy(1) - vz(2)]; %! vsol = ode23d (faym, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# extra input arguments passed trhough %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-7, 'RelTol', 1e-7); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# RelTol and NormControl option %! vopt = odeset ('AbsTol', 1e-7, 'NormControl', 'on'); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], .5e-1); %!test %# NonNegative for second component %! vopt = odeset ('NonNegative', 1); %! vsol = ode23d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2.5, 0.001, 0.237], 1e-1); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode23d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode23d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie, [1; 1]); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vie, vxe, vye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 1e-1); %! %! %# test for Jacobian option is missing %! %# test for Jacobian (being a sparse matrix) is missing %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode23d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vt(end), vy(end,:)], [5, fref], 0.5); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidOption'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/ode23s.m0000644000000000000000000002201712526637474013377 0ustar 00000000000000## Copyright (C) 2012 Davide Prandi ## Copyright (C) 2012 Carlo de Falco ## ## This file 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, either version 3 of the License, or ## (at your option) any later version. ## ## This file 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 file. If not, see ## -*- texinfo -*- ## @deftypefn{Function File} {[@var{tout}, @var{xout}] =} ode23s (@var{FUN}, @var{tspan}, @var{x0}, @var{options}) ## ## This function can be used to solve a set of stiff ordinary differential ## equations with a Rosenbrock method of order (2,3). ## ## All the mathematical formulas are from ## "The MATLAB ode suite", L.F. Shampine, M.W. Reichelt, pp.6-7 ## ## @itemize @minus ## @item @var{FUN}: String or function-handle for the problem description. ## @itemize @minus ## @item signature: @code{xprime = fun (t,x)} ## @item t: Time (scalar). ## @item x: Solution (column-vector). ## @item xprime: Returned derivative (column-vector, @code{xprime(i) = dx(i) / dt}). ## @end itemize ## @item @var{tspan}: Initial value column vector [tstart, tfinal] ## @item @var{x0}: Initial value (column-vector). ## @item @var{options}: User-defined integration parameters, using "odeset". ## See @code{help odeset} for more details. ## Option parameters currently accepted are: RelTol, MaxStep, InitialStep, Mass, Jacobian, JPattern. ## If "options" is not used, these parameters will be given default values. ## ode23s solves problems in the form M*y' = FUN (t, y), where M is a costant mass matrix, ## non-singular and possibly sparse. Set the filed @var{mass} in @var{options} using @var{odeset} ## to specify a mass matrix. ## @end itemize ## ## Example: ## @example ## f=@@(t,y) [y(2); 1000*(1-y(1)^2)*y(2)-y(1)]; ## opt = odeset ('Mass', [1 0; 0 1], 'MaxStep', 1e-1); ## [vt, vy] = ode23s (f, [0 2000], [2 0], opt); ## @end example ## ## The structure of the code is based on "ode23.m", written by Marc Compere. ## @seealso{ode23, odepkg, odeset, daspk, dassl} ## @end deftypefn % Author: Davide Prandi % Created: 5 September 2012 function [tout, xout] = ode23s (FUN, tspan, x0, options) if nargin < 4, options = odeset (); end %% Initialization d = 1 / (2 + sqrt (2)); a = 1 / 2; e32 = 6 + sqrt (2); jacfun = false; jacmat = false; if (isfield (options, 'Jacobian') && ! isempty (options.Jacobian)) %user-defined Jacobian if (ischar (options.Jacobian)) jacfun = true; jac = str2fun (options.Jacobian); elseif (is_function_handle (options.Jacobian)) jacfun = true; jac = options.Jacobian; elseif (ismatrix (options.Jacobian)) jacmat = true; jac = options.Jacobian; else error ("ode23s: the jacobian should be passed as a matrix, a string or a function handle") endif endif jacpat = false; if (isfield (options, 'JPattern') && ! isempty (options.JPattern)) %user-defined Jacobian jacpat = true; [ii, jj] = options.Jacobian; pattern = sparse (ii, jj, true); endif if (isfield (options, 'RelTol') && ! isempty (options.RelTol)) %user-defined relative tolerance rtol = options.RelTol; else rtol = 1.0e-3; endif if (isfield (options, 'AbsTol') && ! isempty (options.AbsTol)) %user-defined absolute tolerance atol = options.AbsTol; else atol = 1.0e-6; endif t = tspan(1); tfinal = tspan(2); if (isfield (options, 'MaxStep') && ! isempty (options.MaxStep)) %user-defined max step size hmax = options.MaxStep; else hmax = .1 * abs (tfinal - t); endif if (isfield (options, 'InitialStep') && ! isempty (options.InitialStep)) %user-defined initial step size h = options.InitialStep else h = (tfinal - t) * .05; % initial guess at a step size end hmin = min (16 * eps (tfinal - t), h); x = x0(:); % this always creates a column vector, x tout = t; % first output time xout = x.'; % first output solution %% The main loop while (t < tfinal) && (h >= hmin) if t + h > tfinal h = tfinal - t; endif %% Jacobian matrix, dfxpdp if (jacmat) J = jac; elseif (jacfun) J = jac (t, x); elseif (! jacpat) J = __dfxpdp__ (x, @(a) feval (FUN, t, a), rtol); elseif (jacpat) J = __dfxpdp__ (x, @(a) feval (FUN, t, a), rtol, pattern); endif T = (feval (FUN, t + .1 * h, x) - feval (FUN, t, x)) / (.1 * h); %% Wolfbrandt coefficient W = eye (length (x0))- h*d*J; %% compute the slopes F(:,1) = feval (FUN, t, x); k(:,1) = W \ (F(:,1) + h*d*T); F(:,2) = feval (FUN, t+a*h, x+a*h*k(:,1)); k(:,2) = W \ ((F(:,2) - k(:,1))) + k(:,1); %% compute the 2nd order estimate x2 = x + h*k(:,2); %% 3rd order, needed in error forumula F(:,3) = feval (FUN, t+h, x2); k(:,3) = W \ (F(:,3) - e32 * (k(:,2)-F(:,2)) - 2 * (k(:,1)-F(:,1)) + h*d*T); %% estimate the error err = (h/6) * (k(:,1) - 2*k(:,2) + k(:,3)); %% Estimate the acceptable error tau = max (rtol .* abs (x), atol); %% Update the solution only if the error is acceptable if all (err <= tau) t = t + h; tout = [tout; t]; x = x2; %no local extrapolation, FSAL (See documentation) if (isfield (options', "Mass") && ! (isempty (options.Mass))) %%user-defined mass matrix M = options.Mass; xout = [xout; (M \ x).']; else xout = [xout; x.']; end %% Update the step size if (err == 0.0) err = 1e-16; endif h = min (hmax, h*1.25); % adaptive step update else if (h <= hmin) error ("ode23s: requested step-size too small at t = %g, h = %g, err = %g \n", t, h, err) endif h = max (hmin, h*0.5); % adaptive step update endif endwhile if (t < tfinal) error ("ode23s: requested step-size too small at t = %g, h = %g, err = %g \n", t, h, err) endif endfunction %% The following function is copied from the optim %% package of Octave-Forge %% Copyright (C) 1992-1994 Richard Shrager %% Copyright (C) 1992-1994 Arthur Jutan %% Copyright (C) 1992-1994 Ray Muzic %% Copyright (C) 2010, 2011 Olaf Till function prt = __dfxpdp__ (p, func, rtol, pattern) f = func (p)(:); m = numel (f); n = numel (p); diffp = rtol .* ones (n, 1); sparse_jac = false; if (nargin > 3 && issparse (pattern)) sparse_jac = true; endif %% initialise Jacobian to Zero if (sparse_jac) prt = pattern; else prt = zeros (m, n); endif del = ifelse (p == 0, diffp, diffp .* p); absdel = abs (del); p2 = p1 = zeros (n, 1); %% double sided interval p1 = p + absdel/2; p2 = p - absdel/2; ps = p; if (! sparse_jac) for j = 1:n ps(j) = p1(j); tp1 = func (ps); ps(j) = p2(j); tp2 = func (ps); prt(:, j) = (tp1(:) - tp2(:)) / absdel(j); ps(j) = p(j); endfor else for j = find (any (pattern, 1)) ps(j) = p1(j); tp1 = func (ps); ps(j) = p2(j); tp2 = func (ps); nnz = find (pattern(:, j)); prt(nnz, j) = (tp1(nnz) - tp2(nnz)) / absdel(j); ps(j) = p(j); endfor endif endfunction %!test %! test1=@(t,y) t - y + 1; %! [vt, vy] = ode23s (test1, [0 10], [1]); %! assert ([vt(end), vy(end)], [10, exp(-10) + 10], 1e-3); %!demo %! # Demo function: stiff Van Der Pol equation %! fun = @(t,y) [y(2); 10*(1-y(1)^2)*y(2)-y(1)]; %! # Calling ode23s method %! tic () %! [vt, vy] = ode23s (fun, [0 20], [2 0]); %! toc () %! # Plotting the result %! plot(vt,vy(:,1),'-o'); %!demo %! # Demo function: stiff Van Der Pol equation %! fun = @(t,y) [y(2); 10*(1-y(1)^2)*y(2)-y(1)]; %! # Calling ode23s method %! options = odeset ("Jacobian", @(t,y) [0 1; -20*y(1)*y(2)-1, 10*(1-y(1)^2)], %! "InitialStep", 1e-3) %! tic () %! [vt, vy] = ode23s (fun, [0 20], [2 0], options); %! toc () %! # Plotting the result %! plot(vt,vy(:,1),'-o'); %!demo %! # Demo function: stiff Van Der Pol equation %! fun = @(t,y) [y(2); 100*(1-y(1)^2)*y(2)-y(1)]; %! # Calling ode23s method %! %! options = odeset ("InitialStep", 1e-4); %! tic () %! [vt, vy] = ode23s (fun, [0 200], [2 0]); %! toc () %! # Plotting the result %! plot(vt,vy(:,1),'-o'); %!demo %! # Demo function: stiff Van Der Pol equation %! fun = @(t,y) [y(2); 100*(1-y(1)^2)*y(2)-y(1)]; %! # Calling ode23s method %! options = odeset ("Jacobian", @(t,y) [0 1; -200*y(1)*y(2)-1, 100*(1-y(1)^2)], %! "InitialStep", 1e-4); %! tic () %! [vt, vy] = ode23s (fun, [0 200], [2 0], options); %! toc () %! # Plotting the result %! plot(vt,vy(:,1),'-o'); odepkg-0.8.5/inst/ode45.m0000644000000000000000000010330312526637474013216 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode45 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode45 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode45 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (4,5). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example, solve an anonymous implementation of the Van der Pol equation %# %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# %# vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ %# "NormControl", "on", "OutputFcn", @@odeplot); %# ode45 (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} %# ChangeLog: %# 20010703 the function file "ode45.m" was written by Marc Compere %# under the GPL for the use with this software. This function has been %# taken as a base for the following implementation. %# 20060810, Thomas Treichl %# This function was adapted to the new syntax that is used by the %# new OdePkg for Octave and is compatible to Matlab's ode45. function [varargout] = ode45 (vfun, vslot, vinit, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode45'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 3) print_usage; elseif ~(isa (vfun, 'function_handle') || isa (vfun, 'inline')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (nargin >= 4) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode45'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode45'); vfunarguments = {}; end else %# if (nargin == 3) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options are set in %# vodeoptions, check if an invalid or unused option is set vslot = vslot(:).'; %# Create a row vector vinit = vinit(:).'; %# Create a row vector if (length (vslot) > 2) %# Step size checking vstepsizefixed = true; else vstepsizefixed = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizefixed) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizefixed) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else vodeoptions.AbsTol = vodeoptions.AbsTol(:); %# Create column vector end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')) vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidArgument', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option OutputSave has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputSave)), vodeoptions.OutputSave = 1; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (vodeoptions.Refine > 0), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizefixed) vodeoptions.InitialStep = (vslot(1,2) - vslot(1,1)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidArgument', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizefixed) vodeoptions.MaxStep = abs (vslot(1,2) - vslot(1,1)) / 10; warning ('OdePkg:InvalidArgument', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidArgument', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidArgument', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidArgument', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidArgument', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidArgument', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode45 vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value %# 20110611, reported by Nils Strunk %# Make it possible to solve equations from negativ to zero, %# eg. vres = ode45 (@(t,y) y, [-2 0], 2); vdirection = sign (vtimestop - vtimestamp); %# Direction flag if (~vstepsizefixed) if (sign (vodeoptions.InitialStep) == vdirection) vstepsize = vodeoptions.InitialStep; else %# Fix wrong direction of InitialStep. vstepsize = - vodeoptions.InitialStep; end vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = vslot(1,2) - vslot(1,1); vminstepsize = sign (vstepsize) * eps; end vretvaltime = vtimestamp; %# first timestamp output vretvalresult = vinit; %# first solution output %# Initialize the OutputFcn if (vhaveoutputfunction) if (vhaveoutputselection) vretout = vretvalresult(vodeoptions.OutputSel); else vretout = vretvalresult; end feval (vodeoptions.OutputFcn, vslot.', ... vretout.', 'init', vfunarguments{:}); end %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vretvalresult.', 'init', vfunarguments{:}); end vpow = 1/5; %# 20071016, reported by Luis Randez va = [0, 0, 0, 0, 0; %# The Runge-Kutta-Fehlberg 4(5) coefficients 1/4, 0, 0, 0, 0; %# Coefficients proved on 20060827 3/32, 9/32, 0, 0, 0; %# See p.91 in Ascher & Petzold 1932/2197, -7200/2197, 7296/2197, 0, 0; 439/216, -8, 3680/513, -845/4104, 0; -8/27, 2, -3544/2565, 1859/4104, -11/40]; %# 4th and 5th order b-coefficients vb4 = [25/216; 0; 1408/2565; 2197/4104; -1/5; 0]; vb5 = [16/135; 0; 6656/12825; 28561/56430; -9/50; 2/55]; vc = sum (va, 2); %# The solver main loop - stop if the endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu.' * zeros(1,6); vcntiter = 0; vunhandledtermination = true; vcntsave = 2; while ((vdirection * (vtimestamp) < vdirection * (vtimestop)) && ... (vdirection * (vstepsize) >= vdirection * (vminstepsize))) %# Hit the endpoint of the time slot exactely if (vdirection * (vtimestamp + vstepsize) > vdirection * vtimestop) %# vstepsize = vtimestop - vdirection * vtimestamp; %# 20110611, reported by Nils Strunk %# The endpoint of the time slot must be hit exactly, %# eg. vsol = ode45 (@(t,y) y, [0 -1], 1); vstepsize = vdirection * abs (abs (vtimestop) - abs (vtimestamp)); end %# Estimate the six results when using this solver for j = 1:6 vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu.' + vstepsize * vk(:,1:j-1) * va(j,1:j-1).'; if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); end end %# Compute the 4th and the 5th order estimation y4 = vu.' + vstepsize * (vk * vb4); y5 = vu.' + vstepsize * (vk * vb5); if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y4(vodeoptions.NonNegative) = abs (y4(vodeoptions.NonNegative)); y5(vodeoptions.NonNegative) = abs (y5(vodeoptions.NonNegative)); end if (vhaveoutputfunction && vhaverefine) vSaveVUForRefine = vu; end %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizefixed) if (~vnormcontrol) vdelta = abs (y5 - y4); vtau = max (vodeoptions.RelTol * abs (vu.'), vodeoptions.AbsTol); else vdelta = norm (y5 - y4, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu.', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizefixed == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y5.'; %# MC2001: the higher order estimation as "local extrapolation" %# Save the solution every vodeoptions.OutputSave steps if (mod (vcntloop-1,vodeoptions.OutputSave) == 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; vcntsave = vcntsave + 1; end vcntloop = vcntloop + 1; vcntiter = 0; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) for vcnt = 0:vodeoptions.Refine %# Approximation between told and t if (vhaverefine) %# Do interpolation vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine.' + vapproxtime * (vk * vb5); vapproxtime = (vtimestamp - vstepsize) + vapproxtime; else vapproxvals = vu.'; vapproxtime = vtimestamp; end if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end vpltret = feval (vodeoptions.OutputFcn, vapproxtime, ... vapproxvals, [], vfunarguments{:}); if vpltret %# Leave refinement loop break; end end if (vpltret) %# Leave main loop vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu(:), [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizefixed) %# 20080425, reported by Marco Caliari %# vdelta cannot be negative (because of the absolute value that %# has been introduced) but it could be 0, then replace the zeros %# with the maximum value of vdelta vdelta(find (vdelta == 0)) = max (vdelta); %# It could happen that max (vdelta) == 0 (ie. that the original %# vdelta was 0), in that case we double the previous vstepsize vdelta(find (vdelta == 0)) = max (vtau) .* (0.4 ^ (1 / vpow)); if (vdirection == 1) vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); else vstepsize = max (- vodeoptions.MaxStep, ... max (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); end else %# if (vstepsizefixed) if (vcntloop <= vtimelength) vstepsize = vslot(vcntloop) - vslot(vcntloop-1); else %# Get out of the main integration loop break; end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for cost statistics vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vdirection * vtimestamp < vdirection * vtimestop) if (vunhandledtermination == true) error ('OdePkg:InvalidArgument', ... ['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:InvalidArgument', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vu.', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu.', 'done', vfunarguments{:}); end %# Save the last step, if not already saved if (mod (vcntloop-2,vodeoptions.OutputSave) ~= 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 6*(vcntcycles-1); %# number of ode evaluations vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d\n', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d\n', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d\n', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode45'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end end end %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference sol %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidArgument'); %! B = ode45 (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = ode45 (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = ode45 (@flor, [0 25], 1); %!test %# one output argument %! vsol = ode45 (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode45'); %!test %# two output arguments %! [vt, vy] = ode45 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode45 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = ode45 (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed through %! vsol = ode45 (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode45 (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %!test %# Solve vdp in fixed step sizes %! vsol = ode45 (@fpol, [0:0.1:2], [2 0]); %! assert (vsol.x(:), [0:0.1:2]'); %! assert (vsol.y(end,:), fref, 1e-3); %!test %# Solve in backward direction starting at t=0 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode45 (@fpol, [0 -2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve in backward direction starting at t=2 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode45 (@fpol, [2 -2], fref); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve another anonymous function in backward direction %! vref = [-1, 0.367879437558975]; %! vsol = ode45 (@(t,y) y, [0 -1], 1); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# Solve another anonymous function below zero %! vref = [0, 14.77810590694212]; %! vsol = ode45 (@(t,y) y, [-2 0], 2); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-5); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, 2, 0], 0.5); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %!test %# Details of OutputSave can't be tested %! vopt = odeset ('OutputSave', 1, 'OutputSel', 1); %! vsla = ode45 (@fpol, [0 2], [2 0], vopt); %! vopt = odeset ('OutputSave', 2); %! vslb = ode45 (@fpol, [0 2], [2 0], vopt); %! assert (length (vsla.x) > length (vslb.x)) %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode45 (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode45 (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-3); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode45 (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode45 (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], .5e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode45 (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode45 (@fpol, [0 2], [2 0], vopt); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidArgument'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/ode45d.m0000644000000000000000000010413312526637474013364 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode45d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode45d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode45d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (4,5). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# In other words, this function will solve a problem of the form %# @example %# dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) %# y(slot(1)) = init %# y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} %# @end example %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example: %# @itemize @minus %# @item %# the following code solves an anonymous implementation of a chaotic behavior %# %# @example %# fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; %# %# vopt = odeset ("NormControl", "on", "RelTol", 1e-3); %# vsol = ode45d (fcao, [0, 100], 0.5, 2, 0.5, vopt); %# %# vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); %# plot (vsol.y, vlag); legend ("fcao (t,y,z)"); %# @end example %# %# @item %# to solve the following problem with two delayed state variables %# %# @example %# d y1(t)/dt = -y1(t) %# d y2(t)/dt = -y2(t) + y1(t-5) %# d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) %# @end example %# %# one might do the following %# %# @example %# function f = fun (t, y, yd) %# f(1) = -y(1); %% y1' = -y1(t) %# f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) %# f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) %# endfunction %# T = [0,20] %# res = ode45d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); %# @end example %# %# @end itemize %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = ode45d (vfun, vslot, vinit, vlags, vhist, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode45d'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 5) print_usage; elseif (~isa (vfun, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (~isvector (vlags) || ~isnumeric (vlags)) error ('OdePkg:InvalidArgument', ... 'Fourth input argument must be a valid numerical value'); elseif ~(isnumeric (vhist) || isa (vhist, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'Fifth input argument must either be numeric or a function handle'); elseif (nargin >= 6) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode45d'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode45d'); vfunarguments = {}; end else %# if (nargin == 5) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options have been set in %# vodeoptions. Check if an invalid or unused option has been set and %# print warnings. vslot = vslot(:)'; %# Create a row vector vinit = vinit(:)'; %# Create a row vector vlags = vlags(:)'; %# Create a row vector %# Check if the user has given fixed points of time if (length (vslot) > 2), vstepsizegiven = true; %# Step size checking else vstepsizegiven = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizegiven) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); %# This implementation has been added to odepkg_structure_check.m %# elseif (~isscalar (vodeoptions.RelTol) && ~vstepsizegiven) %# error ('OdePkg:InvalidOption', ... %# 'Option "RelTol" must be set to a scalar value for this solver'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizegiven) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else %# create column vector vodeoptions.AbsTol = vodeoptions.AbsTol(:); end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')), vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidOption', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (isequal (vodeoptions.Refine, vodetemp.Refine)), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizegiven) vodeoptions.InitialStep = abs (vslot(1,1) - vslot(1,2)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizegiven) vodeoptions.MaxStep = abs (vslot(1,1) - vslot(1,length (vslot))) / 10; %# vodeoptions.MaxStep = vodeoptions.MaxStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidOption', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidOption', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidOption', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidOption', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidOption', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidOption', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidOption', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidOption', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode45d vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value if (~vstepsizegiven) vstepsize = vodeoptions.InitialStep; vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = abs (vslot(1,1) - vslot(1,2)); vminstepsize = eps; %# vslot(1,2) - vslot(1,1) - eps; end vretvaltime = vtimestamp; %# first timestamp output if (vhaveoutputselection) %# first solution output vretvalresult = vinit(vodeoptions.OutputSel); else vretvalresult = vinit; end %# Initialize the OutputFcn if (vhaveoutputfunction) feval (vodeoptions.OutputFcn, vslot', ... vretvalresult', 'init', vfunarguments{:}); end %# Initialize the History if (isnumeric (vhist)) vhmat = vhist; vhavehistnumeric = true; else %# it must be a function handle for vcnt = 1:length (vlags); vhmat(:,vcnt) = feval (vhist, (vslot(1)-vlags(vcnt)), vfunarguments{:}); end vhavehistnumeric = false; end %# Initialize DDE variables for history calculation vsaveddetime = [vtimestamp - vlags, vtimestamp]'; vsaveddeinput = [vhmat, vinit']'; vsavedderesult = [vhmat, vinit']'; %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult', vhmat}, 'init', vfunarguments{:}); end vpow = 1/5; %# 20071016, reported by Luis Randez va = [0, 0, 0, 0, 0; %# The Runge-Kutta-Fehlberg 4(5) coefficients 1/4, 0, 0, 0, 0; %# Coefficients proved on 20060827 3/32, 9/32, 0, 0, 0; %# See p.91 in Ascher & Petzold 1932/2197, -7200/2197, 7296/2197, 0, 0; 439/216, -8, 3680/513, -845/4104, 0; -8/27, 2, -3544/2565, 1859/4104, -11/40]; %# 4th and 5th order b-coefficients vb4 = [25/216; 0; 1408/2565; 2197/4104; -1/5; 0]; vb5 = [16/135; 0; 6656/12825; 28561/56430; -9/50; 2/55]; vc = sum (va, 2); %# The solver main loop - stop if endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu' * zeros(1,6); vcntiter = 0; vunhandledtermination = true; while ((vtimestamp < vtimestop && vstepsize >= vminstepsize)) %# Hit the endpoint of the time slot exactely if ((vtimestamp + vstepsize) > vtimestop) vstepsize = vtimestop - vtimestamp; end %# Estimate the six results when using this solver for j = 1:6 vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu' + vstepsize * vk(:,1:j-1) * va(j,1:j-1)'; %# Claculate the history values (or get them from an external %# function) that are needed for the next step of solving if (vhavehistnumeric) for vcnt = 1:length (vlags) %# Direct implementation of a 'quadrature cubic Hermite interpolation' %# found at the Faculty for Mathematics of the University of Stuttgart %# http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage1269 vnumb = find (vthetime - vlags(vcnt) >= vsaveddetime); velem = min (vnumb(end), length (vsaveddetime) - 1); vstep = vsaveddetime(velem+1) - vsaveddetime(velem); vdiff = (vthetime - vlags(vcnt) - vsaveddetime(velem)) / vstep; vsubs = 1 - vdiff; %# Calculation of the coefficients for the interpolation algorithm vua = (1 + 2 * vdiff) * vsubs^2; vub = (3 - 2 * vdiff) * vdiff^2; vva = vstep * vdiff * vsubs^2; vvb = -vstep * vsubs * vdiff^2; vhmat(:,vcnt) = vua * vsaveddeinput(velem,:)' + ... vub * vsaveddeinput(velem+1,:)' + ... vva * vsavedderesult(velem,:)' + ... vvb * vsavedderesult(velem+1,:)'; end else %# the history must be a function handle for vcnt = 1:length (vlags) vhmat(:,vcnt) = feval ... (vhist, vthetime - vlags(vcnt), vfunarguments{:}); end end if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); end end %# Compute the 4th and the 5th order estimation y4 = vu' + vstepsize * (vk * vb4); y5 = vu' + vstepsize * (vk * vb5); if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y4(vodeoptions.NonNegative) = abs (y4(vodeoptions.NonNegative)); y5(vodeoptions.NonNegative) = abs (y5(vodeoptions.NonNegative)); end vSaveVUForRefine = vu; %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizegiven) if (~vnormcontrol) vdelta = y5 - y4; vtau = max (vodeoptions.RelTol * vu', vodeoptions.AbsTol); else vdelta = norm (y5 - y4, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizegiven == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y5'; %# MC2001: the higher order estimation as "local extrapolation" vretvaltime(vcntloop,:) = vtimestamp; if (vhaveoutputselection) vretvalresult(vcntloop,:) = vu(vodeoptions.OutputSel); else vretvalresult(vcntloop,:) = vu; end vcntloop = vcntloop + 1; vcntiter = 0; %# Update DDE values for next history calculation vsaveddetime(end+1) = vtimestamp; vsaveddeinput(end+1,:) = vtheinput'; vsavedderesult(end+1,:) = vu; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) if (vhaverefine) %# Do interpolation for vcnt = 0:vodeoptions.Refine %# Approximation between told and t vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine' + vapproxtime * (vk * vb5); if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end feval (vodeoptions.OutputFcn, (vtimestamp - vstepsize) + vapproxtime, ... vapproxvals, [], vfunarguments{:}); end end vpltret = feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', [], vfunarguments{:}); if (vpltret), vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vu(:), vhmat}, [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizegiven) %# vdelta may be 0 or even negative - could be an iteration problem vdelta = max (vdelta, eps); vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); elseif (vstepsizegiven) if (vcntloop < vtimelength) vstepsize = vslot(1,vcntloop-1) - vslot(1,vcntloop-2); end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for postprocessing vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vtimestamp < vtimestop) if (vunhandledtermination == true) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:HideWarning', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult(vcntloop-1,:), vhmat}, 'done', vfunarguments{:}); end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 6*(vcntcycles-1); %# number of ode evaluations vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode45d'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end %# else nothing will be returned, varargout{1} undefined end %! # We are using a "pseudo-DDE" implementation for all tests that %! # are done for this function. We also define an Events and a %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn. %!function [vyd] = fexp (vt, vy, vz, varargin) %! vyd(1,1) = exp (- vt) - vz(1); %# The DDEs that are %! vyd(2,1) = vy(1) - vz(2); %# used for all examples %!function [vval, vtrm, vdir] = feve (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = zeros (2,1); %# don't stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vval, vtrm, vdir] = fevn (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = ones (2,1); %# stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vmas] = fmas (vt, vy, vz, varargin) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy, vz, varargin) %! vmas = sparse ([1, 0; 0, 1]); %# A dummy sparse matrix %!function [vref] = fref () %# The reference solution %! vref = [0.12194462133618, 0.01652432423938]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = ode45d (1, [0 5], [1; 0], 1, [1; 0]); %!error %# input argument number two %! B = ode45d (@fexp, 1, [1; 0], 1, [1; 0]); %!error %# input argument number three %! B = ode45d (@fexp, [0 5], 1, 1, [1; 0]); %!error %# input argument number four %! B = ode45d (@fexp, [0 5], [1; 0], [1; 1], [1; 0]); %!error %# input argument number five %! B = ode45d (@fexp, [0 5], [1; 0], 1, 1); %!test %# one output argument %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode45d'); %!test %# two output arguments %! [vt, vy] = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 0.5); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 0.5); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! faym = @(vt, vy, vz) [exp(-vt) - vz(1); vy(1) - vz(2)]; %! vsol = ode45d (faym, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# extra input arguments passed trhough %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-7, 'RelTol', 1e-7); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# RelTol and NormControl option %! vopt = odeset ('AbsTol', 1e-7, 'NormControl', 'on'); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], .5e-1); %!test %# NonNegative for second component %! vopt = odeset ('NonNegative', 1); %! vsol = ode45d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2.5, 0.001, 0.237], 0.5); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode45d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode45d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie, [1; 1]); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 0.5); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vie, vxe, vye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 0.5); %! %! %# test for Jacobian option is missing %! %# test for Jacobian (being a sparse matrix) is missing %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.5); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode45d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vt(end), vy(end,:)], [5, fref], 0.5); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidOption'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/ode54.m0000644000000000000000000010645112526637474013225 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode54 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode54 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode54 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (5,4). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example, solve an anonymous implementation of the Van der Pol equation %# %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# %# vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ %# "NormControl", "on", "OutputFcn", @@odeplot); %# ode54 (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} %# ChangeLog: %# 20010703 the function file "ode54.m" was written by Marc Compere %# under the GPL for the use with this software. This function has been %# taken as a base for the following implementation. %# 20060810, Thomas Treichl %# This function was adapted to the new syntax that is used by the %# new OdePkg for Octave. An equivalent function in Matlab does not %# exist. function [varargout] = ode54 (vfun, vslot, vinit, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode54'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 3) print_usage; elseif ~(isa (vfun, 'function_handle') || isa (vfun, 'inline')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (nargin >= 4) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode54'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode54'); vfunarguments = {}; end else %# if (nargin == 3) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options are set in %# vodeoptions, check if an invalid or unused option is set vslot = vslot(:).'; %# Create a row vector vinit = vinit(:).'; %# Create a row vector if (length (vslot) > 2) %# Step size checking vstepsizefixed = true; else vstepsizefixed = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizefixed) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizefixed) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else vodeoptions.AbsTol = vodeoptions.AbsTol(:); %# Create column vector end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')) vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidArgument', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option OutputSave has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputSave)), vodeoptions.OutputSave = 1; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (vodeoptions.Refine > 0), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizefixed) vodeoptions.InitialStep = (vslot(1,2) - vslot(1,1)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidArgument', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizefixed) vodeoptions.MaxStep = abs (vslot(1,2) - vslot(1,1)) / 10; warning ('OdePkg:InvalidArgument', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidArgument', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidArgument', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidArgument', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidArgument', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidArgument', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode54 vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value %# 20110611, reported by Nils Strunk %# Make it possible to solve equations from negativ to zero, %# eg. vres = ode54 (@(t,y) y, [-2 0], 2); vdirection = sign (vtimestop - vtimestamp); %# Direction flag if (~vstepsizefixed) if (sign (vodeoptions.InitialStep) == vdirection) vstepsize = vodeoptions.InitialStep; else %# Fix wrong direction of InitialStep. vstepsize = - vodeoptions.InitialStep; end vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = vslot(1,2) - vslot(1,1); vminstepsize = sign (vstepsize) * eps; end vretvaltime = vtimestamp; %# first timestamp output vretvalresult = vinit; %# first solution output %# Initialize the OutputFcn if (vhaveoutputfunction) if (vhaveoutputselection) vretout = vretvalresult(vodeoptions.OutputSel); else vretout = vretvalresult; end feval (vodeoptions.OutputFcn, vslot.', ... vretout.', 'init', vfunarguments{:}); end %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vretvalresult.', 'init', vfunarguments{:}); end vpow = 1/5; %# 20071016, reported by Luis Randez va = [0, 0, 0, 0, 0, 0; %# The Dormand-Prince 5(4) coefficients 1/5, 0, 0, 0, 0, 0; %# Coefficients proved on 20060827 3/40, 9/40, 0, 0, 0, 0; %# See p.91 in Ascher & Petzold 44/45, -56/15, 32/9, 0, 0, 0; 19372/6561, -25360/2187, 64448/6561, -212/729, 0, 0; 9017/3168, -355/33, 46732/5247, 49/176, -5103/18656, 0; 35/384, 0, 500/1113, 125/192, -2187/6784, 11/84]; %# 4th and 5th order b-coefficients vb4 = [5179/57600; 0; 7571/16695; 393/640; -92097/339200; 187/2100; 1/40]; vb5 = [35/384; 0; 500/1113; 125/192; -2187/6784; 11/84; 0]; vc = sum (va, 2); %# The solver main loop - stop if the endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu.' * zeros(1,7); vcntiter = 0; vunhandledtermination = true; vcntsave = 2; %# FSAL optimizations: sent by Bruce Minaker 20110326, find the slope k1 %# (k1 is copied from last k7, so set k7) for first time step only if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vtimestamp, vinit.', vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vtimestamp, vfunarguments{:}); end vk(:,7) = vmass \ feval ... (vfun, vtimestamp, vinit.', vfunarguments{:}); else vk(:,7) = feval ... (vfun, vtimestamp, vinit.', vfunarguments{:}); end while ((vdirection * (vtimestamp) < vdirection * (vtimestop)) && ... (vdirection * (vstepsize) >= vdirection * (vminstepsize))) %# Hit the endpoint of the time slot exactely if (vdirection * (vtimestamp + vstepsize) > vdirection * vtimestop) %# vstepsize = vtimestop - vdirection * vtimestamp; %# 20110611, reported by Nils Strunk %# The endpoint of the time slot must be hit exactly, %# eg. vsol = ode54 (@(t,y) y, [0 -1], 1); vstepsize = vdirection * abs (abs (vtimestop) - abs (vtimestamp)); end %# Estimate the seven results when using this solver (FSAL) %# skip the first result as we already know it from last time step vk(:,1)=vk(:,7); for j = 2:7 %# Start at two instead of one (FSAL) vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu.' + vstepsize * vk(:,1:j-1) * va(j,1:j-1).'; if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); end end %# Compute the 4th and the 5th order estimation y4 = vu.' + vstepsize * (vk * vb4); y5 = vtheinput; %# y5 = vu.' + vstepsize * (vk * vb5); vb5 is the same as va(6,:), %# means that we already know y5 from the first six vk's (FSAL) if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y4(vodeoptions.NonNegative) = abs (y4(vodeoptions.NonNegative)); y5(vodeoptions.NonNegative) = abs (y5(vodeoptions.NonNegative)); end if (vhaveoutputfunction && vhaverefine) vSaveVUForRefine = vu; end %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizefixed) if (~vnormcontrol) vdelta = abs (y5 - y4); vtau = max (vodeoptions.RelTol * abs (vu.'), vodeoptions.AbsTol); else vdelta = norm (y5 - y4, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu.', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizefixed == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y5.'; %# MC2001: the higher order estimation as "local extrapolation" %# Save the solution every vodeoptions.OutputSave steps if (mod (vcntloop-1,vodeoptions.OutputSave) == 0) if (vhaveoutputselection) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu(vodeoptions.OutputSel); else vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; end vcntsave = vcntsave + 1; end vcntloop = vcntloop + 1; vcntiter = 0; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) for vcnt = 0:vodeoptions.Refine %# Approximation between told and t if (vhaverefine) %# Do interpolation vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine.' + vapproxtime * (vk * vb5); vapproxtime = (vtimestamp - vstepsize) + vapproxtime; else vapproxvals = vu.'; vapproxtime = vtimestamp; end if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end vpltret = feval (vodeoptions.OutputFcn, vapproxtime, ... vapproxvals, [], vfunarguments{:}); if vpltret %# Leave refinement loop break; end end if (vpltret) %# Leave main loop vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu(:), [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end else vk(:,7) = vk(:,1); %# If we're here, then we've overwritten the k7 that we %# need to copy to k1 to redo the step Since we copy k7 %# into k1, we'll put the k1 back in k7 for now, until it's %# copied back again (FSAL) end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizefixed) %# 2008-20120425, reported by Marco Caliari %# vdelta cannot be negative (because of the absolute value that %# has been introduced) but it could be 0, then replace the zeros %# with the maximum value of vdelta vdelta(find (vdelta == 0)) = max (vdelta); %# It could happen that max (vdelta) == 0 (ie. that the original %# vdelta was 0), in that case we double the previous vstepsize vdelta(find (vdelta == 0)) = max (vtau) .* (0.4 ^ (1 / vpow)); if (vdirection == 1) vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); else vstepsize = max (- vodeoptions.MaxStep, ... max (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); end else %# if (vstepsizefixed) if (vcntloop <= vtimelength) vstepsize = vslot(vcntloop) - vslot(vcntloop-1); else %# Get out of the main integration loop break; end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for cost statistics vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vdirection * vtimestamp < vdirection * vtimestop) if (vunhandledtermination == true) error ('OdePkg:InvalidArgument', ... ['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:InvalidArgument', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vu.', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu.', 'done', vfunarguments{:}); end %# Save the last step, if not already saved if (mod (vcntloop-2,vodeoptions.OutputSave) ~= 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 6*(vcntcycles-1)+1; %# 7 stages & one first step (FSAL) vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d\n', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d\n', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d\n', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode54'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end end end %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference sol %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidArgument'); %! B = ode54 (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = ode54 (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = ode54 (@flor, [0 25], 1); %!test %# one output argument %! vsol = ode54 (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode54'); %!test %# two output arguments %! [vt, vy] = ode54 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode54 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = ode54 (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = ode54 (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode54 (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %!test %# Solve vdp in fixed step sizes %! vsol = ode54 (@fpol, [0:0.1:2], [2 0]); %! assert (vsol.x(:), [0:0.1:2]'); %! assert (vsol.y(end,:), fref, 1e-3); %!test %# Solve in backward direction starting at t=0 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode54 (@fpol, [0 -2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve in backward direction starting at t=2 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode54 (@fpol, [2 -2], fref); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve another anonymous function in backward direction %! vref = [-1, 0.367879437558975]; %! vsol = ode54 (@(t,y) y, [0 -1], 1); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# Solve another anonymous function below zero %! vref = [0, 14.77810590694212]; %! vsol = ode54 (@(t,y) y, [-2 0], 2); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-4); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, 2, 0], 1e-1); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %!test %# Details of OutputSave can't be tested %! vopt = odeset ('OutputSave', 1, 'OutputSel', 1); %! vsla = ode54 (@fpol, [0 2], [2 0], vopt); %! vopt = odeset ('OutputSave', 2); %! vslb = ode54 (@fpol, [0 2], [2 0], vopt); %! assert (length (vsla.x) > length (vslb.x)) %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode54 (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode54 (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-3); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve, 'InitialStep', 1e-6); %! vsol = ode54 (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! vopt = odeset ('Events', @fevn, 'InitialStep', 1e-6); %! vsol = ode54 (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'InitialStep', 1e-6); %! [vt, vy, vxe, vye, vie] = ode54 (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode54 (@fpol, [0 2], [2 0], vopt); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidArgument'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/ode54d.m0000644000000000000000000010434312526637474013367 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode54d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode54d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode54d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (2,3). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# In other words, this function will solve a problem of the form %# @example %# dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) %# y(slot(1)) = init %# y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} %# @end example %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example: %# @itemize @minus %# @item %# the following code solves an anonymous implementation of a chaotic behavior %# %# @example %# fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; %# %# vopt = odeset ("NormControl", "on", "RelTol", 1e-3); %# vsol = ode54d (fcao, [0, 100], 0.5, 2, 0.5, vopt); %# %# vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); %# plot (vsol.y, vlag); legend ("fcao (t,y,z)"); %# @end example %# %# @item %# to solve the following problem with two delayed state variables %# %# @example %# d y1(t)/dt = -y1(t) %# d y2(t)/dt = -y2(t) + y1(t-5) %# d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) %# @end example %# %# one might do the following %# %# @example %# function f = fun (t, y, yd) %# f(1) = -y(1); %% y1' = -y1(t) %# f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) %# f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) %# endfunction %# T = [0,20] %# res = ode54d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); %# @end example %# %# @end itemize %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = ode54d (vfun, vslot, vinit, vlags, vhist, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode54d'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 5) print_usage; elseif (~isa (vfun, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (~isvector (vlags) || ~isnumeric (vlags)) error ('OdePkg:InvalidArgument', ... 'Fourth input argument must be a valid numerical value'); elseif ~(isnumeric (vhist) || isa (vhist, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'Fifth input argument must either be numeric or a function handle'); elseif (nargin >= 6) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode54d'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode54d'); vfunarguments = {}; end else %# if (nargin == 5) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options have been set in %# vodeoptions. Check if an invalid or unused option has been set and %# print warnings. vslot = vslot(:)'; %# Create a row vector vinit = vinit(:)'; %# Create a row vector vlags = vlags(:)'; %# Create a row vector %# Check if the user has given fixed points of time if (length (vslot) > 2), vstepsizegiven = true; %# Step size checking else vstepsizegiven = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizegiven) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); %# This implementation has been added to odepkg_structure_check.m %# elseif (~isscalar (vodeoptions.RelTol) && ~vstepsizegiven) %# error ('OdePkg:InvalidOption', ... %# 'Option "RelTol" must be set to a scalar value for this solver'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizegiven) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else %# create column vector vodeoptions.AbsTol = vodeoptions.AbsTol(:); end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')), vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidOption', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (isequal (vodeoptions.Refine, vodetemp.Refine)), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizegiven) vodeoptions.InitialStep = abs (vslot(1,1) - vslot(1,2)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizegiven) vodeoptions.MaxStep = abs (vslot(1,1) - vslot(1,length (vslot))) / 10; %# vodeoptions.MaxStep = vodeoptions.MaxStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidOption', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidOption', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidOption', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidOption', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidOption', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidOption', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidOption', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidOption', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode54d vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value if (~vstepsizegiven) vstepsize = vodeoptions.InitialStep; vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = abs (vslot(1,1) - vslot(1,2)); vminstepsize = eps; %# vslot(1,2) - vslot(1,1) - eps; end vretvaltime = vtimestamp; %# first timestamp output if (vhaveoutputselection) %# first solution output vretvalresult = vinit(vodeoptions.OutputSel); else vretvalresult = vinit; end %# Initialize the OutputFcn if (vhaveoutputfunction) feval (vodeoptions.OutputFcn, vslot', ... vretvalresult', 'init', vfunarguments{:}); end %# Initialize the History if (isnumeric (vhist)) vhmat = vhist; vhavehistnumeric = true; else %# it must be a function handle for vcnt = 1:length (vlags); vhmat(:,vcnt) = feval (vhist, (vslot(1)-vlags(vcnt)), vfunarguments{:}); end vhavehistnumeric = false; end %# Initialize DDE variables for history calculation vsaveddetime = [vtimestamp - vlags, vtimestamp]'; vsaveddeinput = [vhmat, vinit']'; vsavedderesult = [vhmat, vinit']'; %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult', vhmat}, 'init', vfunarguments{:}); end vpow = 1/5; %# 20071016, reported by Luis Randez va = [0, 0, 0, 0, 0, 0; %# The Dormand-Prince 5(4) coefficients 1/5, 0, 0, 0, 0, 0; %# Coefficients proved on 20060827 3/40, 9/40, 0, 0, 0, 0; %# See p.91 in Ascher & Petzold 44/45, -56/15, 32/9, 0, 0, 0; 19372/6561, -25360/2187, 64448/6561, -212/729, 0, 0; 9017/3168, -355/33, 46732/5247, 49/176, -5103/18656, 0; 35/384, 0, 500/1113, 125/192, -2187/6784, 11/84]; %# 4th and 5th order b-coefficients vb4 = [35/384; 0; 500/1113; 125/192; -2187/6784; 11/84; 0]; vb5 = [5179/57600; 0; 7571/16695; 393/640; -92097/339200; 187/2100; 1/40]; vc = sum (va, 2); %# The solver main loop - stop if the endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu' * zeros(1,7); vcntiter = 0; vunhandledtermination = true; while ((vtimestamp < vtimestop && vstepsize >= vminstepsize)) %# Hit the endpoint of the time slot exactely if ((vtimestamp + vstepsize) > vtimestop) vstepsize = vtimestop - vtimestamp; end %# Estimate the seven results when using this solver for j = 1:7 vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu' + vstepsize * vk(:,1:j-1) * va(j,1:j-1)'; %# Claculate the history values (or get them from an external %# function) that are needed for the next step of solving if (vhavehistnumeric) for vcnt = 1:length (vlags) %# Direct implementation of a 'quadrature cubic Hermite interpolation' %# found at the Faculty for Mathematics of the University of Stuttgart %# http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage1269 vnumb = find (vthetime - vlags(vcnt) >= vsaveddetime); velem = min (vnumb(end), length (vsaveddetime) - 1); vstep = vsaveddetime(velem+1) - vsaveddetime(velem); vdiff = (vthetime - vlags(vcnt) - vsaveddetime(velem)) / vstep; vsubs = 1 - vdiff; %# Calculation of the coefficients for the interpolation algorithm vua = (1 + 2 * vdiff) * vsubs^2; vub = (3 - 2 * vdiff) * vdiff^2; vva = vstep * vdiff * vsubs^2; vvb = -vstep * vsubs * vdiff^2; vhmat(:,vcnt) = vua * vsaveddeinput(velem,:)' + ... vub * vsaveddeinput(velem+1,:)' + ... vva * vsavedderesult(velem,:)' + ... vvb * vsavedderesult(velem+1,:)'; end else %# the history must be a function handle for vcnt = 1:length (vlags) vhmat(:,vcnt) = feval ... (vhist, vthetime - vlags(vcnt), vfunarguments{:}); end end if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); end end %# Compute the 4th and the 5th order estimation y4 = vu' + vstepsize * (vk * vb4); y5 = vu' + vstepsize * (vk * vb5); if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y4(vodeoptions.NonNegative) = abs (y4(vodeoptions.NonNegative)); y5(vodeoptions.NonNegative) = abs (y5(vodeoptions.NonNegative)); end vSaveVUForRefine = vu; %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizegiven) if (~vnormcontrol) vdelta = y5 - y4; vtau = max (vodeoptions.RelTol * vu', vodeoptions.AbsTol); else vdelta = norm (y5 - y4, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizegiven == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y5'; %# MC2001: the higher order estimation as "local extrapolation" vretvaltime(vcntloop,:) = vtimestamp; if (vhaveoutputselection) vretvalresult(vcntloop,:) = vu(vodeoptions.OutputSel); else vretvalresult(vcntloop,:) = vu; end vcntloop = vcntloop + 1; vcntiter = 0; %# Update DDE values for next history calculation vsaveddetime(end+1) = vtimestamp; vsaveddeinput(end+1,:) = vtheinput'; vsavedderesult(end+1,:) = vu; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) if (vhaverefine) %# Do interpolation for vcnt = 0:vodeoptions.Refine %# Approximation between told and t vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine' + vapproxtime * (vk * vb5); if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end feval (vodeoptions.OutputFcn, (vtimestamp - vstepsize) + vapproxtime, ... vapproxvals, [], vfunarguments{:}); end end vpltret = feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', [], vfunarguments{:}); if (vpltret), vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vu(:), vhmat}, [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizegiven) %# vdelta may be 0 or even negative - could be an iteration problem vdelta = max (vdelta, eps); vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); elseif (vstepsizegiven) if (vcntloop < vtimelength) vstepsize = vslot(1,vcntloop-1) - vslot(1,vcntloop-2); end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for postprocessing vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vtimestamp < vtimestop) if (vunhandledtermination == true) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:HideWarning', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult(vcntloop-1,:), vhmat}, 'done', vfunarguments{:}); end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 7*(vcntcycles-1); %# number of ode evaluations vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode54d'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end %# else nothing will be returned, varargout{1} undefined end %! # We are using a "pseudo-DDE" implementation for all tests that %! # are done for this function. We also define an Events and a %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn. %!function [vyd] = fexp (vt, vy, vz, varargin) %! vyd(1,1) = exp (- vt) - vz(1); %# The DDEs that are %! vyd(2,1) = vy(1) - vz(2); %# used for all examples %!function [vval, vtrm, vdir] = feve (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = zeros (2,1); %# don't stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vval, vtrm, vdir] = fevn (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = ones (2,1); %# stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vmas] = fmas (vt, vy, vz, varargin) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy, vz, varargin) %! vmas = sparse ([1, 0; 0, 1]); %# A dummy sparse matrix %!function [vref] = fref () %# The reference solution %! vref = [0.12194462133618, 0.01652432423938]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = ode54d (1, [0 5], [1; 0], 1, [1; 0]); %!error %# input argument number two %! B = ode54d (@fexp, 1, [1; 0], 1, [1; 0]); %!error %# input argument number three %! B = ode54d (@fexp, [0 5], 1, 1, [1; 0]); %!error %# input argument number four %! B = ode54d (@fexp, [0 5], [1; 0], [1; 1], [1; 0]); %!error %# input argument number five %! B = ode54d (@fexp, [0 5], [1; 0], 1, 1); %!test %# one output argument %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode54d'); %!test %# two output arguments %! [vt, vy] = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 1e-1); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 1e-1); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! faym = @(vt, vy, vz) [exp(-vt) - vz(1); vy(1) - vz(2)]; %! vsol = ode54d (faym, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# extra input arguments passed trhough %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-7, 'RelTol', 1e-7); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# RelTol and NormControl option %! vopt = odeset ('AbsTol', 1e-7, 'NormControl', 'on'); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], .5e-1); %!test %# NonNegative for second component %! vopt = odeset ('NonNegative', 1); %! vsol = ode54d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2.5, 0.001, 0.237], 1e-1); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode54d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode54d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie, [1; 1]); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vie, vxe, vye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 1e-1); %! %! %# test for Jacobian option is missing %! %# test for Jacobian (being a sparse matrix) is missing %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 1e-1); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode54d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vt(end), vy(end,:)], [5, fref], 0.5); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidOption'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/ode78.m0000644000000000000000000010671412526637474013235 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode78 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode78 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode78 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (7,8). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example, solve an anonymous implementation of the Van der Pol equation %# %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# %# vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ %# "NormControl", "on", "OutputFcn", @@odeplot); %# ode78 (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} %# ChangeLog: %# 20010519 the function file "ode78.m" was written by Marc Compere %# under the GPL for the use with this software. This function has been %# taken as a base for the following implementation. %# 20060810, Thomas Treichl %# This function was adapted to the new syntax that is used by the %# new OdePkg for Octave. An equivalent function in Matlab does not %# exist. function [varargout] = ode78 (vfun, vslot, vinit, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode78'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 3) print_usage; elseif ~(isa (vfun, 'function_handle') || isa (vfun, 'inline')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (nargin >= 4) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode78'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode78'); vfunarguments = {}; end else %# if (nargin == 3) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options are set in %# vodeoptions, check if an invalid or unused option is set vslot = vslot(:).'; %# Create a row vector vinit = vinit(:).'; %# Create a row vector if (length (vslot) > 2) %# Step size checking vstepsizefixed = true; else vstepsizefixed = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizefixed) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizefixed) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else vodeoptions.AbsTol = vodeoptions.AbsTol(:); %# Create column vector end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')) vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidArgument', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option OutputSave has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputSave)), vodeoptions.OutputSave = 1; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (vodeoptions.Refine > 0), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizefixed) vodeoptions.InitialStep = (vslot(1,2) - vslot(1,1)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidArgument', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizefixed) vodeoptions.MaxStep = abs (vslot(1,2) - vslot(1,1)) / 10; warning ('OdePkg:InvalidArgument', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidArgument', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidArgument', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidArgument', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidArgument', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidArgument', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode78 vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value %# 20110611, reported by Nils Strunk %# Make it possible to solve equations from negativ to zero, %# eg. vres = ode78 (@(t,y) y, [-2 0], 2); vdirection = sign (vtimestop - vtimestamp); %# Direction flag if (~vstepsizefixed) if (sign (vodeoptions.InitialStep) == vdirection) vstepsize = vodeoptions.InitialStep; else %# Fix wrong direction of InitialStep. vstepsize = - vodeoptions.InitialStep; end vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = vslot(1,2) - vslot(1,1); vminstepsize = sign (vstepsize) * eps; end vretvaltime = vtimestamp; %# first timestamp output vretvalresult = vinit; %# first solution output %# Initialize the OutputFcn if (vhaveoutputfunction) if (vhaveoutputselection) vretout = vretvalresult(vodeoptions.OutputSel); else vretout = vretvalresult; end feval (vodeoptions.OutputFcn, vslot.', ... vretout.', 'init', vfunarguments{:}); end %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vretvalresult.', 'init', vfunarguments{:}); end vpow = 1/8; %# MC2001: see p.91 in Ascher & Petzold va = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; %# The 7(8) coefficients 1/18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; %# Coefficients proved, tt 20060827 1/48, 1/16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 1/32, 0, 3/32, 0, 0, 0, 0, 0, 0, 0, 0, 0; 5/16, 0, -75/64, 75/64, 0, 0, 0, 0, 0, 0, 0, 0; 3/80, 0, 0, 3/16, 3/20, 0, 0, 0, 0, 0, 0, 0; 29443841/614563906, 0, 0, 77736538/692538347, -28693883/1125000000, ... 23124283/1800000000, 0, 0, 0, 0, 0, 0; 16016141/946692911, 0, 0, 61564180/158732637, 22789713/633445777, ... 545815736/2771057229, -180193667/1043307555, 0, 0, 0, 0, 0; 39632708/573591083, 0, 0, -433636366/683701615, -421739975/2616292301, ... 100302831/723423059, 790204164/839813087, 800635310/3783071287, 0, 0, 0, 0; 246121993/1340847787, 0, 0, -37695042795/15268766246, -309121744/1061227803, ... -12992083/490766935, 6005943493/2108947869, 393006217/1396673457, ... 123872331/1001029789, 0, 0, 0; -1028468189/846180014, 0, 0, 8478235783/508512852, 1311729495/1432422823, ... -10304129995/1701304382, -48777925059/3047939560, 15336726248/1032824649, ... -45442868181/3398467696, 3065993473/597172653, 0, 0; 185892177/718116043, 0, 0, -3185094517/667107341, -477755414/1098053517, ... -703635378/230739211, 5731566787/1027545527, 5232866602/850066563, ... -4093664535/808688257, 3962137247/1805957418, 65686358/487910083, 0; 403863854/491063109, 0, 0, -5068492393/434740067, -411421997/543043805, ... 652783627/914296604, 11173962825/925320556, -13158990841/6184727034, ... 3936647629/1978049680, -160528059/685178525, 248638103/1413531060, 0]; vb7 = [13451932/455176623; 0; 0; 0; 0; -808719846/976000145; ... 1757004468/5645159321; 656045339/265891186; -3867574721/1518517206; ... 465885868/322736535; 53011238/667516719; 2/45; 0]; vb8 = [14005451/335480064; 0; 0; 0; 0; -59238493/1068277825; 181606767/758867731; ... 561292985/797845732; -1041891430/1371343529; 760417239/1151165299; ... 118820643/751138087; -528747749/2220607170; 1/4]; vc = sum (va, 2); %# The solver main loop - stop if the endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu' * zeros(1,13); vcntiter = 0; vunhandledtermination = true; vcntsave = 2; while ((vdirection * (vtimestamp) < vdirection * (vtimestop)) && ... (vdirection * (vstepsize) >= vdirection * (vminstepsize))) %# Hit the endpoint of the time slot exactely if (vdirection * (vtimestamp + vstepsize) > vdirection * vtimestop) %# vstepsize = vtimestop - vdirection * vtimestamp; %# 20110611, reported by Nils Strunk %# The endpoint of the time slot must be hit exactly, %# eg. vsol = ode78 (@(t,y) y, [0 -1], 1); vstepsize = vdirection * abs (abs (vtimestop) - abs (vtimestamp)); end %# Estimate the thirteen results when using this solver for j = 1:13 vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu.' + vstepsize * vk(:,1:j-1) * va(j,1:j-1).'; if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vfunarguments{:}); end end %# Compute the 7th and the 8th order estimation y7 = vu.' + vstepsize * (vk * vb7); y8 = vu.' + vstepsize * (vk * vb8); if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y7(vodeoptions.NonNegative) = abs (y7(vodeoptions.NonNegative)); y8(vodeoptions.NonNegative) = abs (y8(vodeoptions.NonNegative)); end if (vhaveoutputfunction && vhaverefine) vSaveVUForRefine = vu; end %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizefixed) if (~vnormcontrol) vdelta = abs (y8 - y7); vtau = max (vodeoptions.RelTol * abs (vu.'), vodeoptions.AbsTol); else vdelta = norm (y8 - y7, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu.', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizefixed == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y8.'; %# MC2001: the higher order estimation as "local extrapolation" %# Save the solution every vodeoptions.OutputSave steps if (mod (vcntloop-1,vodeoptions.OutputSave) == 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; vcntsave = vcntsave + 1; end vcntloop = vcntloop + 1; vcntiter = 0; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) for vcnt = 0:vodeoptions.Refine %# Approximation between told and t if (vhaverefine) %# Do interpolation vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine.' + vapproxtime * (vk * vb8); vapproxtime = (vtimestamp - vstepsize) + vapproxtime; else vapproxvals = vu.'; vapproxtime = vtimestamp; end if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end vpltret = feval (vodeoptions.OutputFcn, vapproxtime, ... vapproxvals, [], vfunarguments{:}); if vpltret %# Leave refinement loop break; end end if (vpltret) %# Leave main loop vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu(:), [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizefixed) %# 20080425, reported by Marco Caliari %# vdelta cannot be negative (because of the absolute value that %# has been introduced) but it could be 0, then replace the zeros %# with the maximum value of vdelta vdelta(find (vdelta == 0)) = max (vdelta); %# It could happen that max (vdelta) == 0 (ie. that the original %# vdelta was 0), in that case we double the previous vstepsize vdelta(find (vdelta == 0)) = max (vtau) .* (0.4 ^ (1 / vpow)); if (vdirection == 1) vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); else vstepsize = max (- vodeoptions.MaxStep, ... max (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); end else %# if (vstepsizefixed) if (vcntloop <= vtimelength) vstepsize = vslot(vcntloop) - vslot(vcntloop-1); else %# Get out of the main integration loop break; end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for cost statistics vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vdirection * vtimestamp < vdirection * vtimestop) if (vunhandledtermination == true) error ('OdePkg:InvalidArgument', ... ['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:InvalidArgument', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vu.', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu.', 'done', vfunarguments{:}); end %# Save the last step, if not already saved if (mod (vcntloop-2,vodeoptions.OutputSave) ~= 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 13*(vcntcycles-1); %# number of ode evaluations vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d\n', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d\n', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d\n', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode78'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end end end %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference sol %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidArgument'); %! B = ode78 (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = ode78 (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = ode78 (@flor, [0 25], 1); %!test %# one output argument %! vsol = ode78 (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode78'); %!test %# two output arguments %! [vt, vy] = ode78 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode78 (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = ode78 (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed through %! vsol = ode78 (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode78 (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %!test %# Solve vdp in fixed step sizes %! vsol = ode78 (@fpol, [0:0.1:2], [2 0]); %! assert (vsol.x(:), [0:0.1:2]'); %! assert (vsol.y(end,:), fref, 1e-3); %!test %# Solve in backward direction starting at t=0 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode78 (@fpol, [0 -2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve in backward direction starting at t=2 %! vref = [-1.205364552835178, 0.951542399860817]; %! vsol = ode78 (@fpol, [2 -2], fref); %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3); %!test %# Solve another anonymous function in backward direction %! vref = [-1, 0.367879437558975]; %! vsol = ode78 (@(t,y) y, [0 -1], 1); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# Solve another anonymous function below zero %! vref = [0, 14.77810590694212]; %! vsol = ode78 (@(t,y) y, [-2 0], 2); %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, 2, 0], 3e-1); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %!test %# Details of OutputSave can't be tested %! vopt = odeset ('OutputSave', 1, 'OutputSel', 1); %! vsla = ode78 (@fpol, [0 2], [2 0], vopt); %! vopt = odeset ('OutputSave', 2); %! vslb = ode78 (@fpol, [0 2], [2 0], vopt); %! assert (length (vsla.x) > length (vslb.x)) %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode78 (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode78 (@fpol, [0 0.2], [2 0], vopt); %! %# assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-3); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve, 'InitialStep', 1e-4); %! vsol = ode78 (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! vopt = odeset ('Events', @fevn, 'InitialStep', 1e-4); %! vsol = ode78 (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 2e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'InitialStep', 1e-4); %! [vt, vy, vxe, vye, vie] = ode78 (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 2e-1); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode78 (@fpol, [0 2], [2 0], vopt); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidArgument'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/ode78d.m0000644000000000000000000010747512526637474013406 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} ode78d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} ode78d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode78d (@var{@@fun}, @var{slot}, @var{init}, @var{lags}, @var{hist}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of non--stiff delay differential equations (non--stiff DDEs) with a modified version of the well known explicit Runge--Kutta method of order (7,8). %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of DDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{lags} is a double vector that describes the lags of time, @var{hist} is a double matrix and describes the history of the DDEs, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# In other words, this function will solve a problem of the form %# @example %# dy/dt = fun (t, y(t), y(t-lags(1), y(t-lags(2), @dots{}))) %# y(slot(1)) = init %# y(slot(1)-lags(1)) = hist(1), y(slot(1)-lags(2)) = hist(2), @dots{} %# @end example %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of DDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example: %# @itemize @minus %# @item %# the following code solves an anonymous implementation of a chaotic behavior %# %# @example %# fcao = @@(vt, vy, vz) [2 * vz / (1 + vz^9.65) - vy]; %# %# vopt = odeset ("NormControl", "on", "RelTol", 1e-3); %# vsol = ode78d (fcao, [0, 100], 0.5, 2, 0.5, vopt); %# %# vlag = interp1 (vsol.x, vsol.y, vsol.x - 2); %# plot (vsol.y, vlag); legend ("fcao (t,y,z)"); %# @end example %# %# @item %# to solve the following problem with two delayed state variables %# %# @example %# d y1(t)/dt = -y1(t) %# d y2(t)/dt = -y2(t) + y1(t-5) %# d y3(t)/dt = -y3(t) + y2(t-10)*y1(t-10) %# @end example %# %# one might do the following %# %# @example %# function f = fun (t, y, yd) %# f(1) = -y(1); %% y1' = -y1(t) %# f(2) = -y(2) + yd(1,1); %% y2' = -y2(t) + y1(t-lags(1)) %# f(3) = -y(3) + yd(2,2)*yd(1,2); %% y3' = -y3(t) + y2(t-lags(2))*y1(t-lags(2)) %# endfunction %# T = [0,20] %# res = ode78d (@@fun, T, [1;1;1], [5, 10], ones (3,2)); %# @end example %# %# @end itemize %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = ode78d (vfun, vslot, vinit, vlags, vhist, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('ode78d'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 5) print_usage; elseif (~isa (vfun, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (~isvector (vlags) || ~isnumeric (vlags)) error ('OdePkg:InvalidArgument', ... 'Fourth input argument must be a valid numerical value'); elseif ~(isnumeric (vhist) || isa (vhist, 'function_handle')) error ('OdePkg:InvalidArgument', ... 'Fifth input argument must either be numeric or a function handle'); elseif (nargin >= 6) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'ode78d'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'ode78d'); vfunarguments = {}; end else %# if (nargin == 5) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options have been set in %# vodeoptions. Check if an invalid or unused option has been set and %# print warnings. vslot = vslot(:)'; %# Create a row vector vinit = vinit(:)'; %# Create a row vector vlags = vlags(:)'; %# Create a row vector %# Check if the user has given fixed points of time if (length (vslot) > 2), vstepsizegiven = true; %# Step size checking else vstepsizegiven = false; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizegiven) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); %# This implementation has been added to odepkg_structure_check.m %# elseif (~isscalar (vodeoptions.RelTol) && ~vstepsizegiven) %# error ('OdePkg:InvalidOption', ... %# 'Option "RelTol" must be set to a scalar value for this solver'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizegiven) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizegiven) warning ('OdePkg:InvalidOption', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else %# create column vector vodeoptions.AbsTol = vodeoptions.AbsTol(:); end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')), vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option NonNegative has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.NonNegative)) if (isempty (vodeoptions.Mass)), vhavenonnegative = true; else vhavenonnegative = false; warning ('OdePkg:InvalidOption', ... 'Option "NonNegative" will be ignored if mass matrix is set'); end else vhavenonnegative = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option Refine has been finished. This option %# can be set by the user to another value than default value. if (isequal (vodeoptions.Refine, vodetemp.Refine)), vhaverefine = true; else vhaverefine = false; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizegiven) vodeoptions.InitialStep = abs (vslot(1,1) - vslot(1,2)) / 10; vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizegiven) vodeoptions.MaxStep = abs (vslot(1,1) - vslot(1,length (vslot))) / 10; %# vodeoptions.MaxStep = vodeoptions.MaxStep / 10^vodeoptions.Refine; warning ('OdePkg:InvalidOption', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored %# by this solver because this solver uses an explicit Runge-Kutta %# method and therefore no Jacobian calculation is necessary if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) warning ('OdePkg:InvalidOption', ... 'Option "Jacobian" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidOption', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidOption', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxNewtonIterations,... vodetemp.MaxNewtonIterations)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" will be ignored by this solver'); end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidOption', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidOption', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidOption', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidOption', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidOption', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver ode78d vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value if (~vstepsizegiven) vstepsize = vodeoptions.InitialStep; vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = abs (vslot(1,1) - vslot(1,2)); vminstepsize = eps; %# vslot(1,2) - vslot(1,1) - eps; end vretvaltime = vtimestamp; %# first timestamp output if (vhaveoutputselection) %# first solution output vretvalresult = vinit(vodeoptions.OutputSel); else vretvalresult = vinit; end %# Initialize the OutputFcn if (vhaveoutputfunction) feval (vodeoptions.OutputFcn, vslot', ... vretvalresult', 'init', vfunarguments{:}); end %# Initialize the History if (isnumeric (vhist)) vhmat = vhist; vhavehistnumeric = true; else %# it must be a function handle for vcnt = 1:length (vlags); vhmat(:,vcnt) = feval (vhist, (vslot(1)-vlags(vcnt)), vfunarguments{:}); end vhavehistnumeric = false; end %# Initialize DDE variables for history calculation vsaveddetime = [vtimestamp - vlags, vtimestamp]'; vsaveddeinput = [vhmat, vinit']'; vsavedderesult = [vhmat, vinit']'; %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult', vhmat}, 'init', vfunarguments{:}); end vpow = 1/8; %# MC2001: see p.91 in Ascher & Petzold va = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; %# The 7(8) coefficients 1/18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; %# Coefficients proved, tt 20060827 1/48, 1/16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 1/32, 0, 3/32, 0, 0, 0, 0, 0, 0, 0, 0, 0; 5/16, 0, -75/64, 75/64, 0, 0, 0, 0, 0, 0, 0, 0; 3/80, 0, 0, 3/16, 3/20, 0, 0, 0, 0, 0, 0, 0; 29443841/614563906, 0, 0, 77736538/692538347, -28693883/1125000000, ... 23124283/1800000000, 0, 0, 0, 0, 0, 0; 16016141/946692911, 0, 0, 61564180/158732637, 22789713/633445777, ... 545815736/2771057229, -180193667/1043307555, 0, 0, 0, 0, 0; 39632708/573591083, 0, 0, -433636366/683701615, -421739975/2616292301, ... 100302831/723423059, 790204164/839813087, 800635310/3783071287, 0, 0, 0, 0; 246121993/1340847787, 0, 0, -37695042795/15268766246, -309121744/1061227803, ... -12992083/490766935, 6005943493/2108947869, 393006217/1396673457, ... 123872331/1001029789, 0, 0, 0; -1028468189/846180014, 0, 0, 8478235783/508512852, 1311729495/1432422823, ... -10304129995/1701304382, -48777925059/3047939560, 15336726248/1032824649, ... -45442868181/3398467696, 3065993473/597172653, 0, 0; 185892177/718116043, 0, 0, -3185094517/667107341, -477755414/1098053517, ... -703635378/230739211, 5731566787/1027545527, 5232866602/850066563, ... -4093664535/808688257, 3962137247/1805957418, 65686358/487910083, 0; 403863854/491063109, 0, 0, -5068492393/434740067, -411421997/543043805, ... 652783627/914296604, 11173962825/925320556, -13158990841/6184727034, ... 3936647629/1978049680, -160528059/685178525, 248638103/1413531060, 0]; vb7 = [13451932/455176623; 0; 0; 0; 0; -808719846/976000145; ... 1757004468/5645159321; 656045339/265891186; -3867574721/1518517206; ... 465885868/322736535; 53011238/667516719; 2/45; 0]; vb8 = [14005451/335480064; 0; 0; 0; 0; -59238493/1068277825; 181606767/758867731; ... 561292985/797845732; -1041891430/1371343529; 760417239/1151165299; ... 118820643/751138087; -528747749/2220607170; 1/4]; vc = sum (va, 2); %# The solver main loop - stop if the endpoint has been reached vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu' * zeros(1,13); vcntiter = 0; vunhandledtermination = true; while ((vtimestamp < vtimestop && vstepsize >= vminstepsize)) %# Hit the endpoint of the time slot exactely if ((vtimestamp + vstepsize) > vtimestop) vstepsize = vtimestop - vtimestamp; end %# Estimate the thirteen results when using this solver for j = 1:13 vthetime = vtimestamp + vc(j,1) * vstepsize; vtheinput = vu' + vstepsize * vk(:,1:j-1) * va(j,1:j-1)'; %# Claculate the history values (or get them from an external %# function) that are needed for the next step of solving if (vhavehistnumeric) for vcnt = 1:length (vlags) %# Direct implementation of a 'quadrature cubic Hermite interpolation' %# found at the Faculty for Mathematics of the University of Stuttgart %# http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage1269 vnumb = find (vthetime - vlags(vcnt) >= vsaveddetime); velem = min (vnumb(end), length (vsaveddetime) - 1); vstep = vsaveddetime(velem+1) - vsaveddetime(velem); vdiff = (vthetime - vlags(vcnt) - vsaveddetime(velem)) / vstep; vsubs = 1 - vdiff; %# Calculation of the coefficients for the interpolation algorithm vua = (1 + 2 * vdiff) * vsubs^2; vub = (3 - 2 * vdiff) * vdiff^2; vva = vstep * vdiff * vsubs^2; vvb = -vstep * vsubs * vdiff^2; vhmat(:,vcnt) = vua * vsaveddeinput(velem,:)' + ... vub * vsaveddeinput(velem+1,:)' + ... vva * vsavedderesult(velem,:)' + ... vvb * vsavedderesult(velem+1,:)'; end else %# the history must be a function handle for vcnt = 1:length (vlags) vhmat(:,vcnt) = feval ... (vhist, vthetime - vlags(vcnt), vfunarguments{:}); end end if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, vthetime, vfunarguments{:}); end vk(:,j) = vmass \ feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); else vk(:,j) = feval ... (vfun, vthetime, vtheinput, vhmat, vfunarguments{:}); end end %# Compute the 7th and the 8th order estimation y7 = vu' + vstepsize * (vk * vb7); y8 = vu' + vstepsize * (vk * vb8); if (vhavenonnegative) vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative)); y7(vodeoptions.NonNegative) = abs (y7(vodeoptions.NonNegative)); y8(vodeoptions.NonNegative) = abs (y8(vodeoptions.NonNegative)); end vSaveVUForRefine = vu; %# Calculate the absolute local truncation error and the acceptable error if (~vstepsizegiven) if (~vnormcontrol) vdelta = y8 - y7; vtau = max (vodeoptions.RelTol * vu', vodeoptions.AbsTol); else vdelta = norm (y8 - y7, Inf); vtau = max (vodeoptions.RelTol * max (norm (vu', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizegiven == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y8'; %# MC2001: the higher order estimation as "local extrapolation" vretvaltime(vcntloop,:) = vtimestamp; if (vhaveoutputselection) vretvalresult(vcntloop,:) = vu(vodeoptions.OutputSel); else vretvalresult(vcntloop,:) = vu; end vcntloop = vcntloop + 1; vcntiter = 0; %# Update DDE values for next history calculation vsaveddetime(end+1) = vtimestamp; vsaveddeinput(end+1,:) = vtheinput'; vsavedderesult(end+1,:) = vu; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) if (vhaverefine) %# Do interpolation for vcnt = 0:vodeoptions.Refine %# Approximation between told and t vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2); vapproxvals = vSaveVUForRefine' + vapproxtime * (vk * vb8); if (vhaveoutputselection) vapproxvals = vapproxvals(vodeoptions.OutputSel); end feval (vodeoptions.OutputFcn, (vtimestamp - vstepsize) + vapproxtime, ... vapproxvals, [], vfunarguments{:}); end end vpltret = feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', [], vfunarguments{:}); if (vpltret), vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vu(:), vhmat}, [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizegiven) %# vdelta may be 0 or even negative - could be an iteration problem vdelta = max (vdelta, eps); vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); elseif (vstepsizegiven) if (vcntloop < vtimelength) vstepsize = vslot(1,vcntloop-1) - vslot(1,vcntloop-2); end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for postprocessing vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vtimestamp < vtimestop) if (vunhandledtermination == true) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:HideWarning', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vretvalresult(vcntloop-1,:)', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... {vretvalresult(vcntloop-1,:), vhmat}, 'done', vfunarguments{:}); end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = 13*(vcntcycles-1); %# number of ode evaluations vndecomps = 0; %# number of LU decompositions vnpds = 0; %# number of partial derivatives vnlinsols = 0; %# no. of solutions of linear systems %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'ode78d'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end %# else nothing will be returned, varargout{1} undefined end %! # We are using a "pseudo-DDE" implementation for all tests that %! # are done for this function. We also define an Events and a %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn. %!function [vyd] = fexp (vt, vy, vz, varargin) %! vyd(1,1) = exp (- vt) - vz(1); %# The DDEs that are %! vyd(2,1) = vy(1) - vz(2); %# used for all examples %!function [vval, vtrm, vdir] = feve (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = zeros (2,1); %# don't stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vval, vtrm, vdir] = fevn (vt, vy, vz, varargin) %! vval = fexp (vt, vy, vz); %# We use the derivatives %! vtrm = ones (2,1); %# stop solving here %! vdir = ones (2,1); %# in positive direction %!function [vmas] = fmas (vt, vy, vz, varargin) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy, vz, varargin) %! vmas = sparse ([1, 0; 0, 1]); %# A dummy sparse matrix %!function [vref] = fref () %# The reference solution %! vref = [0.12194462133618, 0.01652432423938]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = ode78d (1, [0 5], [1; 0], 1, [1; 0]); %!error %# input argument number two %! B = ode78d (@fexp, 1, [1; 0], 1, [1; 0]); %!error %# input argument number three %! B = ode78d (@fexp, [0 5], 1, 1, [1; 0]); %!error %# input argument number four %! B = ode78d (@fexp, [0 5], [1; 0], [1; 1], [1; 0]); %!error %# input argument number five %! B = ode78d (@fexp, [0 5], [1; 0], 1, 1); %!test %# one output argument %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode78d'); %!test %# two output arguments %! [vt, vy] = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 0.2); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0]); %! assert ([vt(end), vy(end,:)], [5, fref], 0.2); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! faym = @(vt, vy, vz) [exp(-vt) - vz(1); vy(1) - vz(2)]; %! vsol = ode78d (faym, [0 5], [1; 0], 1, [1; 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# extra input arguments passed trhough %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-7, 'RelTol', 1e-7); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# RelTol and NormControl option %! vopt = odeset ('AbsTol', 1e-7, 'NormControl', 'on'); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# NonNegative for second component %! vopt = odeset ('NonNegative', 1); %! vsol = ode78d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2.5, 0.001, 0.237], 0.2); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode78d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode78d (@fexp, [0 2.5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie, [1; 1]); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 0.2); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vie, vxe, vye], ... %! [1.0000, 2.9219, -0.2127, -0.2671], 0.2); %! %! %# test for Jacobian option is missing %! %# test for Jacobian (being a sparse matrix) is missing %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for NewtonTol option is missing %! %# test for MaxNewtonIterations option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [5, fref], 0.2); %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! [vt, vy] = ode78d (@fexp, [0 5], [1; 0], 1, [1; 0], vopt); %! assert ([vt(end), vy(end,:)], [5, fref], 0.5); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %! warning ('on', 'OdePkg:InvalidOption'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odebwe.m0000644000000000000000000010526112526637474013550 0ustar 00000000000000%# Copyright (C) 2009-2012, Sebastian Schoeps %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} odebwe (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{sol}] =} odebwe (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odebwe (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}]) %# %# This function file can be used to solve a set of stiff ordinary differential equations (stiff ODEs) or stiff differential algebraic equations (stiff DAEs) with the Backward Euler method. %# %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}. %# %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}. %# %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector. %# %# For example, solve an anonymous implementation of the Van der Pol equation %# %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# vjac = @@(vt,vy) [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %# vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \ %# "NormControl", "on", "OutputFcn", @@odeplot, \ %# "Jacobian",vjac); %# odebwe (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = odebwe (vfun, vslot, vinit, varargin) if (nargin == 0) %# Check number and types of all input arguments help ('odebwe'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin < 3) print_usage; elseif ~(isa (vfun, 'function_handle') || isa (vfun, 'inline')) error ('OdePkg:InvalidArgument', ... 'First input argument must be a valid function handle'); elseif (~isvector (vslot) || length (vslot) < 2) error ('OdePkg:InvalidArgument', ... 'Second input argument must be a valid vector'); elseif (~isvector (vinit) || ~isnumeric (vinit)) error ('OdePkg:InvalidArgument', ... 'Third input argument must be a valid numerical value'); elseif (nargin >= 4) if (~isstruct (varargin{1})) %# varargin{1:len} are parameters for vfun vodeoptions = odeset; vfunarguments = varargin; elseif (length (varargin) > 1) %# varargin{1} is an OdePkg options structure vopt vodeoptions = odepkg_structure_check (varargin{1}, 'odebwe'); vfunarguments = {varargin{2:length(varargin)}}; else %# if (isstruct (varargin{1})) vodeoptions = odepkg_structure_check (varargin{1}, 'odebwe'); vfunarguments = {}; end else %# if (nargin == 3) vodeoptions = odeset; vfunarguments = {}; end %# Start preprocessing, have a look which options are set in %# vodeoptions, check if an invalid or unused option is set vslot = vslot(:).'; %# Create a row vector vinit = vinit(:).'; %# Create a row vector if (length (vslot) > 2) %# Step size checking vstepsizefixed = true; else vstepsizefixed = false; end %# The adaptive method require a second estimate for %# the comparsion, while the fixed step size algorithm %# needs only one if ~vstepsizefixed vestimators = 2; else vestimators = 1; end %# Get the default options that can be set with 'odeset' temporarily vodetemp = odeset; %# Implementation of the option RelTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ~vstepsizefixed) vodeoptions.RelTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol); elseif (~isempty (vodeoptions.RelTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "RelTol" will be ignored if fixed time stamps are given'); end %# Implementation of the option AbsTol has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ~vstepsizefixed) vodeoptions.AbsTol = 1e-6; warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol); elseif (~isempty (vodeoptions.AbsTol) && vstepsizefixed) warning ('OdePkg:InvalidArgument', ... 'Option "AbsTol" will be ignored if fixed time stamps are given'); else vodeoptions.AbsTol = vodeoptions.AbsTol(:); %# Create column vector end %# Implementation of the option NormControl has been finished. This %# option can be set by the user to another value than default value. if (strcmp (vodeoptions.NormControl, 'on')) vnormcontrol = true; else vnormcontrol = false; end %# Implementation of the option OutputFcn has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputFcn) && nargout == 0) vodeoptions.OutputFcn = @odeplot; vhaveoutputfunction = true; elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false; else vhaveoutputfunction = true; end %# Implementation of the option OutputSel has been finished. This %# option can be set by the user to another value than default value. if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true; else vhaveoutputselection = false; end %# Implementation of the option OutputSave has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.OutputSave)), vodeoptions.OutputSave = 1; end %# Implementation of the option Stats has been finished. This option %# can be set by the user to another value than default value. %# Implementation of the option InitialStep has been finished. This %# option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && ~vstepsizefixed) vodeoptions.InitialStep = (vslot(1,2) - vslot(1,1)) / 10; warning ('OdePkg:InvalidArgument', ... 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep); end %# Implementation of the option MaxNewtonIterations has been finished. This option %# can be set by the user to another value than default value. if isempty (vodeoptions.MaxNewtonIterations) vodeoptions.MaxNewtonIterations = 10; warning ('OdePkg:InvalidArgument', ... 'Option "MaxNewtonIterations" not set, new value %f is used', vodeoptions.MaxNewtonIterations); end %# Implementation of the option NewtonTol has been finished. This option %# can be set by the user to another value than default value. if isempty (vodeoptions.NewtonTol) vodeoptions.NewtonTol = 1e-7; warning ('OdePkg:InvalidArgument', ... 'Option "NewtonTol" not set, new value %f is used', vodeoptions.NewtonTol); end %# Implementation of the option MaxStep has been finished. This option %# can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ~vstepsizefixed) vodeoptions.MaxStep = (vslot(1,2) - vslot(1,1)) / 10; warning ('OdePkg:InvalidArgument', ... 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep); end %# Implementation of the option Events has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Events)), vhaveeventfunction = true; else vhaveeventfunction = false; end %# Implementation of the option Jacobian has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Jacobian) && isnumeric (vodeoptions.Jacobian)) vhavejachandle = false; vjac = vodeoptions.Jacobian; %# constant jac elseif (isa (vodeoptions.Jacobian, 'function_handle')) vhavejachandle = true; %# jac defined by a function handle else %# no Jacobian - we will use numerical differentiation vhavejachandle = false; end %# Implementation of the option Mass has been finished. This option %# can be set by the user to another value than default value. if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass)) vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass elseif (isa (vodeoptions.Mass, 'function_handle')) vhavemasshandle = true; %# mass defined by a function handle else %# no mass matrix - creating a diag-matrix of ones for mass vhavemasshandle = false; vmass = sparse (eye (length (vinit)) ); end %# Implementation of the option MStateDependence has been finished. %# This option can be set by the user to another value than default %# value. if (strcmp (vodeoptions.MStateDependence, 'none')) vmassdependence = false; else vmassdependence = true; end %# Other options that are not used by this solver. Print a warning %# message to tell the user that the option(s) is/are ignored. if (~isequal (vodeoptions.NonNegative, vodetemp.NonNegative)) warning ('OdePkg:InvalidArgument', ... 'Option "NonNegative" will be ignored by this solver'); end if (~isequal (vodeoptions.Refine, vodetemp.Refine)) warning ('OdePkg:InvalidArgument', ... 'Option "Refine" will be ignored by this solver'); end if (~isequal (vodeoptions.JPattern, vodetemp.JPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "JPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) warning ('OdePkg:InvalidArgument', ... 'Option "Vectorized" will be ignored by this solver'); end if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) warning ('OdePkg:InvalidArgument', ... 'Option "MvPattern" will be ignored by this solver'); end if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) warning ('OdePkg:InvalidArgument', ... 'Option "MassSingular" will be ignored by this solver'); end if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) warning ('OdePkg:InvalidArgument', ... 'Option "InitialSlope" will be ignored by this solver'); end if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) warning ('OdePkg:InvalidArgument', ... 'Option "MaxOrder" will be ignored by this solver'); end if (~isequal (vodeoptions.BDF, vodetemp.BDF)) warning ('OdePkg:InvalidArgument', ... 'Option "BDF" will be ignored by this solver'); end %# Starting the initialisation of the core solver odebwe vtimestamp = vslot(1,1); %# timestamp = start time vtimelength = length (vslot); %# length needed if fixed steps vtimestop = vslot(1,vtimelength); %# stop time = last value vdirection = sign (vtimestop); %# Flag for direction to solve if (~vstepsizefixed) vstepsize = vodeoptions.InitialStep; vminstepsize = (vtimestop - vtimestamp) / (1/eps); else %# If step size is given then use the fixed time steps vstepsize = vslot(1,2) - vslot(1,1); vminstepsize = sign (vstepsize) * eps; end vretvaltime = vtimestamp; %# first timestamp output vretvalresult = vinit; %# first solution output %# Initialize the OutputFcn if (vhaveoutputfunction) if (vhaveoutputselection) vretout = vretvalresult(vodeoptions.OutputSel); else vretout = vretvalresult; end feval (vodeoptions.OutputFcn, vslot.', ... vretout.', 'init', vfunarguments{:}); end %# Initialize the EventFcn if (vhaveeventfunction) odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vretvalresult.', 'init', vfunarguments{:}); end %# Initialize parameters and counters vcntloop = 2; vcntcycles = 1; vu = vinit; vcntsave = 2; vunhandledtermination = true; vpow = 1/2; vnpds = 0; vcntiter = 0; vcntnewt = 0; vndecomps = 0; vnlinsols = 0; %# the following option enables the simplified Newton method %# which evaluates the Jacobian only once instead of the %# standard method that updates the Jacobian in each iteration vsimplified = false; % or true %# The solver main loop - stop if the endpoint has been reached while ((vdirection * (vtimestamp) < vdirection * (vtimestop)) && ... (vdirection * (vstepsize) >= vdirection * (vminstepsize))) %# Hit the endpoint of the time slot exactely if ((vtimestamp + vstepsize) > vdirection * vtimestop) vstepsize = vtimestop - vdirection * vtimestamp; end %# Run the time integration for each estimator %# from vtimestamp -> vtimestamp+vstepsize for j = 1:vestimators %# Initial value (result of the previous timestep) y0 = vu; %# Initial guess for Newton-Raphson y(j,:) = vu; %# We do not use a higher order approximation for the %# comparsion, but two steps by the Backward Euler %# method for i = 1:j % Initialize the time stepping parameters vthestep = vstepsize / j; vthetime = vtimestamp + i*vthestep; vnewtit = 1; vresnrm = inf (1, vodeoptions.MaxNewtonIterations); %# Start the Newton iteration while (vnewtit < vodeoptions.MaxNewtonIterations) && ... (vresnrm (vnewtit) > vodeoptions.NewtonTol) %# Compute the Jacobian of the non-linear equation, %# that is the matrix pencil of the mass matrix and %# the right-hand-side's Jacobian. Perform a (sparse) %# LU-Decomposition afterwards. if ( (vnewtit==1) || (~vsimplified) ) %# Get the mass matrix from the left-hand-side if (vhavemasshandle) %# Handle only the dynamic mass matrix, if (vmassdependence) %# constant mass matrices have already vmass = feval ... %# been set before (if any) (vodeoptions.Mass, vthetime, y(j,:)', vfunarguments{:}); else %# if (vmassdependence == false) vmass = feval ... %# then we only have the time argument (vodeoptions.Mass, y(j,:)', vfunarguments{:}); end end %# Get the Jacobian of the right-hand-side's function if (vhavejachandle) %# Handle only the dynamic jacobian vjac = feval(vodeoptions.Jacobian, vthetime,... y(j,:)', vfunarguments{:}); elseif isempty(vodeoptions.Jacobian) %# If no Jacobian is given vjac = feval(@jacobian, vfun, vthetime,y(j,:)',... vfunarguments); %# then we differentiate end vnpds = vnpds + 1; vfulljac = vmass/vthestep - vjac; %# one could do a matrix decomposition of vfulljac here, %# but the choice of decomposition depends on the problem %# and therefore we use the backslash-operator in row 374 end %# Compute the residual vres = vmass/vthestep*(y(j,:)-y0)' - feval(vfun,vthetime,y(j,:)',vfunarguments{:}); vresnrm(vnewtit+1) = norm(vres,inf); %# Solve the linear system y(j,:) = vfulljac\(-vres+vfulljac*y(j,:)'); %# the backslash operator decomposes the matrix %# and solves the system in a single step. vndecomps = vndecomps + 1; vnlinsols = vnlinsols + 1; %# Prepare next iteration vnewtit = vnewtit + 1; end %# while Newton %# Leave inner loop if Newton diverged if vresnrm(vnewtit)>vodeoptions.NewtonTol break; end %# Save intermediate solution as initial value %# for the next intermediate step y0 = y(j,:); %# Count all Newton iterations vcntnewt = vcntnewt + (vnewtit-1); end %# for steps %# Leave outer loop if Newton diverged if vresnrm(vnewtit)>vodeoptions.NewtonTol break; end end %# for estimators % if all Newton iterations converged if vresnrm(vnewtit)vodeoptions.NewtonTol vdelta = 2; vtau = 1; elseif (~vstepsizefixed) if (~vnormcontrol) vdelta = abs (y3 - y1)'; vtau = max (vodeoptions.RelTol * abs (vu.'), vodeoptions.AbsTol); else vdelta = norm ((y3 - y1)', Inf); vtau = max (vodeoptions.RelTol * max (norm (vu.', Inf), 1.0), ... vodeoptions.AbsTol); end else %# if (vstepsizefixed == true) vdelta = 1; vtau = 2; end %# If the error is acceptable then update the vretval variables if (all (vdelta <= vtau)) vtimestamp = vtimestamp + vstepsize; vu = y2; % or y3 if we want the extrapolation.... %# Save the solution every vodeoptions.OutputSave steps if (mod (vcntloop-1,vodeoptions.OutputSave) == 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; vcntsave = vcntsave + 1; end vcntloop = vcntloop + 1; vcntiter = 0; %# Call plot only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if plot function %# returns false if (vhaveoutputfunction) if (vhaveoutputselection) vpltout = vu(vodeoptions.OutputSel); else vpltout = vu; end vpltret = feval (vodeoptions.OutputFcn, vtimestamp, ... vpltout.', [], vfunarguments{:}); if vpltret %# Leave loop vunhandledtermination = false; break; end end %# Call event only if a valid result has been found, therefore this %# code fragment has moved here. Stop integration if veventbreak is %# true if (vhaveeventfunction) vevent = ... odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu(:), [], vfunarguments{:}); if (~isempty (vevent{1}) && vevent{1} == 1) vretvaltime(vcntloop-1,:) = vevent{3}(end,:); vretvalresult(vcntloop-1,:) = vevent{4}(end,:); vunhandledtermination = false; break; end end end %# If the error is acceptable ... %# Update the step size for the next integration step if (~vstepsizefixed) %# 20080425, reported by Marco Caliari %# vdelta cannot be negative (because of the absolute value that %# has been introduced) but it could be 0, then replace the zeros %# with the maximum value of vdelta vdelta(find (vdelta == 0)) = max (vdelta); %# It could happen that max (vdelta) == 0 (ie. that the original %# vdelta was 0), in that case we double the previous vstepsize vdelta(find (vdelta == 0)) = max (vtau) .* (0.4 ^ (1 / vpow)); if (vdirection == 1) vstepsize = min (vodeoptions.MaxStep, ... min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); else vstepsize = max (vodeoptions.MaxStep, ... max (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow)); end else %# if (vstepsizefixed) if (vresnrm(vnewtit)>vodeoptions.NewtonTol) vunhandledtermination = true; break; elseif (vcntloop <= vtimelength) vstepsize = vslot(vcntloop) - vslot(vcntloop-1); else %# Get out of the main integration loop break; end end %# Update counters that count the number of iteration cycles vcntcycles = vcntcycles + 1; %# Needed for cost statistics vcntiter = vcntiter + 1; %# Needed to find iteration problems %# Stop solving because the last 1000 steps no successful valid %# value has been found if (vcntiter >= 5000) error (['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f before endpoint at', ... ' tend = %f was reached. This happened because the iterative', ... ' integration loop does not find a valid solution at this time', ... ' stamp. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); end end %# The main loop %# Check if integration of the ode has been successful if (vdirection * vtimestamp < vdirection * vtimestop) if (vunhandledtermination == true) error ('OdePkg:InvalidArgument', ... ['Solving has not been successful. The iterative', ... ' integration loop exited at time t = %f', ... ' before endpoint at tend = %f was reached. This may', ... ' happen if the stepsize grows smaller than defined in', ... ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ... ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop); else warning ('OdePkg:InvalidArgument', ... ['Solver has been stopped by a call of "break" in', ... ' the main iteration loop at time t = %f before endpoint at', ... ' tend = %f was reached. This may happen because the @odeplot', ... ' function returned "true" or the @event function returned "true".'], ... vtimestamp, vtimestop); end end %# Postprocessing, do whatever when terminating integration algorithm if (vhaveoutputfunction) %# Cleanup plotter feval (vodeoptions.OutputFcn, vtimestamp, ... vu.', 'done', vfunarguments{:}); end if (vhaveeventfunction) %# Cleanup event function handling odepkg_event_handle (vodeoptions.Events, vtimestamp, ... vu.', 'done', vfunarguments{:}); end %# Save the last step, if not already saved if (mod (vcntloop-2,vodeoptions.OutputSave) ~= 0) vretvaltime(vcntsave,:) = vtimestamp; vretvalresult(vcntsave,:) = vu; end %# Print additional information if option Stats is set if (strcmp (vodeoptions.Stats, 'on')) vhavestats = true; vnsteps = vcntloop-2; %# vcntloop from 2..end vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end vnfevals = vcntnewt; %# number of rhs evaluations if isempty(vodeoptions.Jacobian) %# additional evaluations due vnfevals = vnfevals + vcntnewt*(1+length(vinit)); %# to differentiation end %# Print cost statistics if no output argument is given if (nargout == 0) vmsg = fprintf (1, 'Number of successful steps: %d\n', vnsteps); vmsg = fprintf (1, 'Number of failed attempts: %d\n', vnfailed); vmsg = fprintf (1, 'Number of function calls: %d\n', vnfevals); end else vhavestats = false; end if (nargout == 1) %# Sort output variables, depends on nargout varargout{1}.x = vretvaltime; %# Time stamps are saved in field x varargout{1}.y = vretvalresult; %# Results are saved in field y varargout{1}.solver = 'odebwe'; %# Solver name is saved in field solver if (vhaveeventfunction) varargout{1}.ie = vevent{2}; %# Index info which event occured varargout{1}.xe = vevent{3}; %# Time info when an event occured varargout{1}.ye = vevent{4}; %# Results when an event occured end if (vhavestats) varargout{1}.stats = struct; varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; varargout{1}.stats.npds = vnpds; varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; end elseif (nargout == 2) varargout{1} = vretvaltime; %# Time stamps are first output argument varargout{2} = vretvalresult; %# Results are second output argument elseif (nargout == 5) varargout{1} = vretvaltime; %# Same as (nargout == 2) varargout{2} = vretvalresult; %# Same as (nargout == 2) varargout{3} = []; %# LabMat doesn't accept lines like varargout{4} = []; %# varargout{3} = varargout{4} = []; varargout{5} = []; if (vhaveeventfunction) varargout{3} = vevent{3}; %# Time info when an event occured varargout{4} = vevent{4}; %# Results when an event occured varargout{5} = vevent{2}; %# Index info which event occured end end end function df = jacobian(vfun,vthetime,vtheinput,vfunarguments); %# Internal function for the numerical approximation of a jacobian vlen = length(vtheinput); vsigma = sqrt(eps); vfun0 = feval(vfun,vthetime,vtheinput,vfunarguments{:}); df = zeros(vlen,vlen); for j = 1:vlen vbuffer = vtheinput(j); if (vbuffer==0) vh = vsigma; elseif (abs(vbuffer)>1) vh = vsigma*vbuffer; else vh = sign(vbuffer)*vsigma; end vtheinput(j) = vbuffer + vh; df(:,j) = (feval(vfun,vthetime,vtheinput,... vfunarguments{:}) - vfun0) / vh; vtheinput(j) = vbuffer; end end %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference sol %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end %! elseif (isempty (vflag)) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidArgument'); %! B = odebwe (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = odebwe (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = odebwe (@flor, [0 25], 1); %!test %# one output argument %! vsol = odebwe (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'odebwe'); %!test %# two output arguments %! [vt, vy] = odebwe (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = odebwe (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = odebwe (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = odebwe (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = odebwe (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %!test %# Solve vdp in fixed step sizes %! vsol = odebwe (@fpol, [0:0.001:2], [2 0]); %! assert (vsol.x(:), [0:0.001:2]'); %! assert (vsol.y(end,:), fref, 1e-2); %!test %# Solve in backward direction starting at t=0 %! %# vref = [-1.2054034414, 0.9514292694]; %! vsol = odebwe (@fpol, [0 -2], [2 0]); %! %# assert ([vsol.x(end), vsol.y(end,:)], [-2, fref], 1e-3); %!test %# Solve in backward direction starting at t=2 %! %# vref = [-1.2154183302, 0.9433018000]; %! vsol = odebwe (@fpol, [2 -2], [0.3233166627 -1.8329746843]); %! %# assert ([vsol.x(end), vsol.y(end,:)], [-2, fref], 1e-3); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-2); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-6, 'RelTol', 1e-6); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-2); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-6, 'NormControl', 'on'); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for NonNegative option is missing %! %# test for OutputSel and Refine option is missing %! %!test %# Details of OutputSave can't be tested %! vopt = odeset ('OutputSave', 1, 'OutputSel', 1); %! vsla = odebwe (@fpol, [0 2], [2 0], vopt); %! vopt = odeset ('OutputSave', 2); %! vslb = odebwe (@fpol, [0 2], [2 0], vopt); %! assert (length (vsla.x) > length (vslb.x)) %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = odebwe (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = odebwe (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = odebwe (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie, [2; 1; 2; 1]); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = odebwe (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-3); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = odebwe (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-3); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = odebwe (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for MStateDependence option is missing %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %# test for BDF option is missing %! %! warning ('on', 'OdePkg:InvalidArgument'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odeexamples.m0000644000000000000000000000355012526637474014607 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} odeexamples (@var{}) %# Open the differential equations examples menu and allow the user to select a submenu of ODE, DAE, IDE or DDE examples. %# @end deftypefn function [] = odeexamples (varargin) vfam = 1; while (vfam > 0) clc; fprintf (1, ... ['OdePkg examples main menu:\n', ... '==========================\n', ... '\n', ... ' (1) Open the ODE examples menu\n', ... ' (2) Open the DAE examples menu\n', ... ' (3) Open the IDE examples menu\n', ... ' (4) Open the DDE examples menu\n', ... '\n']); vfam = input ('Please choose a number from above or press to return: '); switch (vfam) case 1 odepkg_examples_ode; case 2 odepkg_examples_dae; case 3 odepkg_examples_ide; case 4 odepkg_examples_dde; otherwise %# have nothing to do end end %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odeget.m0000644000000000000000000001340712526637474013552 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{value}] =} odeget (@var{odestruct}, @var{option}, [@var{default}]) %# @deftypefnx {Command} {[@var{values}] =} odeget (@var{odestruct}, @{@var{opt1}, @var{opt2}, @dots{}@}, [@{@var{def1}, @var{def2}, @dots{}@}]) %# %# If this function is called with two input arguments and the first input argument @var{odestruct} is of type structure array and the second input argument @var{option} is of type string then return the option value @var{value} that is specified by the option name @var{option} in the OdePkg option structure @var{odestruct}. Optionally if this function is called with a third input argument then return the default value @var{default} if @var{option} is not set in the structure @var{odestruct}. %# %# If this function is called with two input arguments and the first input argument @var{odestruct} is of type structure array and the second input argument @var{option} is of type cell array of strings then return the option values @var{values} that are specified by the option names @var{opt1}, @var{opt2}, @dots{} in the OdePkg option structure @var{odestruct}. Optionally if this function is called with a third input argument of type cell array then return the default value @var{def1} if @var{opt1} is not set in the structure @var{odestruct}, @var{def2} if @var{opt2} is not set in the structure @var{odestruct}, @dots{} %# %# Run examples with the command %# @example %# demo odeget %# @end example %# @end deftypefn %# %# @seealso{odepkg} %# Note: 20061022, Thomas Treichl %# We cannot create a function of the form odeget (@var{odestruct}, %# @var{name1}, @var{name2}) because we would get a mismatch with %# the function form 1 like described above. function [vret] = odeget (varargin) if (nargin == 0) %# Check number and types of input arguments vmsg = sprintf ('Number of input arguments must be greater than zero'); help ('odeget'); error (vmsg); elseif (isstruct (varargin{1}) == true) %# Check first input argument vint.odestruct = odepkg_structure_check (varargin{1}); vint.otemplate = odeset; %# Create default odepkg otpions structure else vmsg = sprintf ('First input argument must be a valid odepkg otpions structure'); error (vmsg); end %# Check number and types of other input arguments if (length (varargin) < 2 || length (varargin) > 3) vmsg = sprintf ('odeget (odestruct, "option", default) or\n odeget (odestruct, {"opt1", "opt2", ...}, {def1, def2, ...})'); usage (vmsg); elseif (ischar (varargin{2}) == true && isempty (varargin{2}) == false) vint.arguments = {varargin{2}}; vint.lengtharg = 1; elseif (iscellstr (varargin{2}) == true && isempty (varargin{2}) == false) vint.arguments = varargin{2}; vint.lengtharg = length (vint.arguments); end if (nargin == 3) %# Check if third input argument is valid if (iscell (varargin{3}) == true) vint.defaults = varargin{3}; vint.lengthdf = length (vint.defaults); else vint.defaults = {varargin{3}}; vint.lengthdf = 1; end if (vint.lengtharg ~= vint.lengthdf) vmsg = sprintf ('If third argument is given then sizes of argument 2 and argument 3 must be the equal'); error (vmsg); end end %# Run through number of input arguments given for vcntarg = 1:vint.lengtharg if ((... isempty(vint.odestruct.(vint.arguments{vcntarg})) )||( ... ischar(vint.odestruct.(vint.arguments{vcntarg})) && ... strcmp(vint.odestruct.(vint.arguments{vcntarg}),vint.otemplate.(vint.arguments{vcntarg}))... )||(... ~ischar(vint.odestruct.(vint.arguments{vcntarg})) && ... vint.odestruct.(vint.arguments{vcntarg}) == vint.otemplate.(vint.arguments{vcntarg}) ... )) if (nargin == 3), vint.returnval{vcntarg} = vint.defaults{vcntarg}; else, vint.returnval{vcntarg} = vint.odestruct.(vint.arguments{vcntarg}); end else, vint.returnval{vcntarg} = vint.odestruct.(vint.arguments{vcntarg}); end end %# Postprocessing, store results in the vret variable if (vint.lengtharg == 1), vret = vint.returnval{1}; else, vret = vint.returnval; end %!test assert (odeget (odeset (), 'RelTol'), []); %!test assert (odeget (odeset (), 'RelTol', 10), 10); %!test assert (odeget (odeset (), {'RelTol', 'AbsTol'}), {[] []}) %!test assert (odeget (odeset (), {'RelTol', 'AbsTol'}, {10 20}), {10 20}); %!test assert (odeget (odeset (), 'Stats'), 'off'); %!test assert (odeget (odeset (), 'Stats', 'on'), 'on'); %!demo %! # Return the manually changed value RelTol of the OdePkg options %! # strutcure A. If RelTol wouldn't have been changed then an %! # empty matrix value would have been returned. %! %! A = odeset ('RelTol', 1e-1, 'AbsTol', 1e-2); %! odeget (A, 'RelTol', []) %!demo %! # Return the manually changed value of RelTol and the value 1e-4 %! # for AbsTol of the OdePkg options structure A. %! %! A = odeset ('RelTol', 1e-1); %! odeget (A, {'RelTol', 'AbsTol'}, {1e-2, 1e-4}) %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odephas2.m0000644000000000000000000000664412526637474014015 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{ret}] =} odephas2 (@var{t}, @var{y}, @var{flag}) %# %# Open a new figure window and plot the first result from the variable @var{y} that is of type double column vector over the second result from the variable @var{y} while solving. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is %# @table @option %# @item @code{"init"} %# then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, %# @item @code{""} %# then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', %# @item @code{"done"} %# then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. %# @end table %# %# This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. %# %# For example, solve an anonymous implementation of the "Van der Pol" equation and display the results while solving in a 2D plane %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# %# vopt = odeset ('OutputFcn', @@odephas2, 'RelTol', 1e-6); %# vsol = ode45 (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = odephas2 (vt, vy, vflag) %# No input argument check is done for a higher processing speed persistent vfigure; persistent vyold; persistent vcounter; if (strcmp (vflag, 'init')) %# Nothing to return, vt is either the time slot [tstart tstop] %# or [t0, t1, ..., tn], vy is the inital value vector 'vinit' vfigure = figure; vyold = vy(:,1); vcounter = 1; elseif (isempty (vflag)) %# Return something in varargout{1}, either false for 'not stopping %# the integration' or true for 'stopping the integration' vcounter = vcounter + 1; figure (vfigure); vyold(:,vcounter) = vy(:,1); plot (vyold(1,:), vyold(2,:), '-o', 'markersize', 1); drawnow; varargout{1} = false; elseif (strcmp (vflag, 'done')) %# Cleanup has to be done, clear the persistent variables because %# we don't need them anymore clear ('vfigure', 'vyold', 'vcounter'); end %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odephas3.m0000644000000000000000000000721612526637474014012 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{ret}] =} odephas3 (@var{t}, @var{y}, @var{flag}) %# %# Open a new figure window and plot the first result from the variable @var{y} that is of type double column vector over the second and the third result from the variable @var{y} while solving. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is %# @table @option %# @item @code{"init"} %# then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, %# @item @code{""} %# then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', %# @item @code{"done"} %# then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. %# @end table %# %# This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. %# %# For example, solve the "Lorenz attractor" and display the results while solving in a 3D plane %# @example %# function vyd = florenz (vt, vx) %# vyd = [10 * (vx(2) - vx(1)); %# vx(1) * (28 - vx(3)); %# vx(1) * vx(2) - 8/3 * vx(3)]; %# endfunction %# %# vopt = odeset ('OutputFcn', @@odephas3); %# vsol = ode23 (@@florenz, [0:0.01:7.5], [3 15 1], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = odephas3 (vt, vy, vflag) %# vt and vy are always column vectors, vflag can be either 'init' %# or [] or 'done'. If 'init' then varargout{1} = [], if [] the %# varargout{1} either true or false, if 'done' then varargout{1} = []. persistent vfigure; persistent vyold; persistent vcounter; if (strcmp (vflag, 'init')) %# vt is either the time slot [tstart tstop] or [t0, t1, ..., tn] %# vy is the inital value vector vinit from the caller function vfigure = figure; vyold = vy(:,1); vcounter = 1; elseif (isempty (vflag)) %# Return something in varargout{1}, either false for 'not stopping %# the integration' or true for 'stopping the integration' vcounter = vcounter + 1; figure (vfigure); vyold(:,vcounter) = vy(:,1); plot3 (vyold(1,:), vyold(2,:), vyold (3,:), '-o', 'markersize', 1); drawnow; varargout{1} = false; elseif (strcmp (vflag, 'done')) %# Cleanup has to be done, clear the persistent variables because %# we don't need them anymore clear ('vfigure', 'vyold', 'vcounter'); end %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg.m0000644000000000000000000002156412526637474013557 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} odepkg () %# %# OdePkg is part of the GNU Octave Repository (the Octave--Forge project). The package includes commands for setting up various options, output functions etc. before solving a set of differential equations with the solver functions that are also included. At this time OdePkg is under development with the main target to make a package that is mostly compatible to proprietary solver products. %# %# If this function is called without any input argument then open the OdePkg tutorial in the Octave window. The tutorial can also be opened with the following command %# %# @example %# doc odepkg %# @end example %# @end deftypefn function [] = odepkg (vstr) %# Check number and types of all input arguments if (nargin == 0) doc ('odepkg'); elseif (nargin > 1) error ('Number of input arguments must be zero or one'); elseif (ischar (vstr)) feval (vstr); else error ('Input argument must be a valid string'); end function [] = odepkg_validate_mfiles () %# From command line in the 'inst' directory do something like this: %# octave --quiet --eval "odepkg ('odepkg_validate_mfiles')" vfun = {'ode23', 'ode45', 'ode54', 'ode78', ... 'ode23d', 'ode45d', 'ode54d', 'ode78d', ... 'odeget', 'odeset', 'odeplot', 'odephas2', 'odephas3', 'odeprint', ... 'odepkg_structure_check', 'odepkg_event_handle', ... 'odepkg_testsuite_calcscd', 'odepkg_testsuite_calcmescd'}; %# vfun = sort (vfun); for vcnt=1:length(vfun) printf ('Testing function %s ... ', vfun{vcnt}); test (vfun{vcnt}, 'quiet'); fflush (1); end function [] = odepkg_validate_ccfiles () %# From command line in the 'src' directory do something like this: %# octave --quiet --eval "odepkg ('odepkg_validate_ccfiles')" vfile = {'odepkg_octsolver_mebdfdae.cc', 'odepkg_octsolver_mebdfi.cc', ... 'odepkg_octsolver_ddaskr.cc', ... 'odepkg_octsolver_radau.cc', 'odepkg_octsolver_radau5.cc', ... 'odepkg_octsolver_rodas.cc', 'odepkg_octsolver_seulex.cc'}; vsolv = {'odebda', 'odebdi', 'odekdi', ... 'ode2r', 'ode5r', 'oders', 'odesx'}; %# vfile = {'odepkg_octsolver_ddaskr.cc'}; %# vsolv = {'odekdi'}; for vcnt=1:length(vfile) printf ('Testing function %s ... ', vsolv{vcnt}); autoload (vsolv{vcnt}, which ('dldsolver.oct')); test (vfile{vcnt}, 'quiet'); fflush (1); end function [] = odepkg_internal_mhelpextract () %# In the inst directory do %# octave --quiet --eval "odepkg ('odepkg_internal_mhelpextract')" vfun = {'odepkg', 'odeget', 'odeset', ... 'ode23', 'ode45', 'ode54', 'ode78', ... 'ode23d', 'ode45d', 'ode54d', 'ode78d', ... 'odebwe', ... 'odeplot', 'odephas2', 'odephas3', 'odeprint', ... 'odepkg_structure_check', 'odepkg_event_handle', ... 'odepkg_testsuite_calcscd', 'odepkg_testsuite_calcmescd', ... 'odepkg_testsuite_oregonator', 'odepkg_testsuite_pollution', ... 'odepkg_testsuite_hires', ... 'odepkg_testsuite_robertson', 'odepkg_testsuite_chemakzo', ... 'odepkg_testsuite_transistor', ... 'odepkg_testsuite_implrober', 'odepkg_testsuite_implakzo', ... 'odepkg_testsuite_imptrans', ... 'odeexamples', 'odepkg_examples_ode', 'odepkg_examples_dae', ... 'odepkg_examples_ide', 'odepkg_examples_dde'}; vfun = sort (vfun); [vout, vmsg] = fopen ('../doc/mfunref.texi', 'w'); if ~(isempty (vmsg)), error (vmsg); end for vcnt = 1:length (vfun) if (exist (vfun{vcnt}, 'file')) [vfid, vmsg] = fopen (which (vfun{vcnt}), 'r'); if ~(isempty (vmsg)), error (vmsg); end while (true) vlin = fgets (vfid); if ~(ischar (vlin)), break; end if (regexp (vlin, '^(%# -\*- texinfo -\*-)')) while (~isempty (regexp (vlin, '^(%#)')) && ... isempty (regexp (vlin, '^(%# @end deftypefn)'))) vlin = fgets (vfid); if (length (vlin) > 3), fprintf (vout, '%s', vlin(4:end)); else fprintf (vout, '%s', vlin(3:end)); end end fprintf (vout, '\n'); end end fclose (vfid); end end fclose (vout); function [] = odepkg_internal_octhelpextract () %# In the src directory do %# octave --quiet --eval "odepkg ('odepkg_internal_octhelpextract')" vfiles = {'../src/odepkg_octsolver_mebdfdae.cc', ... '../src/odepkg_octsolver_mebdfi.cc', ... '../src/odepkg_octsolver_ddaskr.cc', ... '../src/odepkg_octsolver_radau.cc', ... '../src/odepkg_octsolver_radau5.cc', ... '../src/odepkg_octsolver_rodas.cc', ... '../src/odepkg_octsolver_seulex.cc', ... }; %# vfiles = sort (vfiles); Don't sort these files [vout, vmsg] = fopen ('../doc/dldfunref.texi', 'w'); if ~(isempty (vmsg)), error (vmsg); end for vcnt = 1:length (vfiles) if (exist (vfiles{vcnt}, 'file')) [vfid, vmsg] = fopen (vfiles{vcnt}, 'r'); if ~(isempty (vmsg)), error (vmsg); end while (true) vlin = fgets (vfid); if ~(ischar (vlin)), break; end if (regexp (vlin, '^("-\*- texinfo -\*-\\n\\)')) %#" vlin = ' * '; %# Needed for the first call of while() while (isempty (regexp (vlin, '^(@end deftypefn)')) && ischar (vlin)) vlin = fgets (vfid); vlin = sprintf (regexprep (vlin, '\\n\\', '')); vlin = regexprep (vlin, '\\"', '"'); fprintf (vout, '%s', vlin); end fprintf (vout, '\n'); end end fclose (vfid); end end fclose (vout); function [] = odepkg_performance_mathires () vfun = {@ode113, @ode23, @ode45, ... @ode15s, @ode23s, @ode23t, @ode23tb}; for vcnt=1:length(vfun) vsol{vcnt, 1} = odepkg_testsuite_hires (vfun{vcnt}, 1e-7); end odepkg_testsuite_write (vsol); function [] = odepkg_performance_octavehires () vfun = {@ode23, @ode45, @ode54, @ode78, ... @ode2r, @ode5r, @oders, @odesx, @odebda}; for vcnt=1:length(vfun) vsol{vcnt, 1} = odepkg_testsuite_hires (vfun{vcnt}, 1e-7); end odepkg_testsuite_write (vsol); function [] = odepkg_performance_matchemakzo () vfun = {@ode23, @ode45, ... @ode15s, @ode23s, @ode23t, @ode23tb}; for vcnt=1:length(vfun) vsol{vcnt, 1} = odepkg_testsuite_chemakzo (vfun{vcnt}, 1e-7); end odepkg_testsuite_write (vsol); function [] = odepkg_performance_octavechemakzo () vfun = {@ode23, @ode45, @ode54, @ode78, ... @ode2r, @ode5r, @oders, @odesx, @odebda}; for vcnt=1:length(vfun) vsol{vcnt, 1} = odepkg_testsuite_chemakzo (vfun{vcnt}, 1e-7); end odepkg_testsuite_write (vsol); function [] = odepkg_testsuite_write (vsol) fprintf (1, ['-----------------------------------------------------------------------------------------\n']); fprintf (1, [' Solver RelTol AbsTol Init Mescd Scd Steps Accept FEval JEval LUdec Time\n']); fprintf (1, ['-----------------------------------------------------------------------------------------\n']); [vlin, vcol] = size (vsol); for vcntlin = 1:vlin if (isempty (vsol{vcntlin}{9})), vsol{vcntlin}{9} = 0; end %# if (vcntlin > 1) %# Delete solver name if it is the same as before %# if (strcmp (func2str (vsol{vcntlin}{1}), func2str (vsol{vcntlin-1}{1}))) %# vsol{vcntlin}{1} = ' '; %# end %# end fprintf (1, ['%7s %6.0g %6.0g %6.0g %5.2f %5.2f %5.0d %6.0d %5.0d %5.0d %5.0d %6.3f\n'], ... func2str (vsol{vcntlin}{1}), vsol{vcntlin}{2:12}); end fprintf (1, ['-----------------------------------------------------------------------------------------\n']); function [] = odepkg_internal_demos () vfun = {'odepkg', 'odeget', 'odeset', ... 'ode23', 'ode45', 'ode54', 'ode78', ... 'ode23d', 'ode45d', 'ode54d', 'ode78d', ... 'odeplot', 'odephas2', 'odephas3', 'odeprint', ... 'odepkg_structure_check', 'odepkg_event_handle'}; vfun = sort (vfun); for vcnt = 1:length (vfun), demo (vfun{vcnt}); end %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_event_handle.m0000644000000000000000000001742512526637474016274 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{sol}] =} odepkg_event_handle (@var{@@fun}, @var{time}, @var{y}, @var{flag}, [@var{par1}, @var{par2}, @dots{}]) %# %# Return the solution of the event function that is specified as the first input argument @var{@@fun} in form of a function handle. The second input argument @var{time} is of type double scalar and specifies the time of the event evaluation, the third input argument @var{y} either is of type double column vector (for ODEs and DAEs) and specifies the solutions or is of type cell array (for IDEs and DDEs) and specifies the derivatives or the history values, the third input argument @var{flag} is of type string and can be of the form %# @table @option %# @item @code{"init"} %# then initialize internal persistent variables of the function @command{odepkg_event_handle} and return an empty cell array of size 4, %# @item @code{"calc"} %# then do the evaluation of the event function and return the solution @var{sol} as type cell array of size 4, %# @item @code{"done"} %# then cleanup internal variables of the function @command{odepkg_event_handle} and return an empty cell array of size 4. %# @end table %# Optionally if further input arguments @var{par1}, @var{par2}, @dots{} of any type are given then pass these parameters through @command{odepkg_event_handle} to the event function. %# %# This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. %# @end deftypefn %# %# @seealso{odepkg} function [vretval] = odepkg_event_handle (vevefun, vt, vy, vflag, varargin) %# No error handling has been implemented in this function to achieve %# the highest performance available. %# vretval{1} is true or false; either to terminate or to continue %# vretval{2} is the index information for which event occured %# vretval{3} is the time information column vector %# vretval{4} is the line by line result information matrix %# These persistent variables are needed to store the results and the %# time value from the processing in the time stamp before, veveold %# are the results from the event function, vtold the time stamp, %# vretcell the return values cell array, vyold the result of the ode %# and vevecnt the counter for how often this event handling %# has been called persistent veveold; persistent vtold; persistent vretcell; persistent vyold; persistent vevecnt; %# Call the event function if an event function has been defined to %# initialize the internal variables of the event function an to get %# a value for veveold if (strcmp (vflag, 'init')) if (~iscell (vy)) vinpargs = {vevefun, vt, vy}; else vinpargs = {vevefun, vt, vy{1}, vy{2}}; vy = vy{1}; %# Delete cell element 2 end if (nargin > 4) vinpargs = {vinpargs{:}, varargin{:}}; end [veveold, vterm, vdir] = feval (vinpargs{:}); %# We assume that all return values must be column vectors veveold = veveold(:)'; vterm = vterm(:)'; vdir = vdir(:)'; vtold = vt; vyold = vy; vevecnt = 1; vretcell = cell (1,4); %# Process the event, find the zero crossings either for a rising %# or for a falling edge elseif (isempty (vflag)) if (~iscell (vy)) vinpargs = {vevefun, vt, vy}; else vinpargs = {vevefun, vt, vy{1}, vy{2}}; vy = vy{1}; %# Delete cell element 2 end if (nargin > 4) vinpargs = {vinpargs{:}, varargin{:}}; end [veve, vterm, vdir] = feval (vinpargs{:}); %# We assume that all return values must be column vectors veve = veve(:)'; vterm = vterm(:)'; vdir = vdir(:)'; %# Check if one or more signs of the event has changed vsignum = (sign (veveold) ~= sign (veve)); if (any (vsignum)) %# One or more values have changed vindex = find (vsignum); %# Get the index of the changed values if (any (vdir(vindex) == 0)) %# Rising or falling (both are possible) %# Don't change anything, keep the index elseif (any (vdir(vindex) == sign (veve(vindex)))) %# Detected rising or falling, need a new index vindex = find (vdir == sign (veve)); else %# Found a zero crossing but must not be notified vindex = []; end %# Create new output values if a valid index has been found if (~isempty (vindex)) %# Change the persistent result cell array vretcell{1} = any (vterm(vindex)); %# Stop integration or not vretcell{2}(vevecnt,1) = vindex(1,1); %# Take first event found %# Calculate the time stamp when the event function returned 0 and %# calculate new values for the integration results, we do both by %# a linear interpolation vtnew = vt - veve(1,vindex) * (vt - vtold) / (veve(1,vindex) - veveold(1,vindex)); vynew = (vy - (vt - vtnew) * (vy - vyold) / (vt - vtold))'; vretcell{3}(vevecnt,1) = vtnew; vretcell{4}(vevecnt,:) = vynew; vevecnt = vevecnt + 1; end %# if (~isempty (vindex)) end %# Check for one or more signs ... veveold = veve; vtold = vt; vretval = vretcell; vyold = vy; elseif (strcmp (vflag, 'done')) %# Clear this event handling function clear ('veveold', 'vtold', 'vretcell', 'vyold', 'vevecnt'); vretcell = cell (1,4); end %!function [veve, vterm, vdir] = feveode (vt, vy, varargin) %! veve = vy(1); %# Which event component should be treaded %! vterm = 1; %# Terminate if an event is found %! vdir = -1; %# In which direction, -1 for falling %!function [veve, vterm, vdir] = feveide (vt, vy, vyd, varargin) %! veve = vy(1); %# Which event component should be treaded %! vterm = 1; %# Terminate if an event is found %! vdir = -1; %# In which direction, -1 for falling %! %!test %# First call to initialize the odepkg_event_handle function %! odepkg_event_handle (@feveode, 0.0, [0 1 2 3], 'init', 123, 456); %!test %# Two calls to find the event that may occur with ODE/DAE syntax %! A = odepkg_event_handle (@feveode, 2.0, [ 0 0 3 2], '', 123, 456); %! B = odepkg_event_handle (@feveode, 3.0, [-1 0 3 2], '', 123, 456); %! assert (A, {[], [], [], []}); %! assert (B{4}, [0 0 3 2]); %!test %# Last call to cleanup the odepkg_event_handle function %! odepkg_event_handle (@feveode, 4.0, [0 1 2 3], 'done', 123, 456); %!test %# First call to initialize the odepkg_event_handle function %! odepkg_event_handle (@feveide, 0.0, {[0 1 2 3], [0 1 2 3]}, 'init', 123, 456); %!test %# Two calls to find the event that may occur with IDE/DDE syntax %! A = odepkg_event_handle (@feveide, 2.0, {[0 0 3 2], [0 0 3 2]}, '', 123, 456); %! B = odepkg_event_handle (@feveide, 3.0, {[-1 0 3 2], [0 0 3 2]}, '', 123, 456); %! assert (A, {[], [], [], []}); %! assert (B{4}, [0 0 3 2]); %!test %# Last call to cleanup the odepkg_event_handle function %! odepkg_event_handle (@feveide, 4.0, {[0 1 2 3], [0 1 2 3]}, 'done', 123, 456); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_examples_dae.m0000644000000000000000000000644312526637474016265 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} odepkg_examples_dae (@var{}) %# Open the DAE examples menu and allow the user to select a demo that will be evaluated. %# @end deftypefn function [] = odepkg_examples_dae () vode = 1; while (vode > 0) clc; fprintf (1, ... ['DAE examples menu:\n', ... '==================\n', ... '\n', ... ' (1) Solve the "Robertson problem" with solver "ode2r"\n', ... ' (2) Solve another "Robertson implementation" with solver "ode5r"\n', ... '\n', ... ' Note: There are further DAE examples available with the OdePkg\n', ... ' testsuite functions.\n', ... '\n', ... ' If you have another interesting DAE example that you would like\n', ... ' to share then please modify this file, create a patch and send\n', ... ' your patch with your added example to the OdePkg developer team.\n', ... '\n' ]); vode = input ('Please choose a number from above or press to return: '); clc; if (vode > 0 && vode < 3) %# We can't use the function 'demo' directly here because it does %# not allow to run other functions within a demo. vexa = example (mfilename (), vode); disp (vexa); eval (vexa); input ('Press to continue: '); end %# if (vode > 0) end %# while (vode > 0) %!demo %! # Solve the "Robertson problem" with a Mass function that is given by %! # a function handle. %! %! function [vyd] = frobertson (vt, vy, varargin) %! vyd(1,1) = -0.04 * vy(1) + 1e4 * vy(2) * vy(3); %! vyd(2,1) = 0.04 * vy(1) - 1e4 * vy(2) * vy(3) - 3e7 * vy(2)^2; %! vyd(3,1) = vy(1) + vy(2) + vy(3) - 1; %! endfunction %! %! function [vmass] = fmass (vt, vy, varargin) %! vmass = [1, 0, 0; 0, 1, 0; 0, 0, 0]; %! endfunction %! %! vopt = odeset ('Mass', @fmass, 'NormControl', 'on'); %! vsol = ode2r (@frobertson, [0, 1e5], [1, 0, 0], vopt); %! plot (vsol.x, vsol.y); %!demo %! # Solve the "Robertson problem" with a Mass function that is given by %! # a constant mass matrix. %! %! function [vyd] = frobertson (vt, vy, varargin) %! vyd(1,1) = -0.04 * vy(1) + 1e4 * vy(2) * vy(3); %! vyd(2,1) = 0.04 * vy(1) - 1e4 * vy(2) * vy(3) - 3e7 * vy(2)^2; %! vyd(3,1) = vy(1) + vy(2) + vy(3) - 1; %! endfunction %! %! vopt = odeset ('Mass', [1, 0, 0; 0, 1, 0; 0, 0, 0], ... %! 'NormControl', 'on'); %! vsol = ode5r (@frobertson, [0, 1e5], [1, 0, 0], vopt); %! plot (vsol.x, vsol.y); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_examples_dde.m0000644000000000000000000001230412526637474016261 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} odepkg_examples_dde (@var{}) %# Open the DDE examples menu and allow the user to select a demo that will be evaluated. %# @end deftypefn function [] = odepkg_examples_dde () vode = 1; while (vode > 0) clc; fprintf (1, ... ['DDE examples menu:\n', ... '==================\n', ... '\n', ... ' (1) Solve a simple "exp(...)" example with solver "ode23d"\n', ... ' (2) Solve an example from Wille and Baker with solver "ode45d"\n', ... ' (3) Solve an example from Hu and Wang with solver "ode54d"\n', ... ' (4) Solve the "infectious disease model" with solver "ode78d"\n', ... '\n', ... ' Note: There are further DDE examples available with the OdePkg\n', ... ' testsuite functions.\n', ... '\n', ... ' If you have another interesting DDE example that you would like\n', ... ' to share then please modify this file, create a patch and send\n', ... ' your patch with your added example to the OdePkg developer team.\n', ... '\n' ]); vode = input ('Please choose a number from above or press to return: '); clc; if (vode > 0 && vode < 5) %# We can't use the function 'demo' directly here because it does %# not allow to run other functions within a demo. vexa = example (mfilename (), vode); disp (vexa); eval (vexa); input ('Press to continue: '); end %# if (vode > 0) end %# while (vode > 0) %!demo %! # Solves a simple example where the delay differential equation is %! # of the form yd = e^(-lambda*t) - y(t-tau). %! %! function [vyd] = fexp (vt, vy, vz, varargin) %! vlambda = varargin{1}; %! vyd = exp (- vlambda * vt) - vz(1); %! endfunction %! %! vtslot = [0, 15]; vlambda = 1; vinit = 10; %! vopt = odeset ('NormControl', 'on', 'RelTol', 1e-4, 'AbsTol', 1e-4); %! vsol = ode23d (@fexp, vtslot, vinit, vlambda, vinit, vopt, vlambda); %! plot (vsol.x, vsol.y); %!demo %! # Solves the example 3 from the publication 'DELSOL - a numerical %! # code for the solution of systems of delay-differential equations' %! # from the authors David Wille and Christopher Baker. %! %! function [vyd] = fdelsol (vt, vy, vz, varargin) %! %# vy is a column vector of size (3,1) %! %# vz is the history of size (3,2) %! vyd = [vz(1,1); vz(1,1) + vz(2,2); vy(2,1)]; %! endfunction %! %! vopt = odeset ('NormControl', 'on', 'MaxStep', 0.1, 'InitialStep', 0.01); %! vsol = ode45d (@fdelsol, [0, 5], [1, 1, 1], [1, 0.2], ones(3,2), vopt); %! plot (vsol.x, vsol.y); %!demo %! # Solves the examples 2.3.1 and 2.3.2 from the book 'Dynamics of %! # Controlled Mechanical Systems with Delayed Feedback' from the %! # authors Haiyan Hu and Zaihua Wang. %! %! function [vyd] = fhuwang1 (vt, vy, vz, varargin) %! %# vy is of size (1,1), vz is of size (1,1) %! vyd = (vz(1,1) - varargin{1})^(1/3); %! endfunction %! %! function [vyd] = fhuwang2 (vt, vy, vz, varargin) %! %# vy is of size (1,1), vz is of size (1,1) %! vyd = (vy - vz)^(1/3); %! endfunction %! %! vtslot = [0, 10]; vK = 1; vinit = 1; vhist = 0; %! vopt = odeset ('NormControl', 'on', 'RelTol', 1e-6, 'InitialStep', 0.1); %! %! vsol = ode54d (@fhuwang1, vtslot, vK, vinit, vhist, vopt, vK); %! plot (vsol.x, vsol.y, 'ko-', 'markersize', 1); hold; %! %! vsol = ode54d (@fhuwang2, vtslot, vK, vinit, vhist, vopt, vK); %! plot (vsol.x, vsol.y, 'bx-', 'markersize', 1); %!demo %! # Solves the infectious disease model from the book 'Solving Ordinary %! # Differential Equations 1' from the authors Ernst Hairer and Gerhard %! # Wanner. %! %! function [vyd] = finfect (vx, vy, vz, varargin) %! %# vy is of size (3,1), vz is of size (3,2) %! vyd = [ - vy(1) * vz(2,1) + vz(2,2); %! vy(1) * vz(2,1) - vy(2); %! vy(2) - vz(2,2) ]; %! endfunction %! %! function [vval, vtrm, vdir] = fevent (vx, vy, vz, varargin) %! %# vy is of size (3,1), vz is of size (3,2) %! vfec = finfect (vx, vy, vz); %! vval = vfec(2:3); %# Have a look at component two + three %! vtrm = zeros(1,2); %# Don't stop if an event is found %! vdir = -ones(1,2); %# Check only for falling direction %! endfunction %! %! vopt = odeset ('InitialStep', 1e-3, 'Events', @fevent); %! vsol = ode78d (@finfect, [0, 40], [5, 0.1, 1], [1, 10], ... %! [5, 5; 0.1, 0.1; 1, 1], vopt); %! plot (vsol.x, vsol.y, 'k-', vsol.xe, vsol.ye, 'ro'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_examples_ide.m0000644000000000000000000001042112526637474016264 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} odepkg_examples_ide (@var{}) %# Open the IDE examples menu and allow the user to select a demo that will be evaluated. %# @end deftypefn function [] = odepkg_examples_ide () vode = 1; while (vode > 0) clc; fprintf (1, ... ['IDE examples menu:\n', ... '==================\n', ... '\n', ... ' (1) Solve the "Robertson problem" with solver "odebdi"\n', ... ' (2) Solve another "Robertson implementation" with solver "odekdi"\n', ... ' (3) Solve a stiff "Van der Pol" implementation with solver "odebdi"\n', ... ' (4) Solve a stiff "Van der Pol" implementation with solver "odekdi"\n', ... '\n', ... ' Note: There are further IDE examples available with the OdePkg\n', ... ' testsuite functions.\n', ... '\n', ... ' If you have another interesting IDE example that you would like\n', ... ' to share then please modify this file, create a patch and send\n', ... ' your patch to the OdePkg developer team.\n', ... '\n' ]); vode = input ('Please choose a number from above or press to return: '); clc; if (vode > 0 && vode < 5) %# We can't use the function 'demo' directly here because it does %# not allow to run other functions within a demo. vexa = example (mfilename (), vode); disp (vexa); eval (vexa); input ('Press to continue: '); end %# if (vode > 0) end %# while (vode > 0) %!demo %! # Solve the "Robertson problem" that is given as a function handle %! # to an implicite differential equation implementation. %! %! function [vres] = firobertson (vt, vy, vyd, varargin) %! vres(1,1) = -0.04*vy(1) + 1e4*vy(2)*vy(3) - vyd(1); %! vres(2,1) = 0.04*vy(1) - 1e4*vy(2)*vy(3) - 3e7*vy(2)^2-vyd(2); %! vres(3,1) = vy(1) + vy(2) + vy(3) - 1; %! endfunction %! %! vopt = odeset ("NormControl", "on"); %! vsol = odebdi (@firobertson, [0, 1e5], [1, 0, 0], [-4e-2, 4e-2, 0], vopt); %! plot (vsol.x, vsol.y); %!demo %! # Solve the "Robertson problem" that is given as a function handle %! # to an implicite differential equation implementation. %! %! function [vres] = firobertson (vt, vy, vyd, varargin) %! vres(1,1) = -0.04*vy(1) + 1e4*vy(2)*vy(3) - vyd(1); %! vres(2,1) = 0.04*vy(1) - 1e4*vy(2)*vy(3) - 3e7*vy(2)^2-vyd(2); %! vres(3,1) = vy(1) + vy(2) + vy(3) - 1; %! endfunction %! %! vopt = odeset ("NormControl", "on"); %! vsol = odekdi (@firobertson, [0, 1e5], [1, 0, 0], [-4e-2, 4e-2, 0], vopt); %! plot (vsol.x, vsol.y); %!demo %! # Solve the "Van der Pol" problem that is given as a function %! # handle to an implicite differential equation implementation. %! %! function [vres] = fvanderpol (vt, vy, vyd, varargin) %! mu = varargin{1}; %! vres = [vy(2) - vyd(1); %! mu * (1 - vy(1)^2) * vy(2) - vy(1) - vyd(2)]; %! endfunction %! %! vopt = odeset ("NormControl", "on", "RelTol", 1e-8, "MaxStep", 1e-2); %! vsol = odebdi (@fvanderpol, [0, 20], [2; 0], [0; -2], vopt, 10); %! plot (vsol.x, vsol.y); %!demo %! # Solve the "Van der Pol" problem that is given as a function %! # handle to an implicite differential equation implementation. %! %! function [vres] = fvanderpol (vt, vy, vyd, varargin) %! mu = varargin{1}; %! vres = [vy(2) - vyd(1); %! mu * (1 - vy(1)^2) * vy(2) - vy(1) - vyd(2)]; %! endfunction %! %! vsol = odekdi (@fvanderpol, [0, 1000], [2; 0], [0; -2], 500); %! plot (vsol.x, vsol.y); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_examples_ode.m0000644000000000000000000001610212526637474016274 0ustar 00000000000000%# Copyright (C) 2008-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{}] =} odepkg_examples_ode (@var{}) %# Open the ODE examples menu and allow the user to select a demo that will be evaluated. %# @end deftypefn function [] = odepkg_examples_ode () vode = 1; while (vode > 0) clc; fprintf (1, ... ['ODE examples menu:\n', ... '==================\n', ... '\n', ... ' (1) Solve a non-stiff "Van der Pol" example with solver "ode78"\n', ... ' (2) Solve a "Van der Pol" example backward with solver "ode23"\n', ... ' (3) Solve a "Pendulous" example with solver "ode45"\n', ... ' (4) Solve the "Lorenz attractor" with solver "ode54"\n', ... ' (5) Solve the "Roessler equation" with solver "ode78"\n', ... '\n', ... ' Note: There are further ODE examples available with the OdePkg\n', ... ' testsuite functions.\n', ... '\n', ... ' If you have another interesting ODE example that you would like\n', ... ' to share then please modify this file, create a patch and send\n', ... ' your patch with your added example to the OdePkg developer team.\n', ... '\n' ]); vode = input ('Please choose a number from above or press to return: '); clc; if (vode > 0 && vode < 6) %# We can't use the function 'demo' directly here because it does %# not allow to run other functions within a demo. vexa = example (mfilename (), vode); disp (vexa); eval (vexa); input ('Press to continue: '); end %# if (vode > 0) end %# while (vode > 0) %!demo %! # In this example the non-stiff "Van der Pol" equation (mu = 1) is %! # solved and the results are displayed in a figure while solving. %! # Read about the Van der Pol oscillator at %! # http://en.wikipedia.org/wiki/Van_der_Pol_oscillator. %! %! function [vyd] = fvanderpol (vt, vy, varargin) %! mu = varargin{1}; %! vyd = [vy(2); mu * (1 - vy(1)^2) * vy(2) - vy(1)]; %! endfunction %! %! vopt = odeset ('RelTol', 1e-8); %! ode78 (@fvanderpol, [0 20], [2 0], vopt, 1); %!demo %! # In this example the non-stiff "Van der Pol" equation (mu = 1) is %! # solved in forward and backward direction and the results are %! # displayed in a figure after solving. Read about the Van der Pol %! # oscillator at http://en.wikipedia.org/wiki/Van_der_Pol_oscillator. %! %! function [ydot] = fpol (vt, vy, varargin) %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! endfunction %! %! vopt = odeset ('NormControl', 'on'); %! vsol = ode23 (@fpol, [0, 20], [2, 0], vopt); %! subplot (2, 3, 1); plot (vsol.x, vsol.y); %! vsol = ode23 (@fpol, [0:0.1:20], [2, 0], vopt); %! subplot (2, 3, 2); plot (vsol.x, vsol.y); %! vsol = ode23 (@fpol, [-20, 20], [-1.1222e-3, -0.2305e-3], vopt); %! subplot (2, 3, 3); plot (vsol.x, vsol.y); %! %! vopt = odeset ('NormControl', 'on'); %! vsol = ode23 (@fpol, [0:-0.1:-20], [2, 0], vopt); %! subplot (2, 3, 4); plot (vsol.x, vsol.y); %! vsol = ode23 (@fpol, [0, -20], [2, 0], vopt); %! subplot (2, 3, 5); plot (vsol.x, vsol.y); %! vsol = ode23 (@fpol, [20:-0.1:-20], [-2.0080, 0.0462], vopt); %! subplot (2, 3, 6); plot (vsol.x, vsol.y); %!demo %! # In this example a simple "pendulum with damping" is solved and the %! # results are displayed in a figure while solving. Read about the %! # pendulum with damping at %! # http://en.wikipedia.org/wiki/Pendulum %! %! function [vyd] = fpendulum (vt, vy) %! m = 1; %# The pendulum mass in kg %! g = 9.81; %# The gravity in m/s^2 %! l = 1; %# The pendulum length in m %! b = 0.7; %# The damping factor in kgm^2/s %! vyd = [vy(2,1); ... %! 1 / (1/3 * m * l^2) * (-b * vy(2,1) - m * g * l/2 * sin (vy(1,1)))]; %! endfunction %! %! vopt = odeset ('RelTol', 1e-3, 'OutputFcn', @odeplot); %! ode45 (@fpendulum, [0 5], [30*pi/180, 0], vopt); %!demo %! # In this example the "Lorenz attractor" implementation is solved %! # and the results are plot in a figure after solving. Read about %! # the Lorenz attractor at %! # http://en.wikipedia.org/wiki/Lorenz_equation %! # %! # The upper left subfigure shows the three results of the integration %! # over time. The upper right subfigure shows the force f in a two %! # dimensional (x,y) plane as well as the lower left subfigure shows %! # the force in the (y,z) plane. The three dimensional force is plot %! # in the lower right subfigure. %! %! function [vyd] = florenz (vt, vy) %! vyd = [10 * (vy(2) - vy(1)); %! vy(1) * (28 - vy(3)); %! vy(1) * vy(2) - 8/3 * vy(3)]; %! endfunction %! %! A = odeset ('InitialStep', 1e-3, 'MaxStep', 1e-1); %! [t, y] = ode54 (@florenz, [0 25], [3 15 1], A); %! %! subplot (2, 2, 1); grid ('on'); %! plot (t, y(:,1), '-b', t, y(:,2), '-g', t, y(:,3), '-r'); %! legend ('f_x(t)', 'f_y(t)', 'f_z(t)'); %! subplot (2, 2, 2); grid ('on'); %! plot (y(:,1), y(:,2), '-b'); %! legend ('f_{xyz}(x, y)'); %! subplot (2, 2, 3); grid ('on'); %! plot (y(:,2), y(:,3), '-b'); %! legend ('f_{xyz}(y, z)'); %! subplot (2, 2, 4); grid ('on'); %! plot3 (y(:,1), y(:,2), y(:,3), '-b'); %! legend ('f_{xyz}(x, y, z)'); %!demo %! # In this example the "Roessler attractor" implementation is solved %! # and the results are plot in a figure after solving. Read about %! # the Roessler attractor at %! # http://en.wikipedia.org/wiki/R%C3%B6ssler_attractor %! # %! # The upper left subfigure shows the three results of the integration %! # over time. The upper right subfigure shows the force f in a two %! # dimensional (x,y) plane as well as the lower left subfigure shows %! # the force in the (y,z) plane. The three dimensional force is plot %! # in the lower right subfigure. %! %! function [vyd] = froessler (vt, vx) %! vyd = [- ( vx(2) + vx(3) ); %! vx(1) + 0.2 * vx(2); %! 0.2 + vx(1) * vx(3) - 5.7 * vx(3)]; %! endfunction %! %! A = odeset ('MaxStep', 1e-1); %! [t, y] = ode78 (@froessler, [0 70], [0.1 0.3 0.1], A); %! %! subplot (2, 2, 1); grid ('on'); %! plot (t, y(:,1), '-b;f_x(t);', t, y(:,2), '-g;f_y(t);', \ %! t, y(:,3), '-r;f_z(t);'); %! subplot (2, 2, 2); grid ('on'); %! plot (y(:,1), y(:,2), '-b;f_{xyz}(x, y);'); %! subplot (2, 2, 3); grid ('on'); %! plot (y(:,2), y(:,3), '-b;f_{xyz}(y, z);'); %! subplot (2, 2, 4); grid ('on'); %! plot3 (y(:,1), y(:,2), y(:,3), '-b;f_{xyz}(x, y, z);'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_structure_check.m0000644000000000000000000003760612526637474017040 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{newstruct}] =} odepkg_structure_check (@var{oldstruct}, [@var{"solver"}]) %# %# If this function is called with one input argument of type structure array then check the field names and the field values of the OdePkg structure @var{oldstruct} and return the structure as @var{newstruct} if no error is found. Optionally if this function is called with a second input argument @var{"solver"} of type string taht specifies the name of a valid OdePkg solver then a higher level error detection is performed. The function does not modify any of the field names or field values but terminates with an error if an invalid option or value is found. %# %# This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. %# %# Run examples with the command %# @example %# demo odepkg_structure_check %# @end example %# @end deftypefn %# %# @seealso{odepkg} function [vret] = odepkg_structure_check (varargin) %# Check the number of input arguments if (nargin == 0) help ('odepkg_structure_check'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (nargin > 2) print_usage; elseif (nargin == 1 && isstruct (varargin{1})) vret = varargin{1}; vsol = ''; vfld = fieldnames (vret); vlen = length (vfld); elseif (nargin == 2 && isstruct (varargin{1}) && ischar (varargin{2})) vret = varargin{1}; vsol = varargin{2}; vfld = fieldnames (vret); vlen = length (vfld); end for vcntarg = 1:vlen %# Run through the number of given structure field names switch (vfld{vcntarg}) case 'RelTol' if (isnumeric (vret.(vfld{vcntarg})) && ... isreal (vret.(vfld{vcntarg})) && ... all (vret.(vfld{vcntarg}) > 0)) %# 'all' is a MatLab need else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end switch (vsol) case {'ode23', 'ode45', 'ode54', 'ode78', ... 'ode23d', 'ode45d', 'ode54d', 'ode78d',} if (~isscalar (vret.(vfld{vcntarg})) && ... ~isempty (vret.(vfld{vcntarg}))) error ('OdePkg:InvalidParameter', ... 'Value of option "RelTol" must be a scalar'); end otherwise end case 'AbsTol' if (isnumeric (vret.(vfld{vcntarg})) && ... isreal (vret.(vfld{vcntarg})) && ... all (vret.(vfld{vcntarg}) > 0)) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'NormControl' if (strcmp (vret.(vfld{vcntarg}), 'on') || ... strcmp (vret.(vfld{vcntarg}), 'off')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'NonNegative' if (isempty (vret.(vfld{vcntarg})) || ... (isnumeric (vret.(vfld{vcntarg})) && isvector (vret.(vfld{vcntarg})))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'OutputFcn' if (isempty (vret.(vfld{vcntarg})) || ... isa (vret.(vfld{vcntarg}), 'function_handle')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'OutputSel' if (isempty (vret.(vfld{vcntarg})) || ... (isnumeric (vret.(vfld{vcntarg})) && isvector (vret.(vfld{vcntarg}))) || ... isscalar (vret.(vfld{vcntarg}))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'OutputSave' if (isempty (vret.(vfld{vcntarg})) || ... (isscalar (vret.(vfld{vcntarg})) && ... mod (vret.(vfld{vcntarg}), 1) == 0 && ... vret.(vfld{vcntarg}) > 0) || ... vret.(vfld{vcntarg}) == Inf) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'Refine' if (isscalar (vret.(vfld{vcntarg})) && ... mod (vret.(vfld{vcntarg}), 1) == 0 && ... vret.(vfld{vcntarg}) >= 0 && ... vret.(vfld{vcntarg}) <= 5) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'Stats' if (strcmp (vret.(vfld{vcntarg}), 'on') || ... strcmp (vret.(vfld{vcntarg}), 'off')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'InitialStep' if (isempty (vret.(vfld{vcntarg})) || ... (isscalar (vret.(vfld{vcntarg})) && ... isreal (vret.(vfld{vcntarg})))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'MaxStep' if (isempty (vret.(vfld{vcntarg})) || ... (isscalar (vret.(vfld{vcntarg})) && ... vret.(vfld{vcntarg}) > 0) ) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'Events' if (isempty (vret.(vfld{vcntarg})) || ... isa (vret.(vfld{vcntarg}), 'function_handle')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'Jacobian' if (isempty (vret.(vfld{vcntarg})) || ... isnumeric (vret.(vfld{vcntarg})) || ... isa (vret.(vfld{vcntarg}), 'function_handle') || ... iscell (vret.(vfld{vcntarg}))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'JPattern' if (isempty (vret.(vfld{vcntarg})) || ... isvector (vret.(vfld{vcntarg})) || ... isnumeric (vret.(vfld{vcntarg}))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'Vectorized' if (strcmp (vret.(vfld{vcntarg}), 'on') || ... strcmp (vret.(vfld{vcntarg}), 'off')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'Mass' if (isempty (vret.(vfld{vcntarg})) || ... isnumeric (vret.(vfld{vcntarg})) || ... isa (vret.(vfld{vcntarg}), 'function_handle')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'MStateDependence' if (strcmp (vret.(vfld{vcntarg}), 'none') || ... strcmp (vret.(vfld{vcntarg}), 'weak') || ... strcmp (vret.(vfld{vcntarg}), 'strong')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'MvPattern' if (isempty (vret.(vfld{vcntarg})) || ... isvector (vret.(vfld{vcntarg})) || ... isnumeric (vret.(vfld{vcntarg}))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'MassSingular' if (strcmp (vret.(vfld{vcntarg}), 'yes') || ... strcmp (vret.(vfld{vcntarg}), 'no') || ... strcmp (vret.(vfld{vcntarg}), 'maybe')) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'InitialSlope' if (isempty (vret.(vfld{vcntarg})) || ... isvector (vret.(vfld{vcntarg}))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'MaxOrder' if (isempty (vret.(vfld{vcntarg})) || ... (mod (vret.(vfld{vcntarg}), 1) == 0 && ... vret.(vfld{vcntarg}) > 0 && ... vret.(vfld{vcntarg}) < 8)) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'BDF' if (isempty (vret.(vfld{vcntarg})) || ... (strcmp (vret.(vfld{vcntarg}), 'on') || ... strcmp (vret.(vfld{vcntarg}), 'off'))) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'NewtonTol' if (isnumeric (vret.(vfld{vcntarg})) && ... isreal (vret.(vfld{vcntarg})) && ... all (vret.(vfld{vcntarg}) > 0)) %# 'all' is a MatLab need else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end case 'MaxNewtonIterations' if (isempty (vret.(vfld{vcntarg})) || ... (mod (vret.(vfld{vcntarg}), 1) == 0 && ... vret.(vfld{vcntarg}) > 0)) else error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s" or no valid parameter value', ... vfld{vcntarg}); end otherwise error ('OdePkg:InvalidParameter', ... 'Unknown parameter name "%s"', ... vfld{vcntarg}); end %# switch end %# for %# The following line can be uncommented for a even higher level error %# detection %# if (vlen ~= 21) %# vmsg = sprintf ('Number of fields in structure must match 21'); %# error (vmsg); %# end %!test A = odeset ('RelTol', 1e-4); %!test A = odeset ('RelTol', [1e-4, 1e-3]); %!test A = odeset ('RelTol', []); %!error A = odeset ('RelTol', '1e-4'); %!test A = odeset ('AbsTol', 1e-4); %!test A = odeset ('AbsTol', [1e-4, 1e-3]); %!test A = odeset ('AbsTol', []); %!error A = odeset ('AbsTol', '1e-4'); %!test A = odeset ('NormControl', 'on'); %!test A = odeset ('NormControl', 'off'); %!error A = odeset ('NormControl', []); %!error A = odeset ('NormControl', '12'); %!test A = odeset ('NonNegative', 1); %!test A = odeset ('NonNegative', [1, 2, 3]); %!test A = odeset ('NonNegative', []); %!error A = odeset ('NonNegative', '12'); %!test A = odeset ('OutputFcn', @odeprint); %!test A = odeset ('OutputFcn', @odeplot); %!test A = odeset ('OutputFcn', []); %!error A = odeset ('OutputFcn', 'odeprint'); %!test A = odeset ('OutputSel', 1); %!test A = odeset ('OutputSel', [1, 2, 3]); %!test A = odeset ('OutputSel', []); %!error A = odeset ('OutputSel', '12'); %!test A = odeset ('Refine', 3); %!error A = odeset ('Refine', [1, 2, 3]); %!error A = odeset ('Refine', []); %!error A = odeset ('Refine', 6); %!test A = odeset ('Stats', 'on'); %!test A = odeset ('Stats', 'off'); %!error A = odeset ('Stats', []); %!error A = odeset ('Stats', '12'); %!test A = odeset ('InitialStep', 3); %!error A = odeset ('InitialStep', [1, 2, 3]); %!test A = odeset ('InitialStep', []); %!test A = odeset ('InitialStep', 6); %!test A = odeset ('MaxStep', 3); %!error A = odeset ('MaxStep', [1, 2, 3]); %!test A = odeset ('MaxStep', []); %!test A = odeset ('MaxStep', 6); %!test A = odeset ('Events', @demo); %!error A = odeset ('Events', 'off'); %!test A = odeset ('Events', []); %!error A = odeset ('Events', '12'); %!test A = odeset ('Jacobian', @demo); %!test A = odeset ('Jacobian', [1, 2; 3, 4]); %!test A = odeset ('Jacobian', []); %!error A = odeset ('Jacobian', '12'); %!test A = odeset ('JPattern', []); %!test A = odeset ('JPattern', [1, 2, 4]); %!test A = odeset ('JPattern', [1, 2; 3, 4]); %!test A = odeset ('JPattern', 1); %!test A = odeset ('Vectorized', 'on'); %!test A = odeset ('Vectorized', 'off'); %!error A = odeset ('Vectorized', []); %!error A = odeset ('Vectorized', '12'); %!test A = odeset ('Mass', @demo); %!test A = odeset ('Mass', [1, 2; 3, 4]); %!test A = odeset ('Mass', []); %!error A = odeset ('Mass', '12'); %!test A = odeset ('MStateDependence', 'none'); %!test A = odeset ('MStateDependence', 'weak'); %!test A = odeset ('MStateDependence', 'strong'); %!error A = odeset ('MStateDependence', [1, 2; 3, 4]); %!error A = odeset ('MStateDependence', []); %!error A = odeset ('MStateDependence', '12'); %!test A = odeset ('MvPattern', []); %!test A = odeset ('MvPattern', [1, 2, 3 ]); %!test A = odeset ('MvPattern', [1, 2; 3, 4]); %!test A = odeset ('MvPattern', 1); %!test A = odeset ('MassSingular', 'yes'); %!test A = odeset ('MassSingular', 'no'); %!test A = odeset ('MassSingular', 'maybe'); %!error A = odeset ('MassSingular', [1, 2; 3, 4]); %!error A = odeset ('MassSingular', []); %!error A = odeset ('MassSingular', '12'); %!test A = odeset ('InitialSlope', [1, 2, 3]); %!test A = odeset ('InitialSlope', 1); %!test A = odeset ('InitialSlope', []); %!test A = odeset ('InitialSlope', '12'); %!test A = odeset ('MaxOrder', 3); %!error A = odeset ('MaxOrder', 3.5); %!test A = odeset ('MaxOrder', [1, 2; 3, 4]); %!test A = odeset ('MaxOrder', []); %!test A = odeset ('BDF', 'on'); %!test A = odeset ('BDF', 'off'); %!test A = odeset ('BDF', []); %!error A = odeset ('BDF', [1, 2; 3, 4]); %!test A = odeset ('NewtonTol', []); %!test A = odeset ('NewtonTol', 1e-3); %!test A = odeset ('NewtonTol', [1e-3, 1e-3, 1e-3]); %!error A = odeset ('NewtonTol', 'string'); %!test A = odeset ('MaxNewtonIterations', []); %!test A = odeset ('MaxNewtonIterations', 2); %!error A = odeset ('MaxNewtonIterations', 'string'); %!demo %! # Return the checked OdePkg options structure that is created by %! # the command odeset. %! %! odepkg_structure_check (odeset); %! %!demo %! # Create the OdePkg options structure A with odeset and check it %! # with odepkg_structure_check. This actually is unnecessary %! # because odeset automtically calls odepkg_structure_check before %! # returning. %! %! A = odeset (); odepkg_structure_check (A); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_calcmescd.m0000644000000000000000000000554212526637474017664 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{mescd}] =} odepkg_testsuite_calcmescd (@var{solution}, @var{reference}, @var{abstol}, @var{reltol}) %# %# If this function is called with four input arguments of type double scalar or column vector then return a normalized value for the minimum number of correct digits @var{mescd} that is calculated from the solution at the end of an integration interval @var{solution} and a set of reference values @var{reference}. The input arguments @var{abstol} and @var{reltol} are used to calculate a reference solution that depends on the relative and absolute error tolerances. %# %# Run examples with the command %# @example %# demo odepkg_testsuite_calcmescd %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vmescd = odepkg_testsuite_calcmescd (vsol, vref, vatol, vrtol) vsol = vsol(:); vref = vref(:); vatol = vatol(:); vrtol = vrtol(:); vtmp = (vref - vsol) ./ (vatol ./ vrtol + vref); vmescd = -log10 (norm (vtmp, inf)); %!demo %! # Display the value for the mimum number of correct digits in the vector %! # sol = [1, 2, 2.9] compared to the reference vector ref = [1, 2, 3]. %! %! odepkg_testsuite_calcmescd ([1, 2, 2.9], [1, 2, 3], 1e-3, 1e-6) %! %!demo %! # Display the value for the mimum number of correct digits in the vector %! # sol = [1 + 1e10, 2 + 1e10, 3 + 1e10] compared to the reference vector %! # ref = [1, 2, 3]. %! %! odepkg_testsuite_calcmescd ([1 + 1e10, 2 + 1e10, 3 + 1e10], [1, 2, 3], ... %! [1e-3, 1e-4, 1e-5], [1e-6, 1e-6, 1e-6]) %!assert (odepkg_testsuite_calcmescd ([1, 2, 3], [1, 2, 3], NaN, NaN), Inf); %!assert (odepkg_testsuite_calcmescd ([1, 2, 3], [1, 2, 3], 1, 1), Inf); %!assert (odepkg_testsuite_calcmescd ([1, 2, 3], [1, 2.1, 3], 1, 1), 1.5, 0.1); %!assert (odepkg_testsuite_calcmescd ([1, 2, 3], [1+1e-6, 2, 3], 1, 1), 6.5, 0.2); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_calcscd.m0000644000000000000000000000526512526637474017344 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{scd}] =} odepkg_testsuite_calcscd (@var{solution}, @var{reference}, @var{abstol}, @var{reltol}) %# %# If this function is called with four input arguments of type double scalar or column vector then return a normalized value for the minimum number of correct digits @var{scd} that is calculated from the solution at the end of an integration interval @var{solution} and a set of reference values @var{reference}. The input arguments @var{abstol} and @var{reltol} are unused but present because of compatibility to the function @command{odepkg_testsuite_calcmescd}. %# %# Run examples with the command %# @example %# demo odepkg_testsuite_calcscd %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vscd = odepkg_testsuite_calcscd (vsol, vref, vatol, vrtol) vsol = vsol(:); vref = vref(:); vrel = (vref - vsol) / vref; vscd = -log10 (norm (vrel, inf)); %!demo %! # Displays the value for the mimum number of correct digits in %! # the vector sol = [1, 2, 2.9] compared to the reference vector %! # ref = [1, 2, 3]. %! %! odepkg_testsuite_calcscd ([1, 2, 2.9], [1, 2, 3], NaN, NaN) %! %!demo %! # Displays the value for the mimum number of correct digits in %! # the vector sol = [1, 2, 2.9] compared to the reference vector %! # ref = [1, 2, 3]. %! %! odepkg_testsuite_calcscd ([1 + 1e10, 2 + 1e10, 3 + 1e10], ... %! [1, 2, 3], NaN, NaN) %!assert (odepkg_testsuite_calcscd ([1, 2, 3], [1, 2, 3], NaN, NaN), Inf); %!assert (odepkg_testsuite_calcscd ([1, 2, 3], [1, 2.1, 3], 1, 1), 1.5, 0.2); %!assert (odepkg_testsuite_calcscd ([1, 2, 3], [1+1e-6, 2, 3], 1, 1), 6.5, 0.2); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_chemakzo.m0000644000000000000000000001604612526637474017550 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_chemakzo (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the chemical AKZO Nobel testsuite of differential algebraic equations after solving (DAE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_chemakzo %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_chemakzo (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_chemakzo'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2}; %# The value for the absolute tolerance vret{4} = vret{2}; %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite AKZO, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 180.0; %# The point of time when solving is stoped vinit = odepkg_testsuite_chemakzoinit; %# The initial values vmass = odepkg_testsuite_chemakzomass; %# The mass matrix vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_chemakzojac, 'Mass', vmass, ... 'MaxStep', vstop-vstart); %# , 'OutputFcn', @odeplot); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_chemakzofun, ... [vstart, vstop], vinit, vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_chemakzoref; %# Get the reference solution vector if (max (size (vsol.y(end,:))) == max (size (vref))), vlst = vsol.y(end,:); elseif (max (size (vsol.y(:,end))) == max (size (vref))), vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Returns the results for the for the chemical AKZO problem function f = odepkg_testsuite_chemakzofun (t, y, varargin) k1 = 18.7; k2 = 0.58; k3 = 0.09; k4 = 0.42; kbig = 34.4; kla = 3.3; ks = 115.83; po2 = 0.9; hen = 737; %# if (y(2) <= 0) %# error ('odepkg_testsuite_chemakzojac: Second input argument is negative'); %# end r1 = k1 * y(1)^4 * sqrt (y(2)); r2 = k2 * y(3) * y(4); r3 = k2 / kbig * y(1) * y(5); r4 = k3 * y(1) * y(4)^2; r5 = k4 * y(6)^2 * sqrt (y(2)); fin = kla * (po2 / hen - y(2)); f(1,1) = -2 * r1 + r2 - r3 - r4; f(2,1) = -0.5 * r1 - r4 - 0.5 * r5 + fin; f(3,1) = r1 - r2 + r3; f(4,1) = - r2 + r3 - 2 * r4; f(5,1) = r2 - r3 + r5; f(6,1) = ks * y(1) * y(4) - y(6); %# Returns the INITIAL values for the chemical AKZO problem function vinit = odepkg_testsuite_chemakzoinit () vinit = [0.444, 0.00123, 0, 0.007, 0, 115.83 * 0.444 * 0.007]; %# Returns the JACOBIAN matrix for the chemical AKZO problem function dfdy = odepkg_testsuite_chemakzojac (t, y, varargin) k1 = 18.7; k2 = 0.58; k3 = 0.09; k4 = 0.42; kbig = 34.4; kla = 3.3; ks = 115.83; po2 = 0.9; hen = 737; %# if (y(2) <= 0) %# error ('odepkg_testsuite_chemakzojac: Second input argument is negative'); %# end dfdy = zeros (6, 6); r11 = 4 * k1 * y(1)^3 * sqrt (y(2)); r12 = 0.5 * k1 * y(1)^4 / sqrt (y(2)); r23 = k2 * y(4); r24 = k2 * y(3); r31 = (k2 / kbig) * y(5); r35 = (k2 / kbig) * y(1); r41 = k3 * y(4)^2; r44 = 2 * k3 * y(1) * y(4); r52 = 0.5 * k4 * y(6)^2 / sqrt (y(2)); r56 = 2 * k4 * y(6) * sqrt (y(2)); fin2 = -kla; dfdy(1,1) = -2 * r11 - r31 - r41; dfdy(1,2) = -2 * r12; dfdy(1,3) = r23; dfdy(1,4) = r24 - r44; dfdy(1,5) = -r35; dfdy(2,1) = -0.5 * r11 - r41; dfdy(2,2) = -0.5 * r12 - 0.5 * r52 + fin2; dfdy(2,4) = -r44; dfdy(2,6) = -0.5 * r56; dfdy(3,1) = r11 + r31; dfdy(3,2) = r12; dfdy(3,3) = -r23; dfdy(3,4) = -r24; dfdy(3,5) = r35; dfdy(4,1) = r31 - 2 * r41; dfdy(4,3) = -r23; dfdy(4,4) = -r24 - 2 * r44; dfdy(4,5) = r35; dfdy(5,1) = -r31; dfdy(5,2) = r52; dfdy(5,3) = r23; dfdy(5,4) = r24; dfdy(5,5) = -r35; dfdy(5,6) = r56; dfdy(6,1) = ks * y(4); dfdy(6,4) = ks * y(1); dfdy(6,6) = -1; %# Returns the MASS matrix for the chemical AKZO problem function mass = odepkg_testsuite_chemakzomass (t, y, varargin) mass = [ 1, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0; 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0 ]; %# Returns the REFERENCE values for the chemical AKZO problem function y = odepkg_testsuite_chemakzoref () y(1,1) = 0.11507949206617e+0; y(2,1) = 0.12038314715677e-2; y(3,1) = 0.16115628874079e+0; y(4,1) = 0.36561564212492e-3; y(5,1) = 0.17080108852644e-1; y(6,1) = 0.48735313103074e-2; %!demo %! vsolver = {@odebda, @oders, @ode2r, @ode5r, @odesx}; %! for vcnt=1:length (vsolver) %! vakzo{vcnt,1} = odepkg_testsuite_chemakzo (vsolver{vcnt}, 1e-7); %! end %! vakzo %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_hires.m0000644000000000000000000001336512526637474017062 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_hires (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the HIRES testsuite of ordinary differential equations after solving (ODE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_hires %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_hires (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_hires'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2}; %# The value for the absolute tolerance vret{4} = vret{2} * 10^(-2); %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite HIRES, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 321.8122; %# The point of time when solving is stoped vinit = odepkg_testsuite_hiresinit; %# The initial values vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_hiresjac, 'MaxStep', vstop-vstart); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_hiresfun, ... [vstart, vstop], vinit, vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_hiresref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Returns the results for the HIRES problem function f = odepkg_testsuite_hiresfun (t, y, varargin) f(1,1) = -1.71 * y(1) + 0.43 * y(2) + 8.32 * y(3) + 0.0007; f(2,1) = 1.71 * y(1) - 8.75 * y(2); f(3,1) = -10.03 * y(3) + 0.43 * y(4) + 0.035 * y(5); f(4,1) = 8.32 * y(2) + 1.71 * y(3) - 1.12 * y(4); f(5,1) = -1.745 * y(5) + 0.43 * (y(6) + y(7)); f(6,1) = -280 * y(6) * y(8) + 0.69 * y(4) + 1.71 * y(5) - 0.43 * y(6) + 0.69 * y(7); f(7,1) = 280 * y(6) * y(8) - 1.81 * y(7); f(8,1) = -f(7); %# Returns the INITIAL values for the HIRES problem function vinit = odepkg_testsuite_hiresinit () vinit = [1, 0, 0, 0, 0, 0, 0, 0.0057]; %# Returns the JACOBIAN matrix for the HIRES problem function dfdy = odepkg_testsuite_hiresjac (t, y, varargin) dfdy(1,1) = -1.71; dfdy(1,2) = 0.43; dfdy(1,3) = 8.32; dfdy(2,1) = 1.71; dfdy(2,2) = -8.75; dfdy(3,3) = -10.03; dfdy(3,4) = 0.43; dfdy(3,5) = 0.035; dfdy(4,2) = 8.32; dfdy(4,3) = 1.71; dfdy(4,4) = -1.12; dfdy(5,5) = -1.745; dfdy(5,6) = 0.43; dfdy(5,7) = 0.43; dfdy(6,4) = 0.69; dfdy(6,5) = 1.71; dfdy(6,6) = -280 * y(8) - 0.43; dfdy(6,7) = 0.69; dfdy(6,8) = -280 * y(6); dfdy(7,6) = 280 * y(8); dfdy(7,7) = -1.81; dfdy(7,8) = 280 * y(6); dfdy(8,6) = -280 * y(8); dfdy(8,7) = 1.81; dfdy(8,8) = -280 * y(6); %# Returns the REFERENCE values for the HIRES problem function y = odepkg_testsuite_hiresref () y(1,1) = 0.73713125733256e-3; y(2,1) = 0.14424857263161e-3; y(3,1) = 0.58887297409675e-4; y(4,1) = 0.11756513432831e-2; y(5,1) = 0.23863561988313e-2; y(6,1) = 0.62389682527427e-2; y(7,1) = 0.28499983951857e-2; y(8,1) = 0.28500016048142e-2; %!demo %! vsolver = {@ode23, @ode45, @ode54, @ode78, ... %! @odebda, @oders, @ode2r, @ode5r, @odesx}; %! for vcnt=1:length (vsolver) %! vhires{vcnt,1} = odepkg_testsuite_hires (vsolver{vcnt}, 1e-7); %! end %! vhires %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_implakzo.m0000644000000000000000000001642112526637474017572 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_implakzo (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the chemical AKZO Nobel testsuite of implicit differential algebraic equations after solving (IDE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_implakzo %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_implakzo (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_implakzo'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2}; %# The value for the absolute tolerance vret{4} = vret{2}; %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite AKZO, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 180.0; %# The point of time when solving is stoped [vinity, vinityd] = odepkg_testsuite_implakzoinit; %# The initial values vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_implakzojac, 'MaxStep', vstop-vstart); %# ,'OutputFcn', @odeplot, 'MaxStep', 1); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_implakzofun, ... [vstart, vstop], vinity, vinityd', vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_implakzoref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Return the results for the for the chemical AKZO problem function res = odepkg_testsuite_implakzofun (t, y, yd, varargin) k1 = 18.7; k2 = 0.58; k3 = 0.09; k4 = 0.42; kbig = 34.4; kla = 3.3; ks = 115.83; po2 = 0.9; hen = 737; r1 = k1 * y(1)^4 * sqrt (y(2)); r2 = k2 * y(3) * y(4); r3 = k2 / kbig * y(1) * y(5); r4 = k3 * y(1) * y(4)^2; r5 = k4 * y(6)^2 * sqrt (y(2)); fin = kla * (po2 / hen - y(2)); res(1,1) = -2 * r1 + r2 - r3 - r4 - yd(1); res(2,1) = -0.5 * r1 - r4 - 0.5 * r5 + fin - yd(2); res(3,1) = r1 - r2 + r3 - yd(3); res(4,1) = - r2 + r3 - 2 * r4 - yd(4); res(5,1) = r2 - r3 + r5 - yd(5); res(6,1) = ks * y(1) * y(4) - y(6) - yd(6); %# Return the INITIAL values for the chemical AKZO problem function [y0, yd0] = odepkg_testsuite_implakzoinit () y0 = [0.444, 0.00123, 0, 0.007, 0, 115.83 * 0.444 * 0.007]; yd0 = [-0.051, -0.014, 0.025, 0, 0.002, 0]; %# Return the JACOBIAN matrix for the chemical AKZO problem function [dfdy, dfdyd] = odepkg_testsuite_implakzojac (t, y, varargin) k1 = 18.7; k2 = 0.58; k3 = 0.09; k4 = 0.42; kbig = 34.4; kla = 3.3; ks = 115.83; po2 = 0.9; hen = 737; %# if (y(2) <= 0) %# error ('odepkg_testsuite_implakzojac: Second input argument is negative'); %# end dfdy = zeros (6, 6); r11 = 4 * k1 * y(1)^3 * sqrt (y(2)); r12 = 0.5 * k1 * y(1)^4 / sqrt (y(2)); r23 = k2 * y(4); r24 = k2 * y(3); r31 = (k2 / kbig) * y(5); r35 = (k2 / kbig) * y(1); r41 = k3 * y(4)^2; r44 = 2 * k3 * y(1) * y(4); r52 = 0.5 * k4 * y(6)^2 / sqrt (y(2)); r56 = 2 * k4 * y(6) * sqrt (y(2)); fin2 = -kla; dfdy(1,1) = -2 * r11 - r31 - r41; dfdy(1,2) = -2 * r12; dfdy(1,3) = r23; dfdy(1,4) = r24 - r44; dfdy(1,5) = -r35; dfdy(2,1) = -0.5 * r11 - r41; dfdy(2,2) = -0.5 * r12 - 0.5 * r52 + fin2; dfdy(2,4) = -r44; dfdy(2,6) = -0.5 * r56; dfdy(3,1) = r11 + r31; dfdy(3,2) = r12; dfdy(3,3) = -r23; dfdy(3,4) = -r24; dfdy(3,5) = r35; dfdy(4,1) = r31 - 2 * r41; dfdy(4,3) = -r23; dfdy(4,4) = -r24 - 2 * r44; dfdy(4,5) = r35; dfdy(5,1) = -r31; dfdy(5,2) = r52; dfdy(5,3) = r23; dfdy(5,4) = r24; dfdy(5,5) = -r35; dfdy(5,6) = r56; dfdy(6,1) = ks * y(4); dfdy(6,4) = ks * y(1); dfdy(6,6) = -1; dfdyd = - [ 1, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0; 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 1 ]; %# For the implicit form of the chemical AKZO Nobel problem a mass %# matrix is not needed. This mass matrix is needed if the problem %# is formulated in explicit form (cf. odepkg_testsuite_cemakzo.m). %# function mass = odepkg_testsuite_implakzomass (t, y, varargin) %# mass = [ 1, 0, 0, 0, 0, 0; %# 0, 1, 0, 0, 0, 0; %# 0, 0, 1, 0, 0, 0; %# 0, 0, 0, 1, 0, 0; %# 0, 0, 0, 0, 1, 0; %# 0, 0, 0, 0, 0, 0 ]; %# Return the REFERENCE values for the chemical AKZO problem function y = odepkg_testsuite_implakzoref () y(1,1) = 0.11507949206617e+0; y(2,1) = 0.12038314715677e-2; y(3,1) = 0.16115628874079e+0; y(4,1) = 0.36561564212492e-3; y(5,1) = 0.17080108852644e-1; y(6,1) = 0.48735313103074e-2; %!demo %! vsolver = {@odebdi}; %! for vcnt=1:length (vsolver) %! vakzo{vcnt,1} = odepkg_testsuite_implakzo (vsolver{vcnt}, 1e-7); %! end %! vakzo %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_implrober.m0000644000000000000000000001376212526637474017744 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_implrober (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the implicit form of the modified ROBERTSON testsuite of implicit differential algebraic equations after solving (IDE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_implrober %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_implrober (vhandle, vrtol) %# Check number and types of all input arguments if (nargin ~= 2) help ('odepkg_testsuite_implrober'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2} * 1e-2; %# The value for the absolute tolerance vret{4} = vret{2}; %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all present solvers fprintf (1, ['Testsuite implicit ROBERTSON, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithm options vstart = 0; %# The point of time when solving is started vstop = 1e11; %# The point of time when solving is stoped [vinity, vinityd] = odepkg_testsuite_implroberinit; %# The initial values vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_implroberjac, 'MaxStep', vstop-vstart); %# 'OutputFcn', @odeplot); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_implroberfun, ... [vstart, vstop], vinity, vinityd', vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_implroberref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %#function odepkg_testsuite_implrober () %# A = odeset ('RelTol', 1e-4, ... %# proprietary ode15i needs 1e-6 to be stable %# 'AbsTol', [1e-6, 1e-10, 1e-6], ... %# 'Jacobian', @odepkg_testsuite_implroberjac); %# [y0, yd0] = odepkg_testsuite_implroberinit; %# odebdi (@odepkg_testsuite_implroberfun, [0, 1e11], y0, yd0', A) %# [y0, yd0] = odepkg_testsuite_implroberinit; %# odebdi (@odepkg_testsuite_implroberfun, [0, 1e11], y0, yd0') %# Return the results for the for the implicit ROBERTSON problem function res = odepkg_testsuite_implroberfun (t, y, yd, varargin) res(1,1) = -0.04 * y(1) + 1e4 * y(2) * y(3) - yd(1); res(2,1) = 0.04 * y(1) - 1e4 * y(2) * y(3) - 3e7 * y(2)^2 - yd(2); res(3,1) = y(1) + y(2) + y(3) - 1; %# Return the INITIAL values for the implicit ROBERTSON problem function [y0, yd0] = odepkg_testsuite_implroberinit () y0 = [1, 0, 0]; yd0 = [-4e-2, 4e-2, 0]; %# Return the JACOBIAN matrix for the implicit ROBERTSON problem function [dfdy, dfdyd] = odepkg_testsuite_implroberjac (t, y, yd, varargin) dfdy(1,1) = -0.04; dfdy(1,2) = 1e4 * y(3); dfdy(1,3) = 1e4 * y(2); dfdy(2,1) = 0.04; dfdy(2,2) = -1e4 * y(3) - 6e7 * y(2); dfdy(2,3) = -1e4 * y(2); dfdy(3,1) = 1; dfdy(3,2) = 1; dfdy(3,3) = 1; dfdyd(1,1) = -1; dfdyd(2,2) = -1; dfdyd(3,3) = 0; %# For the implicit form of the Robertson problem a mass matrix is not %# allowed. This mass matrix is only needed if the Robertson problem %# is formulated in explicit form (cf. odepkg_testsuite_implrober.m). %# function mass = odepkg_testsuite_implrobermass (t, y, varargin) %# mass = [1, 0, 0; 0, 1, 0; 0, 0, 0]; %# Return the REFERENCE values for the implicit ROBERTSON problem function y = odepkg_testsuite_implroberref () y(1) = 0.20833401497012e-07; y(2) = 0.83333607703347e-13; y(3) = 0.99999997916650e+00; %!demo %! vsolver = {@odebdi}; %! for vcnt=1:length (vsolver) %! virob{vcnt,1} = odepkg_testsuite_implrober (vsolver{vcnt}, 1e-7); %! end %! virob %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_impltrans.m0000644000000000000000000001775512526637474017770 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_impltrans (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return the cell array @var{solution} with performance informations about the TRANSISTOR testsuite of implicit differential algebraic equations after solving (IDE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_impltrans %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_impltrans (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_impltrans'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2}; %# The value for the absolute tolerance vret{4} = vret{2}; %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite TRANSISTOR, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 0.2; %# The point of time when solving is stoped [vinity, vinityd] = odepkg_testsuite_impltransinit; %# The initial values vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_impltransjac, 'MaxStep', vstop-vstart); %# ,'OutputFcn', @odeplot, 'MaxStep', 1e-4); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_impltransfun, ... [vstart, vstop], vinity, vinityd', vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_impltransref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Returns the results for the implicit TRANSISTOR problem function res = odepkg_testsuite_impltransfun (t, y, yd, varargin) ub = 6; uf = 0.026; alpha = 0.99; beta = 1e-6; r0 = 1000; r1 = 9000; r2 = 9000; r3 = 9000; r4 = 9000; r5 = 9000; r6 = 9000; r7 = 9000; r8 = 9000; r9 = 9000; c1 = 1e-6; c2 = 2e-6; c3 = 3e-6; c4 = 4e-6; c5 = 5e-6; uet = 0.1 * sin(200 * pi * t); fac1 = beta * (exp((y(2) - y(3)) / uf) - 1); fac2 = beta * (exp((y(5) - y(6)) / uf) - 1); res(1,1) = (y(1) - uet) / r0 + c1 * yd(1) - c1 * yd(2); res(2,1) = y(2) / r1 + (y(2) - ub) / r2 + (1 - alpha) * fac1 - c1 * yd(1) + c1 * yd(2); res(3,1) = y(3) / r3 - fac1 + c2 * yd(3); res(4,1) = (y(4) - ub) / r4 + alpha * fac1 + c3 * yd(4) - c3 * yd(5); res(5,1) = y(5) / r5 + (y(5) - ub) / r6 + (1 - alpha) * fac2 - c3 * yd(4) + c3 * yd(5); res(6,1) = y(6) / r7 - fac2 + c4 * yd(6); res(7,1) = (y(7) - ub) / r8 + alpha * fac2 + c5 * yd(7) - c5 * yd(8); res(8,1) = y(8) / r9 - c5 * yd(7) + c5 * yd(8); %# Returns the INITIAL values for the TRANSISTOR problem function [y0, yd0] = odepkg_testsuite_impltransinit () y0 = [0, 3, 3, 6, 3, 3, 6, 0]; yd0 = 1e-3 * [0, 0, 1/3, 0, 0, 1/3, 0, 0]; %# yd0 = [ 51.338775, 51.338775, -166.666666, -24.975766, ... %# -24.975766, -83.333333, -10.0056445, -10.005644]; %# Returns the JACOBIAN matrix for the TRANSISTOR problem function [dfdy, dfdyd] = odepkg_testsuite_impltransjac (t, y, varargin) ub = 6; uf = 0.026; alpha = 0.99; beta = 1e-6; r0 = 1000; r1 = 9000; r2 = 9000; r3 = 9000; r4 = 9000; r5 = 9000; r6 = 9000; r7 = 9000; r8 = 9000; r9 = 9000; c1 = 1e-6; c2 = 2e-6; c3 = 3e-6; c4 = 4e-6; c5 = 5e-6; fac1p = beta * exp ((y(2) - y(3)) / uf) / uf; fac2p = beta * exp ((y(5) - y(6)) / uf) / uf; dfdy(1,1) = 1 / r0; dfdy(2,2) = 1 / r1 + 1 / r2 + (1 - alpha) * fac1p; dfdy(2,3) = - (1 - alpha) * fac1p; dfdy(3,2) = - fac1p; dfdy(3,3) = 1 / r3 + fac1p; dfdy(4,2) = alpha * fac1p; dfdy(4,3) = - alpha * fac1p; dfdy(4,4) = 1 / r4; dfdy(5,5) = 1 / r5 + 1 / r6 + (1 - alpha) * fac2p; dfdy(5,6) = - (1 - alpha) * fac2p; dfdy(6,5) = - fac2p; dfdy(6,6) = 1 / r7 + fac2p; dfdy(7,5) = alpha * fac2p; dfdy(7,6) = - alpha * fac2p; dfdy(7,7) = 1 / r8; dfdy(8,8) = 1 / r9; dfdyd = [ c1, -c1, 0, 0, 0, 0, 0, 0; ... -c1, c1, 0, 0, 0, 0, 0, 0; ... 0, 0, c2, 0, 0, 0, 0, 0; ... 0, 0, 0, c3, -c3, 0, 0, 0; ... 0, 0, 0, -c3, c3, 0, 0, 0; ... 0, 0, 0, 0, 0, c4, 0, 0; ... 0, 0, 0, 0, 0, 0, c5, -c5; ... 0, 0, 0, 0, 0, 0, -c5, c5]; %# For the implicit form of the TRANSISTOR problem a mass matrix is %# not needed. This mass matrix is needed if the problem is formulated %# in explicit form (cf. odepkg_testsuite_transistor.m). %# function mass = odepkg_testsuite_impltransmass (t, y, varargin) %# mass = [-1e-6, 1e-6, 0, 0, 0, 0, 0, 0; ... %# 1e-6, -1e-6, 0, 0, 0, 0, 0, 0; ... %# 0, 0, -2e-6, 0, 0, 0, 0, 0; ... %# 0, 0, 0, -3e-6, 3e-6, 0, 0, 0; ... %# 0, 0, 0, 3e-6, -3e-6, 0, 0, 0; ... %# 0, 0, 0, 0, 0, -4e-6, 0, 0; ... %# 0, 0, 0, 0, 0, 0, -5e-6, 5e-6; ... %# 0, 0, 0, 0, 0, 0, 5e-6, -5e-6]; %# Returns the REFERENCE values for the TRANSISTOR problem function y = odepkg_testsuite_impltransref () y(1,1) = -0.55621450122627e-2; y(2,1) = 0.30065224719030e+1; y(3,1) = 0.28499587886081e+1; y(4,1) = 0.29264225362062e+1; y(5,1) = 0.27046178650105e+1; y(6,1) = 0.27618377783931e+1; y(7,1) = 0.47709276316167e+1; y(8,1) = 0.12369958680915e+1; %!demo %! vsolver = {@odebdi}; %! for vcnt=1:length (vsolver) %! vtrans{vcnt,1} = odepkg_testsuite_impltrans (vsolver{vcnt}, 1e-7); %! end %! vtrans %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_oregonator.m0000644000000000000000000001233412526637474020122 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_oregonator (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the OREGONATOR testsuite of ordinary differential equations after solving (ODE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_oregonator %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_oregonator (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_oregonator'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) usage ('odepkg_testsuite_oregonator (@solver, reltol)'); end vret{1} = vhandle; %# The name for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2}; %# The value for the absolute tolerance vret{4} = vret{2} * 10^(-2); %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite OREGONATOR, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 360.0; %# The point of time when solving is stoped vinit = odepkg_testsuite_oregonatorinit; %# The initial values vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_oregonatorjac, 'MaxStep', vstop-vstart); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_oregonatorfun, ... [vstart, vstop], vinit, vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_oregonatorref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Returns the results for the OREGONATOR problem function f = odepkg_testsuite_oregonatorfun (t, y, varargin) f(1,1) = 77.27 * (y(2) + y(1) * (1.0 - 8.375e-6 * y(1) - y(2))); f(2,1) = (y(3) - (1.0 + y(1)) * y(2)) / 77.27; f(3,1) = 0.161 * (y(1) - y(3)); %# Returns the INITIAL values for the OREGONATOR problem function vinit = odepkg_testsuite_oregonatorinit () vinit = [1, 2, 3]; %# Returns the JACOBIAN matrix for the OREGONATOR problem function dfdy = odepkg_testsuite_oregonatorjac (t, y, varargin) dfdy(1,1) = 77.27 * (1.0 - 2.0 * 8.375e-6 * y(1) - y(2)); dfdy(1,2) = 77.27 * (1.0 - y(1)); dfdy(1,3) = 0.0; dfdy(2,1) = -y(2) / 77.27; dfdy(2,2) = -(1.0 + y(1)) / 77.27; dfdy(2,3) = 1.0 / 77.27; dfdy(3,1) = 0.161; dfdy(3,2) = 0.0; dfdy(3,3) = -0.161; %# Returns the REFERENCE values for the OREGONATOR problem function y = odepkg_testsuite_oregonatorref () y(1,1) = 0.10008148703185e+1; y(1,2) = 0.12281785215499e+4; y(1,3) = 0.13205549428467e+3; %!demo %! %% vsolver = {@ode23, @ode45, @ode54, @ode78, ... %! %% @odebda, @oders, @ode2r, @ode5r, @odesx}; %! vsolver = {@odebda, @oders, @ode2r, @ode5r, @odesx}; %! for vcnt=1:length (vsolver) %! voreg{vcnt,1} = odepkg_testsuite_oregonator (vsolver{vcnt}, 1e-7); %! end %! voreg %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_pollution.m0000644000000000000000000002455012526637474017773 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_pollution (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return the cell array @var{solution} with performance informations about the POLLUTION testsuite of ordinary differential equations after solving (ODE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_pollution %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_pollution (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_pollution'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2}; %# The value for the absolute tolerance vret{4} = vret{2}; %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite POLLUTION, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 60.0; %# The point of time when solving is stoped vinit = odepkg_testsuite_pollutioninit; %# The initial values vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_pollutionjac, 'MaxStep', vstop-vstart); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_pollutionfun, ... [vstart, vstop], vinit, vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_pollutionref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Returns the results for the for the POLLUTION problem function f = odepkg_testsuite_pollutionfun (t, y, varargin) f(01,1) = - 0.350 * y(1) + 0.266e2 * y(2) * y(4) + 0.123e5 * y(2) * y(5) + ... 0.165e5 * y(2) * y(11) - 0.900e4 * y(1) * y(11) + 0.220e-1 * y(13) + ... 0.120e5 * y(2) * y(10) - 0.163e5 * y(1) * y(6) + 0.578e1 * y(19) - ... 0.474e-1 * y(1) * y(4) - 0.178e4 * y(1) * y(19) + 0.312e1 * y(20); f(02,1) = + 0.350 * y(1) - 0.266e2 * y(2) * y(4) - 0.123e5 * y(2) * y(5) - ... 0.165e5 * y(2) * y(11) - 0.120e5 * y(2) * y(10) + 0.210e1 * y(19); f(03,1) = + 0.350 * y(1) - 0.480e7 * y(3) + 0.175e-1 * y(4) + 0.444e12 * y(16) + 0.578e1 * y(19); f(04,1) = - 0.266e2 * y(2) * y(4) + 0.480e7 * y(3) - 0.350e-3 * y(4) - ... 0.175e-1 * y(4) - 0.474e-1 * y(1) * y(4); f(05,1) = - 0.123e5 * y(2) * y(5) + 2*0.860e-3 * y(7) + 0.150e5 * y(6) * y(7) + ... 0.130e-3 * y(9) + 0.188e1 * y(14) + 0.124e4 * y(6) * y(17); f(06,1) = + 0.123e5 * y(2) * y(5) - 0.150e5 * y(6) * y(7) - 0.240e5 * y(6) * y(9) - ... 0.163e5 * y(1) * y(6) + 2*0.100e9 * y(16) - 0.124e4 * y(6) * y(17); f(07,1) = - 0.860e-3 * y(7) - 0.820e-3 * y(7) - 0.150e5 * y(6) * y(7) + 0.188e1 * y(14); f(08,1) = + 0.860e-3 * y(7) + 0.820e-3 * y(7) + 0.150e5 * y(6) * y(7) + 0.130e-3 * y(9); f(09,1) = - 0.130e-3 * y(9) - 0.240e5 * y(6) * y(9); f(10,1) = + 0.130e-3 * y(9) + 0.165e5 * y(2) * y(11) - 0.120e5 * y(2) * y(10); f(11,1) = + 0.240e5 * y(6) * y(9) - 0.165e5 * y(2) * y(11) - 0.900e4 * y(1) * y(11) + ... 0.220e-1 * y(13); f(12,1) = + 0.165e5 * y(2) * y(11); f(13,1) = + 0.900e4 * y(1) * y(11) - 0.220e-1 * y(13); f(14,1) = + 0.120e5 * y(2) * y(10) - 0.188e1 * y(14); f(15,1) = + 0.163e5 * y(1) * y(6); f(16,1) = + 0.350e-3 * y(4) - 0.100e9 * y(16) - 0.444e12 * y(16); f(17,1) = - 0.124e4 * y(6) * y(17); f(18,1) = + 0.124e4 * y(6) * y(17); f(19,1) = - 0.210e1 * y(19) - 0.578e1 * y(19) + 0.474e-1 * y(1) * y(4) - ... 0.178e4 * y(1) * y(19) + 0.312e1 * y(20); f(20,1) = + 0.178e4 * y(1) * y(19) - 0.312e1 * y(20); %# Returns the INITIAL values for the POLLUTION problem function vinit = odepkg_testsuite_pollutioninit () vinit = [0, 0.2, 0, 0.04, 0, 0, 0.1, 0.3, 0.01, ... 0, 0, 0, 0, 0, 0, 0, 0.007, 0, 0, 0]; %# Returns the JACOBIAN matrix for the POLLUTION problem function dfdy = odepkg_testsuite_pollutionjac (t, y) k1 = 0.35e0; k2 = 0.266e2; k3 = 0.123e5; k4 = 0.86e-3; k5 = 0.82e-3; k6 = 0.15e5; k7 = 0.13e-3; k8 = 0.24e5; k9 = 0.165e5; k10 = 0.9e4; k11 = 0.22e-1; k12 = 0.12e5; k13 = 0.188e1; k14 = 0.163e5; k15 = 0.48e7; k16 = 0.35e-3; k17 = 0.175e-1; k18 = 0.1e9; k19 = 0.444e12; k20 = 0.124e4; k21 = 0.21e1; k22 = 0.578e1; k23 = 0.474e-1; k24 = 0.178e4; k25 = 0.312e1; dfdy(1,1) = -k1 - k10 * y(11) - k14 * y(6) - k23 * y(4) - k24 * y(19); dfdy(1,11) = -k10 * y(1) + k9 * y(2); dfdy(1,6) = -k14 * y(1); dfdy(1,4) = -k23 * y(1) + k2 * y(2); dfdy(1,19) = -k24 * y(1) + k22; dfdy(1,2) = k2 * y(4) + k9 * y(11) + k3 * y(5) + k12 * y(10); dfdy(1,13) = k11; dfdy(1,20) = k25; dfdy(1,5) = k3 * y(2); dfdy(1,10) = k12 * y(2); dfdy(2,4) = -k2 * y(2); dfdy(2,5) = -k3 * y(2); dfdy(2,11) = -k9 * y(2); dfdy(2,10) = -k12 * y(2); dfdy(2,19) = k21; dfdy(2,1) = k1; dfdy(2,2) = -k2 * y(4) - k3 * y(5) - k9 * y(11) - k12 * y(10); dfdy(3,1) = k1; dfdy(3,4) = k17; dfdy(3,16) = k19; dfdy(3,19) = k22; dfdy(3,3) = -k15; dfdy(4,4) = -k2 * y(2) - k16 - k17 - k23 * y(1); dfdy(4,2) = -k2 * y(4); dfdy(4,1) = -k23 * y(4); dfdy(4,3) = k15; dfdy(5,5) = -k3 * y(2); dfdy(5,2) = -k3 * y(5); dfdy(5,7) = 2d0 * k4 + k6 * y(6); dfdy(5,6) = k6 * y(7) + k20 * y(17); dfdy(5,9) = k7; dfdy(5,14) = k13; dfdy(5,17) = k20 * y(6); dfdy(6,6) = -k6 * y(7) - k8 * y(9) - k14 * y(1) - k20 * y(17); dfdy(6,7) = -k6 * y(6); dfdy(6,9) = -k8 * y(6); dfdy(6,1) = -k14 * y(6); dfdy(6,17) = -k20 * y(6); dfdy(6,2) = k3 * y(5); dfdy(6,5) = k3 * y(2); dfdy(6,16) = 2d0 * k18; dfdy(7,7) = -k4 - k5 - k6 * y(6); dfdy(7,6) = -k6 * y(7); dfdy(7,14) = k13; dfdy(8,7) = k4 + k5 + k6 * y(6); dfdy(8,6) = k6 * y(7); dfdy(8,9) = k7; dfdy(9,9) = -k7 - k8 * y(6); dfdy(9,6) = -k8 * y(9); dfdy(10,10) = -k12 * y(2); dfdy(10,2) = -k12 * y(10) + k9 * y(11); dfdy(10,9) = k7; dfdy(10,11) = k9 * y(2); dfdy(11,11) = -k9 * y(2) - k10 * y(1); dfdy(11,2) = -k9 * y(11); dfdy(11,1) = -k10 * y(11); dfdy(11,9) = k8 * y(6); dfdy(11,6) = k8 * y(9); dfdy(11,13) = k11; dfdy(12,11) = k9 * y(2); dfdy(12,2) = k9 * y(11); dfdy(13,13) = -k11; dfdy(13,11) = k10 * y(1); dfdy(13,1) = k10 * y(11); dfdy(14,14) = -k13; dfdy(14,10) = k12 * y(2); dfdy(14,2) = k12 * y(10); dfdy(15,1) = k14 * y(6); dfdy(15,6) = k14 * y(1); dfdy(16,16) = -k18 - k19; dfdy(16,4) = k16; dfdy(17,17) = -k20 * y(6); dfdy(17,6) = -k20 * y(17); dfdy(18,17) = k20 * y(6); dfdy(18,6) = k20 * y(17); dfdy(19,19) = -k21 - k22 - k24 * y(1); dfdy(19,1) = -k24 * y(19) + k23 * y(4); dfdy(19,4) = k23 * y(1); dfdy(19,20) = k25; dfdy(20,20) = -k25; dfdy(20,1) = k24 * y(19); dfdy(20,19) = k24 * y(1); %# Returns the REFERENCE values for the POLLUTION problem function y = odepkg_testsuite_pollutionref () y(01,1) = 0.56462554800227 * 10^(-1); y(02,1) = 0.13424841304223 * 10^(+0); y(03,1) = 0.41397343310994 * 10^(-8); y(04,1) = 0.55231402074843 * 10^(-2); y(05,1) = 0.20189772623021 * 10^(-6); y(06,1) = 0.20189772623021 * 10^(-6); y(07,1) = 0.77842491189979 * 10^(-1); y(08,1) = 0.77842491189979 * 10^(+0); y(09,1) = 0.74940133838804 * 10^(-2); y(10,1) = 0.16222931573015 * 10^(-7); y(11,1) = 0.11358638332570 * 10^(-7); y(12,1) = 0.22305059757213 * 10^(-2); y(13,1) = 0.20871628827986 * 10^(-3); y(14,1) = 0.13969210168401 * 10^(-4); y(15,1) = 0.89648848568982 * 10^(-2); y(16,1) = 0.43528463693301 * 10^(-17); y(17,1) = 0.68992196962634 * 10^(-2); y(18,1) = 0.10078030373659 * 10^(-3); y(19,1) = 0.17721465139699 * 10^(-5); y(20,1) = 0.56829432923163 * 10^(-4); %!demo %! %% vsolver = {@ode23, @ode45, @ode54, @ode78, ... %! %% @odebda, @oders, @ode2r, @ode5r, @odesx}; %! vsolver = {@odebda, @oders, @ode2r, @ode5r, @odesx}; %! for vcnt=1:length (vsolver) %! poll{vcnt,1} = odepkg_testsuite_pollution (vsolver{vcnt}, 1e-7); %! end %! poll %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_robertson.m0000644000000000000000000001244012526637474017756 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_robertson (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return a cell array @var{solution} with performance informations about the modified ROBERTSON testsuite of differential algebraic equations after solving (DAE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_robertson %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_robertson (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_robertson'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2} * 1e-2; %# The value for the absolute tolerance vret{4} = vret{2}; %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite ROBERTSON, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 1e11; %# The point of time when solving is stoped vinit = odepkg_testsuite_robertsoninit; %# The initial values vmass = odepkg_testsuite_robertsonmass; %# The mass matrix vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_robertsonjac, 'Mass', vmass, ... 'MaxStep', vstop-vstart); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_robertsonfun, ... [vstart, vstop], vinit, vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_robertsonref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Returns the results for the for the modified ROBERTSON problem function f = odepkg_testsuite_robertsonfun (t, y, varargin) f(1,1) = -0.04 * y(1) + 1e4 * y(2) * y(3); f(2,1) = 0.04 * y(1) - 1e4 * y(2) * y(3) - 3e7 * y(2)^2; f(3,1) = y(1) + y(2) + y(3) - 1; %# Returns the INITIAL values for the modified ROBERTSON problem function vinit = odepkg_testsuite_robertsoninit () vinit = [1, 0, 0]; %# Returns the JACOBIAN matrix for the modified ROBERTSON problem function dfdy = odepkg_testsuite_robertsonjac (t, y, varargin) dfdy(1,1) = -0.04; dfdy(1,2) = 1e4 * y(3); dfdy(1,3) = 1e4 * y(2); dfdy(2,1) = 0.04; dfdy(2,2) = -1e4 * y(3) - 6e7 * y(2); dfdy(2,3) = -1e4 * y(2); dfdy(3,1) = 1; dfdy(3,2) = 1; dfdy(3,3) = 1; %# Returns the MASS matrix for the modified ROBERTSON problem function mass = odepkg_testsuite_robertsonmass (t, y, varargin) mass = [1, 0, 0; 0, 1, 0; 0, 0, 0]; %# Returns the REFERENCE values for the modified ROBERTSON problem function y = odepkg_testsuite_robertsonref () y(1) = 0.20833401497012e-07; y(2) = 0.83333607703347e-13; y(3) = 0.99999997916650e+00; %!demo %! vsolver = {@odebda, @oders, @ode2r, @ode5r, @odesx}; %! for vcnt=1:length (vsolver) %! vrober{vcnt,1} = odepkg_testsuite_robertson (vsolver{vcnt}, 1e-7); %! end %! vrober %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odepkg_testsuite_transistor.m0000644000000000000000000001572012526637474020155 0ustar 00000000000000%# Copyright (C) 2007-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{solution}] =} odepkg_testsuite_transistor (@var{@@solver}, @var{reltol}) %# %# If this function is called with two input arguments and the first input argument @var{@@solver} is a function handle describing an OdePkg solver and the second input argument @var{reltol} is a double scalar describing the relative error tolerance then return the cell array @var{solution} with performance informations about the TRANSISTOR testsuite of differential algebraic equations after solving (DAE--test). %# %# Run examples with the command %# @example %# demo odepkg_testsuite_transistor %# @end example %# %# This function has been ported from the "Test Set for IVP solvers" which is developed by the INdAM Bari unit project group "Codes and Test Problems for Differential Equations", coordinator F. Mazzia. %# @end deftypefn %# %# @seealso{odepkg} function vret = odepkg_testsuite_transistor (vhandle, vrtol) if (nargin ~= 2) %# Check number and types of all input arguments help ('odepkg_testsuite_transistor'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be exactly two'); elseif (~isa (vhandle, 'function_handle') || ~isscalar (vrtol)) print_usage; end vret{1} = vhandle; %# The handle for the solver that is used vret{2} = vrtol; %# The value for the realtive tolerance vret{3} = vret{2}; %# The value for the absolute tolerance vret{4} = vret{2}; %# The value for the first time step %# Write a debug message on the screen, because this testsuite function %# may be called more than once from a loop over all solvers present fprintf (1, ['Testsuite TRANSISTOR, testing solver %7s with relative', ... ' tolerance %2.0e\n'], func2str (vret{1}), vrtol); fflush (1); %# Setting the integration algorithms option values vstart = 0.0; %# The point of time when solving is started vstop = 0.2; %# The point of time when solving is stoped vinit = odepkg_testsuite_transistorinit; %# The initial values vmass = odepkg_testsuite_transistormass; %# The mass matrix vopt = odeset ('Refine', 0, 'RelTol', vret{2}, 'AbsTol', vret{3}, ... 'InitialStep', vret{4}, 'Stats', 'on', 'NormControl', 'off', ... 'Jacobian', @odepkg_testsuite_transistorjac, 'Mass', vmass, ... 'MaxStep', vstop-vstart); %# Calculate the algorithm, start timer and do solving tic; vsol = feval (vhandle, @odepkg_testsuite_transistorfun, ... [vstart, vstop], vinit, vopt); vret{12} = toc; %# The value for the elapsed time vref = odepkg_testsuite_transistorref; %# Get the reference solution vector if (exist ('OCTAVE_VERSION') ~= 0) vlst = vsol.y(end,:); else vlst = vsol.y(:,end); end vret{5} = odepkg_testsuite_calcmescd (vlst, vref, vret{3}, vret{2}); vret{6} = odepkg_testsuite_calcscd (vlst, vref, vret{3}, vret{2}); vret{7} = vsol.stats.nsteps + vsol.stats.nfailed; %# The value for all evals vret{8} = vsol.stats.nsteps; %# The value for success evals vret{9} = vsol.stats.nfevals; %# The value for fun calls vret{10} = vsol.stats.npds; %# The value for partial derivations vret{11} = vsol.stats.ndecomps; %# The value for LU decompositions %# Returns the results for the TRANSISTOR problem function f = odepkg_testsuite_transistorfun (t, y, varargin) ub = 6; uf = 0.026; alpha = 0.99; beta = 1d-6; r0 = 1000; r1 = 9000; r2 = 9000; r3 = 9000; r4 = 9000; r5 = 9000; r6 = 9000; r7 = 9000; r8 = 9000; r9 = 9000; uet = 0.1 * sin(200 * pi * t); fac1 = beta * (exp((y(2) - y(3)) / uf) - 1); fac2 = beta * (exp((y(5) - y(6)) / uf) - 1); f(1,1) = (y(1) - uet) / r0; f(2,1) = y(2) / r1 + (y(2) - ub) / r2 + (1 - alpha) * fac1; f(3,1) = y(3) / r3 - fac1; f(4,1) = (y(4) - ub) / r4 + alpha * fac1; f(5,1) = y(5) / r5 + (y(5) - ub) / r6 + (1 - alpha) * fac2; f(6,1) = y(6) / r7 - fac2; f(7,1) = (y(7) - ub) / r8 + alpha * fac2; f(8,1) = y(8) / r9; %# Returns the INITIAL values for the TRANSISTOR problem function vinit = odepkg_testsuite_transistorinit () vinit = [0, 3, 3, 6, 3, 3, 6, 0]; %# Returns the JACOBIAN matrix for the TRANSISTOR problem function dfdy = odepkg_testsuite_transistorjac (t, y, varargin) ub = 6; uf = 0.026; alpha = 0.99; beta = 1d-6; r0 = 1000; r1 = 9000; r2 = 9000; r3 = 9000; r4 = 9000; r5 = 9000; r6 = 9000; r7 = 9000; r8 = 9000; r9 = 9000; fac1p = beta * exp ((y(2) - y(3)) / uf) / uf; fac2p = beta * exp ((y(5) - y(6)) / uf) / uf; dfdy(1,1) = 1 / r0; dfdy(2,2) = 1 / r1 + 1 / r2 + (1 - alpha) * fac1p; dfdy(2,3) = - (1 - alpha) * fac1p; dfdy(3,2) = - fac1p; dfdy(3,3) = 1 / r3 + fac1p; dfdy(4,2) = alpha * fac1p; dfdy(4,3) = - alpha * fac1p; dfdy(4,4) = 1 / r4; dfdy(5,5) = 1 / r5 + 1 / r6 + (1 - alpha) * fac2p; dfdy(5,6) = - (1 - alpha) * fac2p; dfdy(6,5) = - fac2p; dfdy(6,6) = 1 / r7 + fac2p; dfdy(7,5) = alpha * fac2p; dfdy(7,6) = - alpha * fac2p; dfdy(7,7) = 1 / r8; dfdy(8,8) = 1 / r9; %# Returns the MASS matrix for the TRANSISTOR problem function mass = odepkg_testsuite_transistormass (t, y, varargin) mass = [-1e-6, 1e-6, 0, 0, 0, 0, 0, 0; ... 1e-6, -1e-6, 0, 0, 0, 0, 0, 0; ... 0, 0, -2e-6, 0, 0, 0, 0, 0; ... 0, 0, 0, -3e-6, 3e-6, 0, 0, 0; ... 0, 0, 0, 3e-6, -3e-6, 0, 0, 0; ... 0, 0, 0, 0, 0, -4e-6, 0, 0; ... 0, 0, 0, 0, 0, 0, -5e-6, 5e-6; ... 0, 0, 0, 0, 0, 0, 5e-6, -5e-6]; %# Returns the REFERENCE values for the TRANSISTOR problem function y = odepkg_testsuite_transistorref () y(1,1) = -0.55621450122627e-2; y(2,1) = 0.30065224719030e+1; y(3,1) = 0.28499587886081e+1; y(4,1) = 0.29264225362062e+1; y(5,1) = 0.27046178650105e+1; y(6,1) = 0.27618377783931e+1; y(7,1) = 0.47709276316167e+1; y(8,1) = 0.12369958680915e+1; %!demo %! vsolver = {@odebda, @oders, @ode2r, @ode5r, @odesx}; %! for vcnt=1:length (vsolver) %! vtrans{vcnt,1} = odepkg_testsuite_transistor (vsolver{vcnt}, 1e-7); %! end %! vtrans %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odeplot.m0000644000000000000000000000665512526637474013760 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{ret}] =} odeplot (@var{t}, @var{y}, @var{flag}) %# %# Open a new figure window and plot the results from the variable @var{y} of type column vector over time while solving. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is %# @table @option %# @item @code{"init"} %# then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, %# @item @code{""} %# then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', %# @item @code{"done"} %# then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. %# @end table %# %# This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. %# %# For example, solve an anonymous implementation of the "Van der Pol" equation and display the results while solving %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# %# vopt = odeset ('OutputFcn', @@odeplot, 'RelTol', 1e-6); %# vsol = ode45 (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = odeplot (vt, vy, vflag, varargin) %# No input argument check is done for a higher processing speed persistent vfigure; persistent vtold; persistent vyold; persistent vcounter; if (strcmp (vflag, 'init')) %# Nothing to return, vt is either the time slot [tstart tstop] %# or [t0, t1, ..., tn], vy is the inital value vector 'vinit' vfigure = figure; vtold = vt(1,1); vyold = vy(:,1); vcounter = 1; elseif (isempty (vflag)) %# Return something in varargout{1}, either false for 'not stopping %# the integration' or true for 'stopping the integration' vcounter = vcounter + 1; figure (vfigure); vtold(vcounter,1) = vt(1,1); vyold(:,vcounter) = vy(:,1); plot (vtold, vyold, '-o', 'markersize', 1); drawnow; varargout{1} = false; elseif (strcmp (vflag, 'done')) %# Cleanup has to be done, clear the persistent variables because %# we don't need them anymore clear ('vfigure', 'vtold', 'vyold', 'vcounter'); end %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odeprint.m0000644000000000000000000000644212526637474014130 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{ret}] =} odeprint (@var{t}, @var{y}, @var{flag}) %# %# Display the results of the set of differential equations in the Octave window while solving. The first column of the screen output shows the actual time stamp that is given with the input arguemtn @var{t}, the following columns show the results from the function evaluation that are given by the column vector @var{y}. The types and the values of the input parameter @var{t} and the output parameter @var{ret} depend on the input value @var{flag} that is of type string. If @var{flag} is %# @table @option %# @item @code{"init"} %# then @var{t} must be a double column vector of length 2 with the first and the last time step and nothing is returned from this function, %# @item @code{""} %# then @var{t} must be a double scalar specifying the actual time step and the return value is false (resp. value 0) for 'not stop solving', %# @item @code{"done"} %# then @var{t} must be a double scalar specifying the last time step and nothing is returned from this function. %# @end table %# %# This function is called by a OdePkg solver function if it was specified in an OdePkg options structure with the @command{odeset}. This function is an OdePkg internal helper function therefore it should never be necessary that this function is called directly by a user. There is only little error detection implemented in this function file to achieve the highest performance. %# %# For example, solve an anonymous implementation of the "Van der Pol" equation and print the results while solving %# @example %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %# %# vopt = odeset ('OutputFcn', @@odeprint, 'RelTol', 1e-6); %# vsol = ode45 (fvdb, [0 20], [2 0], vopt); %# @end example %# @end deftypefn %# %# @seealso{odepkg} function [varargout] = odeprint (vt, vy, vflag, varargin) %# No input argument check is done for a higher processing speed %# vt and vy are always column vectors, see also function odeplot, %# odephas2 and odephas3 for another implementation. vflag either %# is "init", [] or "done". if (strcmp (vflag, 'init')) fprintf (1, '%f%s\n', vt (1,1), sprintf (' %f', vy) ); fflush (1); elseif (isempty (vflag)) %# Return value varargout{1} needed fprintf (1, '%f%s\n', vt (1,1), sprintf (' %f', vy) ); fflush (1); varargout{1} = false; elseif (strcmp (vflag, 'done')) %# Cleanup could be done, but nothing to do in this function end %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/inst/odeset.m0000644000000000000000000001662312526637474013571 0ustar 00000000000000%# Copyright (C) 2006-2012, Thomas Treichl %# OdePkg - A package for solving ordinary differential equations and more %# %# 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; either version 2 of the License, or %# (at your option) any later version. %# %# 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 . %# -*- texinfo -*- %# @deftypefn {Function File} {[@var{odestruct}] =} odeset () %# @deftypefnx {Command} {[@var{odestruct}] =} odeset (@var{"field1"}, @var{value1}, @var{"field2"}, @var{value2}, @dots{}) %# @deftypefnx {Command} {[@var{odestruct}] =} odeset (@var{oldstruct}, @var{"field1"}, @var{value1}, @var{"field2"}, @var{value2}, @dots{}) %# @deftypefnx {Command} {[@var{odestruct}] =} odeset (@var{oldstruct}, @var{newstruct}) %# %# If this function is called without an input argument then return a new OdePkg options structure array that contains all the necessary fields and sets the values of all fields to default values. %# %# If this function is called with string input arguments @var{"field1"}, @var{"field2"}, @dots{} identifying valid OdePkg options then return a new OdePkg options structure with all necessary fields and set the values of the fields @var{"field1"}, @var{"field2"}, @dots{} to the values @var{value1}, @var{value2}, @dots{} %# %# If this function is called with a first input argument @var{oldstruct} of type structure array then overwrite all values of the options @var{"field1"}, @var{"field2"}, @dots{} of the structure @var{oldstruct} with new values @var{value1}, @var{value2}, @dots{} and return the modified structure array. %# %# If this function is called with two input argumnets @var{oldstruct} and @var{newstruct} of type structure array then overwrite all values in the fields from the structure @var{oldstruct} with new values of the fields from the structure @var{newstruct}. Empty values of @var{newstruct} will not overwrite values in @var{oldstruct}. %# %# For a detailed explanation about valid fields and field values in an OdePkg structure aaray have a look at the @file{odepkg.pdf}, Section 'ODE/DAE/IDE/DDE options' or run the command @command{doc odepkg} to open the tutorial. %# %# Run examples with the command %# @example %# demo odeset %# @end example %# @end deftypefn %# %# @seealso{odepkg} function [vret] = odeset (varargin) %# Create a template OdePkg structure vtemplate = struct ... ('RelTol', [], ... 'AbsTol', [], ... 'NormControl', 'off', ... 'NonNegative', [], ... 'OutputFcn', [], ... 'OutputSel', [], ... 'OutputSave',[],... 'Refine', 0, ... 'Stats', 'off', ... 'InitialStep', [], ... 'MaxStep', [], ... 'Events', [], ... 'Jacobian', [], ... 'JPattern', [], ... 'Vectorized', 'off', ... 'Mass', [], ... 'MStateDependence', 'weak', ... 'MvPattern', [], ... 'MassSingular', 'maybe', ... 'InitialSlope', [], ... 'MaxOrder', [], ... 'BDF', [], ... 'NewtonTol', [], ... 'MaxNewtonIterations', []); %# Check number and types of all input arguments if (nargin == 0 && nargout == 1) vret = odepkg_structure_check (vtemplate); return; elseif (nargin == 0) help ('odeset'); error ('OdePkg:InvalidArgument', ... 'Number of input arguments must be greater than zero'); elseif (length (varargin) < 2) usage ('odeset ("field1", "value1", ...)'); elseif (ischar (varargin{1}) && mod (length (varargin), 2) == 0) %# Check if there is an odd number of input arguments. If this is %# true then save all the structure names in varg and its values in %# vval and increment vnmb for every option that is found. vnmb = 1; for vcntarg = 1:2:length (varargin) if (ischar (varargin{vcntarg})) varg{vnmb} = varargin{vcntarg}; vval{vnmb} = varargin{vcntarg+1}; vnmb = vnmb + 1; else error ('OdePkg:InvalidArgument', ... 'Input argument number %d is no valid string', vcntarg); end end %# Create and return a new OdePkg structure and fill up all new %# field values that have been found. for vcntarg = 1:(vnmb-1) vtemplate.(varg{vcntarg}) = vval{vcntarg}; end vret = odepkg_structure_check (vtemplate); elseif (isstruct (varargin{1}) && ischar (varargin{2}) && ... mod (length (varargin), 2) == 1) %# Check if there is an even number of input arguments. If this is %# true then the first input argument also must be a valid OdePkg %# structure. Save all the structure names in varg and its values in %# vval and increment the vnmb counter for every option that is %# found. vnmb = 1; for vcntarg = 2:2:length (varargin) if (ischar (varargin{vcntarg})) varg{vnmb} = varargin{vcntarg}; vval{vnmb} = varargin{vcntarg+1}; vnmb = vnmb + 1; else error ('OdePkg:InvalidArgument', ... 'Input argument number %d is no valid string', vcntarg); end end %# Use the old OdePkg structure and fill up all new field values %# that have been found. vret = odepkg_structure_check (varargin{1}); for vcntarg = 1:(vnmb-1) vret.(varg{vcntarg}) = vval{vcntarg}; end vret = odepkg_structure_check (vret); elseif (isstruct (varargin{1}) && isstruct (varargin{2}) && ... length (varargin) == 2) %# Check if the two input arguments are valid OdePkg structures and %# also check if there does not exist any other input argument. vret = odepkg_structure_check (varargin{1}); vnew = odepkg_structure_check (varargin{2}); vfld = fieldnames (vnew); vlen = length (vfld); for vcntfld = 1:vlen if (~isempty (vnew.(vfld{vcntfld}))) vret.(vfld{vcntfld}) = vnew.(vfld{vcntfld}); end end vret = odepkg_structure_check (vret); else error ('OdePkg:InvalidArgument', ... 'Check types and number of all input arguments'); end end %# All tests that are needed to check if a correct resp. valid option %# has been set are implemented in odepkg_structure_check.m. %!test odeoptA = odeset (); %!test odeoptB = odeset ('AbsTol', 1e-2, 'RelTol', 1e-1); %! if (odeoptB.AbsTol ~= 1e-2), error; end %! if (odeoptB.RelTol ~= 1e-1), error; end %!test odeoptB = odeset ('AbsTol', 1e-2, 'RelTol', 1e-1); %! odeoptC = odeset (odeoptB, 'NormControl', 'on'); %!test odeoptB = odeset ('AbsTol', 1e-2, 'RelTol', 1e-1); %! odeoptC = odeset (odeoptB, 'NormControl', 'on'); %! odeoptD = odeset (odeoptC, odeoptB); %!demo %! # A new OdePkg options structure with default values is created. %! %! odeoptA = odeset (); %! %!demo %! # A new OdePkg options structure with manually set options %! # "AbsTol" and "RelTol" is created. %! %! odeoptB = odeset ('AbsTol', 1e-2, 'RelTol', 1e-1); %! %!demo %! # A new OdePkg options structure from odeoptB is created with %! # a modified value for option "NormControl". %! %! odeoptB = odeset ('AbsTol', 1e-2, 'RelTol', 1e-1); %! odeoptC = odeset (odeoptB, 'NormControl', 'on'); %# Local Variables: *** %# mode: octave *** %# End: *** odepkg-0.8.5/src/Makefile0000644000000000000000000000477312526637474013405 0ustar 00000000000000# Filename: Makefile # Description: Makefile script for the src directory of the OdePkg sinclude Makeconf # This is needed for packaging OdePkg correctly DEVELOPERDEFS = -Wall -W -Wshadow TGZUNPACK = tar -xzf # "tar -xzvf" to print some output PATCHPROG = patch -p0 # Needed to patch the Fortran codes # MKOCTFILE = $(MKOCTFILE) has already been defined in Makeconf # MKMEXFILE = $(MKOCTFILE) --mex MKOCTFILE ?= mkoctfile FFLAGS := $(shell $(MKOCTFILE) -p FFLAGS) ifeq (gfortran,$(findstring gfortran,$(F77))) FFLAGS := -fno-automatic $(FFLAGS) endif ifeq (g95,$(findstring g95,$(F77))) FFLAGS := -fstatic $(FFLAGS) endif MKF77FILE = FFLAGS="$(FFLAGS)" $(MKOCTFILE) ifndef LAPACK_LIBS LAPACK_LIBS := $(shell $(MKOCTFILE) -p BLAS_LIBS) $(shell $(MKOCTFILE) -p LAPACK_LIBS) endif ifndef FLIBS FLIBS := $(shell $(MKOCTFILE) -p FLIBS) endif LFLAGS := $(shell $(MKOCTFILE) -p LFLAGS) $(LAPACK_LIBS) $(FLIBS) EXTERNALDIRS = hairer cash daskr EXTERNALPACKS = $(patsubst %, %.tgz, $(EXTERNALDIRS)) EXTERNALDIFFS = $(patsubst %, %.diff, $(EXTERNALDIRS)) SOLVERSOURCES = odepkg_octsolver_mebdfdae.cc odepkg_octsolver_mebdfi.cc \ odepkg_octsolver_ddaskr.cc \ odepkg_octsolver_radau.cc odepkg_octsolver_radau5.cc \ odepkg_octsolver_rodas.cc odepkg_octsolver_seulex.cc SOLVERDEPENDS = odepkg_auxiliary_functions.cc SOLVEREXTERNS = cash/mebdfdae.f cash/mebdfi.f \ daskr/ddaskr.f daskr/dlinpk.f \ hairer/decsol.f hairer/dc_decsol.f \ hairer/radau.f hairer/radau5.f hairer/rodas.f hairer/seulex.f SOLVEROBJECTS = $(patsubst %.cc, %.o, $(SOLVERSOURCES)) \ $(patsubst %.cc, %.o, $(SOLVERDEPENDS)) \ $(patsubst %.f, %.o, $(SOLVEREXTERNS)) SOLVEROCTFILE = dldsolver.oct %.o : %.f ; $(MKF77FILE) -c $< -o $@ %.o : %.cc ; $(MKOCTFILE) -c $< -o $@ all : $(EXTERNALDIRS) $(SOLVEROCTFILE) $(SOLVEROCTFILE) : $(EXTERNALDIRS) $(SOLVEROBJECTS) LFLAGS="$(LFLAGS)" $(MKOCTFILE) $(SOLVEROBJECTS) -o $(SOLVEROCTFILE) \ $(LAPACK_LIBS) $(FLIBS) install : @$(INSTALL) -d $(DESTDIR)$(MPATH)/odepkg .NOTPARALLEL: $(EXTERNALDIRS) : @for i in $(EXTERNALPACKS); do \ echo "Unpacking external packages: $$i"; \ $(TGZUNPACK) $$i; \ done @for j in $(EXTERNALDIFFS); do \ echo "Applying patches from file: $$j"; \ $(PATCHPROG) < $$j; \ done clean : @echo "Cleaning..."; \ $(RM) -rf $(EXTERNALDIRS) $(SOLVEROBJECTS) $(SOLVEROCTFILE) distclean : clean @echo "Dist Cleaning..."; \ $(RM) -rf *~ config.status config.log autom4te.cache Makeconf realclean : distclean @echo "Really Cleaning..."; \ $(RM) -rf configure dist : all odepkg-0.8.5/src/cash.diff0000644000000000000000000040273112526637474013511 0ustar 00000000000000--- cash.orig/mebdfdae.f 2007-12-15 15:37:46.000000000 -0500 +++ cash/mebdfdae.f 2014-03-02 16:20:04.683064613 -0500 @@ -53,13 +53,13 @@ C C NOVEMBER 6th 1998: FIRST RELEASE C -C OVDRIV +C A_OVDRIV C A PACKAGE FOR THE SOLUTION OF THE INITIAL VALUE PROBLEM C FOR SYSTEMS OF ORDINARY DIFFERENTIAL EQUATIONS C DY/DT = F(Y,T), Y=(Y(1),Y(2),Y(3), . . . ,Y(N)) C AND LINEARLY IMPLICIT DIFFERENTIAL ALGEBRAIC EQUATIONS C M(DY/DT) = F(Y,T) -C SUBROUTINE OVDRIV IS A DRIVER ROUTINE FOR THIS PACKAGE +C SUBROUTINE A_OVDRIV IS A DRIVER ROUTINE FOR THIS PACKAGE C C REFERENCES C @@ -79,8 +79,8 @@ C SPRINGER 1996, page 267. C C ---------------------------------------------------------------- -C OVDRIV IS TO BE CALLED ONCE FOR EACH OUTPUT VALUE OF T, AND -C IN TURN MAKES REPEATED CALLS TO THE CORE INTEGRATOR STIFF. +C A_OVDRIV IS TO BE CALLED ONCE FOR EACH OUTPUT VALUE OF T, AND +C IN TURN MAKES REPEATED CALLS TO THE CORE INTEGRATOR A_STIFF. C C THE INPUT PARAMETERS ARE .. C N = THE NUMBER OF FIRST ORDER DIFFERENTIAL EQUATIONS. @@ -165,7 +165,7 @@ C SHOULD BE NON-NEGATIVE. IF ITOL = 1 THEN SINGLE STEP ERROR C ESTIMATES DIVIDED BY YMAX(I) WILL BE KEPT LESS THAN 1 C IN ROOT-MEAN-SQUARE NORM. THE VECTOR YMAX OF WEIGHTS IS -C COMPUTED IN OVDRIV. INITIALLY YMAX(I) IS SET AS +C COMPUTED IN A_OVDRIV. INITIALLY YMAX(I) IS SET AS C THE MAXIMUM OF 1 AND ABS(Y(I)). THEREAFTER YMAX(I) IS C THE LARGEST VALUE OF ABS(Y(I)) SEEN SO FAR, OR THE C INITIAL VALUE YMAX(I) IF THAT IS LARGER. @@ -251,20 +251,20 @@ C IN ADDITION TO OVDRIVE, THE FOLLOWING ROUTINES ARE PROVIDED C IN THE PACKAGE.. C -C INTERP( - ) INTERPOLATES TO GET THE OUTPUT VALUES +C A_INTERP( - ) INTERPOLATES TO GET THE OUTPUT VALUES C AT T=TOUT FROM THE DATA IN THE Y ARRAY. -C STIFF( - ) IS THE CORE INTEGRATOR ROUTINE. IT PERFORMS A +C A_STIFF( - ) IS THE CORE INTEGRATOR ROUTINE. IT PERFORMS A C SINGLE STEP AND ASSOCIATED ERROR CONTROL. -C COSET( - ) SETS COEFFICIENTS FOR BACKWARD DIFFERENTIATION +C A_COSET( - ) SETS COEFFICIENTS FOR BACKWARD DIFFERENTIATION C SCHEMES FOR USE IN THE CORE INTEGRATOR. -C PSET( - ) COMPUTES AND PROCESSES THE JACOBIAN +C A_PSET( - ) COMPUTES AND PROCESSES THE JACOBIAN C MATRIX J = DF/DY -C DEC( - ) PERFORMS AN LU DECOMPOSITION ON A MATRIX. -C SOL( - ) SOLVES LINEAR SYSTEMS A*X = B AFTER DEC +C A_DEC( - ) PERFORMS AN LU DECOMPOSITION ON A MATRIX. +C A_SOL( - ) SOLVES LINEAR SYSTEMS A*X = B AFTER A_DEC C HAS BEEN CALLED FOR THE MATRIX A -C DGBFA ( - ) FACTORS A DOUBLE PRECISION BAND MATRIX BY +C A_DGBFA ( - ) FACTORS A DOUBLE PRECISION BAND MATRIX BY C ELIMINATION. -C DGBSL ( - ) SOLVES A BANDED LINEAR SYSTEM A*x=b +C A_DGBSL ( - ) SOLVES A BANDED LINEAR SYSTEM A*x=b C C ALSO SUPPLIED ARE THE BLAS ROUTINES C @@ -338,7 +338,7 @@ C >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> C THIS SUBROUTINE IS FOR THE PURPOSE * C OF SPLITTING UP THE WORK ARRAYS WORK AND IWORK * -C FOR USE INSIDE THE INTEGRATOR STIFF * +C FOR USE INSIDE THE INTEGRATOR A_STIFF * C <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C .. SCALAR ARGUMENTS .. @@ -353,7 +353,7 @@ C COMMON BLOCKS C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL OVDRIV,F,PDERV,MAS + EXTERNAL A_OVDRIV,F,PDERV,MAS C .. C .. SAVE STATEMENT .. SAVE I1,I2,I3,I4,I5,I6,I7,I8,I9,I10,I11,I12 @@ -401,7 +401,7 @@ c WORKSPACE HAS TO BE AT LEAST N+14. c - CALL OVDRIV(N,T0,HO,Y0,TOUT,TEND,MF,IDID,LOUT,WORK(3),WORK(I1), + CALL A_OVDRIV(N,T0,HO,Y0,TOUT,TEND,MF,IDID,LOUT,WORK(3),WORK(I1), + WORK(I2),WORK(I3),WORK(I4),WORK(I5),WORK(I6),WORK(I7), + WORK(I8),WORK(I9),WORK(I10),WORK(I11),IWORK(15),MBND,MASBND, + IWORK(1),IWORK(2),IWORK(3),MAXDER,ITOL,RTOL,ATOL,RPAR,IPAR, @@ -431,7 +431,7 @@ + ' WITH N = ',I6) END - SUBROUTINE OVDRIV(N,T0,HO,Y0,TOUT,TEND,MF,IDID,LOUT,Y,YHOLD, + SUBROUTINE A_OVDRIV(N,T0,HO,Y0,TOUT,TEND,MF,IDID,LOUT,Y,YHOLD, + YNHOLD,YMAX,ERRORS,SAVE1,SAVE2,SCALE,ARH,PW,PWCOPY, + AM,IPIV,MBND,MASBND,NIND1,NIND2,NIND3,MAXDER,ITOL, + RTOL,ATOL,RPAR,IPAR,F,PDERV,MAS,NQUSED,NSTEP,NFAIL, @@ -457,7 +457,7 @@ INTEGER I,KGO,NHCUT C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL INTERP,STIFF,F,PDERV,MAS + EXTERNAL A_INTERP,A_STIFF,F,PDERV,MAS C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DABS,DMAX1 @@ -475,7 +475,7 @@ HMAX = DABS(TEND-T0)*10.0D+0 IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT THE OUTPUT POINT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IDID = KFLAG T0 = TOUT HO = H @@ -493,7 +493,7 @@ IF (((T-TOUT)*H.GE.0.0D+0) .OR. (DABS(T-TOUT).LE. + 100.0D+0*UROUND*HMAX)) THEN C HAVE OVERSHOT THE OUTPUT POINT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) T0 = TOUT HO = H IDID = KFLAG @@ -520,7 +520,7 @@ IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT TOUT WRITE (LOUT,9080) T,TOUT,H - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) HO = H T0 = TOUT IDID = -5 @@ -534,7 +534,7 @@ T0 = T IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT,SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IDID = KFLAG T0 = TOUT HO = H @@ -667,7 +667,7 @@ 20 IF ((T+H).EQ.T) THEN WRITE (LOUT,9000) END IF - CALL STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND,MASBND, + CALL A_STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND,MASBND, + NIND1,NIND2,NIND3,T,TOUT,TEND,Y,N, + YMAX,ERRORS,SAVE1,SAVE2,SCALE,PW,PWCOPY,AM,YHOLD, + YNHOLD,ARH,IPIV,LOUT,MAXDER,ITOL,RTOL,ATOL,RPAR,IPAR,F, @@ -679,7 +679,7 @@ ENDIF KGO = 1 - KFLAG IF (KGO.EQ.1) THEN -C NORMAL RETURN FROM STIFF +C NORMAL RETURN FROM A_STIFF GO TO 30 ELSE IF (KGO.EQ.2) THEN @@ -756,7 +756,7 @@ IF (((T-TOUT)*H.GE.0.0D+0) .OR. (DABS(T-TOUT).LE. + 100.0D+0*UROUND*HMAX)) THEN C HAVE OVERSHOT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) T0 = TOUT HO = H IDID = KFLAG @@ -773,7 +773,7 @@ ELSE IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IDID = KFLAG HO = H T0 = TOUT @@ -812,14 +812,14 @@ ELSE C HAVE PASSED TOUT SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) T0 = TOUT IDID = KFLAG END IF HO = H IF(KFLAG.NE.0) IDID = KFLAG RETURN -C -------------------------- END OF SUBROUTINE OVDRIV ----------------- +C -------------------------- END OF SUBROUTINE A_OVDRIV ----------------- 9000 FORMAT (' WARNING.. T + H = T ON NEXT STEP.') 9010 FORMAT (/,/,' KFLAG = -2 FROM INTEGRATOR AT T = ',E16.8,' H =', + E16.8,/, @@ -853,7 +853,7 @@ END - SUBROUTINE INTERP(N,JSTART,H,T,Y,TOUT,Y0) + SUBROUTINE A_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C .. SCALAR ARGUMENTS .. @@ -880,14 +880,14 @@ 20 CONTINUE 30 CONTINUE RETURN -C -------------- END OF SUBROUTINE INTERP --------------------------- +C -------------- END OF SUBROUTINE A_INTERP --------------------------- END - SUBROUTINE COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + SUBROUTINE A_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C -------------------------------------------------------------------- -C COSET IS CALLED BY THE INTEGRATOR AND SETS THE COEFFICIENTS USED +C A_COSET IS CALLED BY THE INTEGRATOR AND SETS THE COEFFICIENTS USED C BY THE CONVENTIONAL BACKWARD DIFFERENTIATION SCHEME AND THE C MODIFIED EXTENDED BACKWARD DIFFERENTIATION SCHEME. THE VECTOR C EL OF LENGTH NQ+1 DETERMINES THE BASIC BDF METHOD WHILE THE VECTOR @@ -1017,10 +1017,10 @@ TQ(4) = 0.5D+0*TQ(2)/DBLE(FLOAT(NQ)) IF(NQ.NE.1) TQ(5)=PERTST(NQ-1,1) RETURN -C --------------------- END OF SUBROUTINE COSET --------------------- +C --------------------- END OF SUBROUTINE A_COSET --------------------- END - SUBROUTINE PSET(Y,N,H,T,UROUND,EPSJAC,CON,MITER,MBND, + SUBROUTINE A_PSET(Y,N,H,T,UROUND,EPSJAC,CON,MITER,MBND, + MASBND,NIND1,NIND2,NIND3,IER,F,PDERV,MAS, + NRENEW,YMAX,SAVE1,SAVE2,PW,PWCOPY,AM,WRKSPC,IPIV, + ITOL,RTOL,ATOL,NPSET,NJE,NFE,NDEC,IPAR,RPAR,IERR) @@ -1029,7 +1029,7 @@ IMPLICIT DOUBLE PRECISION(A-H,O-Z) C ------------------------------------------------------------------- -C PSET IS CALLED BY STIFF TO COMPUTE AND PROCESS THE MATRIX +C A_PSET IS CALLED BY A_STIFF TO COMPUTE AND PROCESS THE MATRIX C M/(H*EL(1)) - J WHERE J IS AN APPROXIMATION TO THE RELEVANT JACOBIAN C AND M IS THE MASS MATRIX. THIS MATRIX IS THEN SUBJECTED TO LU C DECOMPOSITION IN PREPARATION FOR LATER SOLUTION OF LINEAR SYSTEMS @@ -1037,7 +1037,7 @@ C MATRIX J IS FOUND BY THE USER-SUPPLIED ROUTINE PDERV IF MITER=1 C OR 3 OR BY FINITE DIFFERENCING IF MITER = 2 OR 4. C IN ADDITION TO VARIABLES DESCRIBED PREVIOUSLY, COMMUNICATION WITH -C PSET USES THE FOLLOWING .. +C A_PSET USES THE FOLLOWING .. C EPSJAC = DSQRT(UROUND), USED IN NUMERICAL JACOBIAN INCREMENTS. C ******************************************************************* C THE ARGUMENT NRENEW IS USED TO SIGNAL WHETHER OR NOT @@ -1060,7 +1060,7 @@ INTEGER I,J,J1,JJKK,FOUR,FIVE C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL DEC,F,PDERV,DGBFA,MAS + EXTERNAL A_DEC,F,PDERV,A_DGBFA,MAS C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DABS,DMAX1,DSQRT @@ -1267,7 +1267,7 @@ II = II + MBND(4) 75 CONTINUE ENDIF - CALL DGBFA(PW,MBND(4),N,ML,MU,IPIV,IER) + CALL A_DGBFA(PW,MBND(4),N,ML,MU,IPIV,IER) NDEC = NDEC + 1 ELSE IF(MASBND(1).EQ.0) THEN @@ -1278,13 +1278,13 @@ J = J + NP1 80 CONTINUE ENDIF - CALL DEC(N,N,PW,IPIV,IER) + CALL A_DEC(N,N,PW,IPIV,IER) NDEC = NDEC + 1 ENDIF RETURN -C ---------------------- END OF SUBROUTINE PSET --------------------- +C ---------------------- END OF SUBROUTINE A_PSET --------------------- END - SUBROUTINE DEC(N,NDIM,A,IP,IER) + SUBROUTINE A_DEC(N,NDIM,A,IP,IER) IMPLICIT DOUBLE PRECISION(A-H,O-Z) @@ -1301,9 +1301,9 @@ C IP(N) = (-1)**(NUMBER OF INTERCHANGES) OR 0. C IER = 0 IF MATRIX IS NON-SINGULAR, OR K IF FOUND TO BE SINGULAR C AT STAGE K. -C USE SOL TO OBTAIN SOLUTION OF LINEAR SYSTEM. +C USE A_SOL TO OBTAIN SOLUTION OF LINEAR SYSTEM. C DETERM(A) = IP(N)*A(1,1)*A(2,2)* . . . *A(N,N). -C IF IP(N) = 0, A IS SINGULAR, SOL WILL DIVIDE BY ZERO. +C IF IP(N) = 0, A IS SINGULAR, A_SOL WILL DIVIDE BY ZERO. C C REFERENCE. C C.B. MOLER, ALGORITHM 423, LINEAR EQUATION SOLVER, C.A.C.M @@ -1362,9 +1362,9 @@ 80 IER = K IP(N) = 0 RETURN -C --------------------- END OF SUBROUTINE DEC ---------------------- +C --------------------- END OF SUBROUTINE A_DEC ---------------------- END - SUBROUTINE SOL(N,NDIM,A,B,IP) + SUBROUTINE A_SOL(N,NDIM,A,B,IP) IMPLICIT DOUBLE PRECISION(A-H,O-Z) @@ -1386,10 +1386,10 @@ C INPUT .. C N = ORDER OF MATRIX. C NDIM = DECLARED DIMENSION OF MATRIX A. -C A = TRIANGULARISED MATRIX OBTAINED FROM DEC. +C A = TRIANGULARISED MATRIX OBTAINED FROM A_DEC. C B = RIGHT HAND SIDE VECTOR. -C IP = PIVOT VECTOR OBTAINED FROM DEC. -C DO NOT USE IF DEC HAS SET IER .NE. 0 +C IP = PIVOT VECTOR OBTAINED FROM A_DEC. +C DO NOT USE IF A_DEC HAS SET IER .NE. 0 C OUTPUT.. C B = SOLUTION VECTOR, X. C ------------------------------------------------------------------ @@ -1416,15 +1416,15 @@ 40 CONTINUE 50 B(1) = B(1)/A(1,1) RETURN -C ------------------------- END OF SUBROUTINE SOL ------------------ +C ------------------------- END OF SUBROUTINE A_SOL ------------------ END - subroutine dgbfa(abd,lda,n,ml,mu,ipvt,info) + subroutine a_dgbfa(abd,lda,n,ml,mu,ipvt,info) integer lda,n,ml,mu,ipvt(1),info double precision abd(lda,1) c -c dgbfa factors a double precision band matrix by elimination. +c a_dgbfa factors a double precision band matrix by elimination. c -c dgbfa is usually called by dgbco, but it can be called +c a_dgbfa is usually called by dgbco, but it can be called c directly with a saving in time if rcond is not needed. c c on entry @@ -1466,7 +1466,7 @@ c = 0 normal value. c = k if u(k,k) .eq. 0.0 . this is not an error c condition for this subroutine, but it does -c indicate that dgbsl will divide by zero if +c indicate that a_dgbsl will divide by zero if c called. use rcond in dgbco for a reliable c indication of singularity. c @@ -1593,151 +1593,18 @@ return end c - subroutine daxpy(n,da,dx,incx,dy,incy) -c -c constant times a vector plus a vector. -c uses unrolled loops for increments equal to one. -c jack dongarra, linpack, 3/11/78. -c - double precision dx(1),dy(1),da - integer i,incx,incy,ix,iy,m,mp1,n -c - if(n.le.0)return - if (da .eq. 0.0d0) return - if(incx.eq.1.and.incy.eq.1)go to 20 -c -c code for unequal increments or equal increments -c not equal to 1 -c - ix = 1 - iy = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - if(incy.lt.0)iy = (-n+1)*incy + 1 - do 10 i = 1,n - dy(iy) = dy(iy) + da*dx(ix) - ix = ix + incx - iy = iy + incy - 10 continue - return -c -c code for both increments equal to 1 -c -c -c clean-up loop -c - 20 m = mod(n,4) - if( m .eq. 0 ) go to 40 - do 30 i = 1,m - dy(i) = dy(i) + da*dx(i) - 30 continue - if( n .lt. 4 ) return - 40 mp1 = m + 1 - do 50 i = mp1,n,4 - dy(i) = dy(i) + da*dx(i) - dy(i + 1) = dy(i + 1) + da*dx(i + 1) - dy(i + 2) = dy(i + 2) + da*dx(i + 2) - dy(i + 3) = dy(i + 3) + da*dx(i + 3) - 50 continue - return - end -c - subroutine dscal(n,da,dx,incx) -c -c scales a vector by a constant. -c uses unrolled loops for increment equal to one. -c jack dongarra, linpack, 3/11/78. -c modified to correct problem with negative increment, 8/21/90. -c - double precision da,dx(1) - integer i,incx,ix,m,mp1,n -c - if(n.le.0)return - if(incx.eq.1)go to 20 -c -c code for increment not equal to 1 -c - ix = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - do 10 i = 1,n - dx(ix) = da*dx(ix) - ix = ix + incx - 10 continue - return -c -c code for increment equal to 1 -c -c -c clean-up loop -c - 20 m = mod(n,5) - if( m .eq. 0 ) go to 40 - do 30 i = 1,m - dx(i) = da*dx(i) - 30 continue - if( n .lt. 5 ) return - 40 mp1 = m + 1 - do 50 i = mp1,n,5 - dx(i) = da*dx(i) - dx(i + 1) = da*dx(i + 1) - dx(i + 2) = da*dx(i + 2) - dx(i + 3) = da*dx(i + 3) - dx(i + 4) = da*dx(i + 4) - 50 continue - return - end -c - integer function idamax(n,dx,incx) -c -c finds the index of element having max. absolute value. -c jack dongarra, linpack, 3/11/78. -c modified to correct problem with negative increment, 8/21/90. -c - double precision dx(1),dmax - integer i,incx,ix,n -c - idamax = 0 - if( n .lt. 1 ) return - idamax = 1 - if(n.eq.1)return - if(incx.eq.1)go to 20 -c -c code for increment not equal to 1 -c - ix = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - dmax = dabs(dx(ix)) - ix = ix + incx - do 10 i = 2,n - if(dabs(dx(ix)).le.dmax) go to 5 - idamax = i - dmax = dabs(dx(ix)) - 5 ix = ix + incx - 10 continue - return -c -c code for increment equal to 1 -c - 20 dmax = dabs(dx(1)) - do 30 i = 2,n - if(dabs(dx(i)).le.dmax) go to 30 - idamax = i - dmax = dabs(dx(i)) - 30 continue - return - end - - subroutine dgbsl(abd,lda,n,ml,mu,ipvt,b,job) + subroutine a_dgbsl(abd,lda,n,ml,mu,ipvt,b,job) integer lda,n,ml,mu,ipvt(*),job double precision abd(lda,*),b(*) c -c dgbsl solves the double precision band system +c a_dgbsl solves the double precision band system c a * x = b or trans(a) * x = b -c using the factors computed by dgbco or dgbfa. +c using the factors computed by dgbco or a_dgbfa. c c on entry c c abd double precision(lda, n) -c the output from dgbco or dgbfa. +c the output from dgbco or a_dgbfa. c c lda integer c the leading dimension of the array abd . @@ -1752,7 +1619,7 @@ c number of diagonals above the main diagonal. c c ipvt integer(n) -c the pivot vector from dgbco or dgbfa. +c the pivot vector from dgbco or a_dgbfa. c c b double precision(n) c the right hand side vector. @@ -1773,14 +1640,14 @@ c but it is often caused by improper arguments or improper c setting of lda . it will not occur if the subroutines are c called correctly and if dgbco has set rcond .gt. 0.0 -c or dgbfa has set info .eq. 0 . +c or a_dgbfa has set info .eq. 0 . c c to compute inverse(a) * c where c is a matrix c with p columns c call dgbco(abd,lda,n,ml,mu,ipvt,rcond,z) c if (rcond is too small) go to ... c do 10 j = 1, p -c call dgbsl(abd,lda,n,ml,mu,ipvt,c(1,j),0) +c call a_dgbsl(abd,lda,n,ml,mu,ipvt,c(1,j),0) c 10 continue c c linpack. this version dated 08/14/78 . @@ -1862,62 +1729,13 @@ return end c - double precision function ddot(n,dx,incx,dy,incy) -c -c forms the dot product of two vectors. -c uses unrolled loops for increments equal to one. -c jack dongarra, linpack, 3/11/78. -c - double precision dx(1),dy(1),dtemp - integer i,incx,incy,ix,iy,m,mp1,n -c - ddot = 0.0d0 - dtemp = 0.0d0 - if(n.le.0)return - if(incx.eq.1.and.incy.eq.1)go to 20 -c -c code for unequal increments or equal increments -c not equal to 1 -c - ix = 1 - iy = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - if(incy.lt.0)iy = (-n+1)*incy + 1 - do 10 i = 1,n - dtemp = dtemp + dx(ix)*dy(iy) - ix = ix + incx - iy = iy + incy - 10 continue - ddot = dtemp - return -c -c code for both increments equal to 1 -c -c -c clean-up loop -c - 20 m = mod(n,5) - if( m .eq. 0 ) go to 40 - do 30 i = 1,m - dtemp = dtemp + dx(i)*dy(i) - 30 continue - if( n .lt. 5 ) go to 60 - 40 mp1 = m + 1 - do 50 i = mp1,n,5 - dtemp = dtemp + dx(i)*dy(i) + dx(i + 1)*dy(i + 1) + - * dx(i + 2)*dy(i + 2) + dx(i + 3)*dy(i + 3) + dx(i + 4)*dy(i + 4) - 50 continue - 60 ddot = dtemp - return - end - - SUBROUTINE ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + SUBROUTINE A_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C *************************************************** C C THIS ROUTINE CALCULATES ERRORS USED IN TESTS -C IN STIFF . +C IN A_STIFF . C C *************************************************** C .. SCALAR ARGUMENTS .. @@ -1950,7 +1768,7 @@ C ** ERROR ASSOCIATED WITH METHOD OF ORDER TWO LOWER. RETURN END - SUBROUTINE PRDICT(T,H,Y,L,N,YPRIME,NFE,IPAR,RPAR,F,IERR) + SUBROUTINE A_PRDICT(T,H,Y,L,N,YPRIME,NFE,IPAR,RPAR,F,IERR) @@ -1987,10 +1805,10 @@ RETURN END - SUBROUTINE ITRAT2(QQQ,Y,N,T,HBETA,ERRBND,ARH,CRATE,TCRATE,M,WORKED - + ,YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND,AM,MASBND,NIND1, - + NIND2,NIND3,IPIV,LMB,ITOL,RTOL,ATOL,IPAR,RPAR,HUSED,NBSOL, - + NFE,NQUSED,F,IERR) + SUBROUTINE A_ITRAT2(QQQ,Y,N,T,HBETA,ERRBND,ARH,CRATE,TCRATE,M, + + WORKED,YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND,AM,MASBND, + + NIND1,NIND2,NIND3,IPIV,LMB,ITOL,RTOL,ATOL,IPAR,RPAR,HUSED, + + NBSOL,NFE,NQUSED,F,IERR) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C .. SCALAR ARGUMENTS .. @@ -2006,7 +1824,7 @@ INTEGER I C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL F,SOL,DGBSL + EXTERNAL F,A_SOL,A_DGBSL C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DMAX1,DMIN1 @@ -2077,10 +1895,10 @@ 8812 CONTINUE ENDIF IF(MF.GE.23) THEN - CALL DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) + CALL A_DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) NBSOL = NBSOL + 1 ELSE - CALL SOL(N,N,PW,SAVE1,IPIV) + CALL A_SOL(N,N,PW,SAVE1,IPIV) NBSOL = NBSOL + 1 ENDIF D = ZERO @@ -2131,10 +1949,10 @@ C IF WE ARE HERE THEN PARTIALS ARE O.K. C IF( MF.GE. 23) THEN - CALL DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) + CALL A_DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) NBSOL=NBSOL + 1 ELSE - CALL SOL(N,N,PW,SAVE1,IPIV) + CALL A_SOL(N,N,PW,SAVE1,IPIV) NBSOL = NBSOL + 1 ENDIF C @@ -2180,7 +1998,7 @@ END - SUBROUTINE STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND, + SUBROUTINE A_STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND, + MASBND,NIND1,NIND2,NIND3,T,TOUT,TEND,Y,N, + YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,PWCOPY,AM,YHOLD, + YNHOLD,ARH,IPIV,LOUT,MAXDER,ITOL,RTOL,ATOL,RPAR,IPAR,F, @@ -2191,13 +2009,13 @@ IMPLICIT DOUBLE PRECISION(A-H,O-Z) C ------------------------------------------------------------------ -C THE SUBROUTINE STIFF PERFORMS ONE STEP OF THE INTEGRATION OF AN +C THE SUBROUTINE A_STIFF PERFORMS ONE STEP OF THE INTEGRATION OF AN C INITIAL VALUE PROBLEM FOR A SYSTEM OF ORDINARY DIFFERENTIAL C EQUATIONS OR LINEARLY IMPLICIT DIFFERENTIAL ALGEBRAIC EQUATIONS. -C COMMUNICATION WITH STIFF IS DONE WITH THE FOLLOWING VARIABLES.. +C COMMUNICATION WITH A_STIFF IS DONE WITH THE FOLLOWING VARIABLES.. C Y AN N BY LMAX+3 ARRAY CONTAINING THE DEPENDENT VARIABLES C AND THEIR BACKWARD DIFFERENCES. MAXDER (=LMAX-1) IS THE -C MAXIMUM ORDER AVAILABLE. SEE SUBROUTINE COSET. +C MAXIMUM ORDER AVAILABLE. SEE SUBROUTINE A_COSET. C Y(I,J+1) CONTAINS THE JTH BACKWARD DIFFERENCE OF Y(I) C T THE INDEPENDENT VARIABLE. T IS UPDATED ON EACH STEP TAKEN. C H THE STEPSIZE TO BE ATTEMPTED ON THE NEXT STEP. @@ -2207,7 +2025,7 @@ C HMIN THE MINIMUM AND MAXIMUM ABSOLUTE VALUE OF THE STEPSIZE C HMAX TO BE USED FOR THE STEP. THESE MAY BE CHANGED AT ANY C TIME BUT WILL NOT TAKE EFFECT UNTIL THE NEXT H CHANGE. -C RTOL,ATOL THE ERROR BOUNDS. SEE DESCRIPTION IN OVDRIV. +C RTOL,ATOL THE ERROR BOUNDS. SEE DESCRIPTION IN A_OVDRIV. C N THE NUMBER OF FIRST ORDER DIFFERENTIAL EQUATIONS. C MF THE METHOD FLAG. MUST BE SET TO 21,22,23 OR 24 AT PRESENT C KFLAG A COMPLETION FLAG WITH THE FOLLOWING MEANINGS.. @@ -2242,7 +2060,7 @@ C MATRIX WAS FORMED BY A NEW J. C AVOLDJ STORES VALUE FOR AVERAGE CRATE WHEN ITERATION C MATRIX WAS FORMED BY AN OLD J. -C NRENEW FLAG THAT IS USED IN COMMUNICATION WITH SUBROUTINE PSET. +C NRENEW FLAG THAT IS USED IN COMMUNICATION WITH SUBROUTINE A_PSET. C IF NRENEW > 0 THEN FORM A NEW JACOBIAN BEFORE C COMPUTING THE COEFFICIENT MATRIX FOR C THE NEWTON-RAPHSON ITERATION @@ -2271,8 +2089,8 @@ DIMENSION EL(10),ELST(10),TQ(5) C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL COSET,CPYARY,ERRORS,F,HCHOSE,ITRAT2, - + PRDICT,PSET,RSCALE,SOL,DGBSL,PDERV,MAS + EXTERNAL A_COSET,A_CPYARY,A_ERRORS,F,A_HCHOSE,A_ITRAT2, + + A_PRDICT,A_PSET,A_RSCALE,A_SOL,A_DGBSL,PDERV,MAS C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DABS,DMAX1,DMIN1 @@ -2378,7 +2196,7 @@ C BE RE-SCALED. IF H IS CHANGED, IDOUB IS SET TO L+1 TO PREVENT C FURTHER CHANGES IN H FOR THAT MANY STEPS. C ----------------------------------------------------------------- - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL A_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) LMAX = MAXDER + 1 RC = RC*EL(1)/OLDLO OLDLO = EL(1) @@ -2389,20 +2207,20 @@ C NRENEW AND NEWPAR ARE TO INSTRUCT ROUTINE THAT C WE WISH A NEW J TO BE CALCULATED FOR THIS STEP. C ***************************************************** - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL A_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) DO 20 I = 1,N ARH(I) = EL(2)*Y(I,1) 20 CONTINUE - CALL CPYARY(N*L,Y,YHOLD) + CALL A_CPYARY(N*L,Y,YHOLD) QI = H*EL(1) QQ = ONE/QI - CALL PRDICT(T,H,Y,L,N,SAVE2,NFE,IPAR,RPAR,F,IERR) + CALL A_PRDICT(T,H,Y,L,N,SAVE2,NFE,IPAR,RPAR,F,IERR) IF(IERR.NE.0) GOTO 8000 GO TO 110 C >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> C DIFFERENT PARAMETERS ON THIS CALL < C <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - 30 CALL CPYARY(N*L,YHOLD,Y) + 30 CALL A_CPYARY(N*L,YHOLD,Y) IF (MF.NE.MFOLD) THEN METH = MF/10 MITER = MF - 10*METH @@ -2445,7 +2263,7 @@ C ********************************************* 40 RH = DMAX1(RH,HMIN/DABS(H)) 50 RH = DMIN1(RH,HMAX/DABS(H),RMAX) - CALL RSCALE(N,L,RH,Y) + CALL A_RSCALE(N,L,RH,Y) RMAX = 10.0D+0 JCHANG = 1 H = H*RH @@ -2462,7 +2280,7 @@ END IF IDOUB = L + 1 - CALL CPYARY(N*L,Y,YHOLD) + CALL A_CPYARY(N*L,Y,YHOLD) 60 IF (DABS(RC-ONE).GT.UPBND) IWEVAL = MITER HUSED = H @@ -2487,7 +2305,7 @@ IF (JCHANG.EQ.1) THEN C IF WE HAVE CHANGED STEPSIZE THEN PREDICT A VALUE FOR Y(T+H) C AND EVALUATE THE DERIVATIVE THERE (STORED IN SAVE2()) - CALL PRDICT(T,H,Y,L,N,SAVE2,NFE,IPAR,RPAR,F,IERR) + CALL A_PRDICT(T,H,Y,L,N,SAVE2,NFE,IPAR,RPAR,F,IERR) IF(IERR.NE.0) GOTO 8000 ELSE @@ -2507,7 +2325,7 @@ C ------------------------------------------------------------------- C IF INDICATED, THE MATRIX P = I/(H*EL(2)) - J IS RE-EVALUATED BEFORE C STARTING THE CORRECTOR ITERATION. IWEVAL IS SET = 0 TO INDICATE -C THAT THIS HAS BEEN DONE. P IS COMPUTED AND PROCESSED IN PSET. +C THAT THIS HAS BEEN DONE. P IS COMPUTED AND PROCESSED IN A_PSET. C THE PROCESSED MATRIX IS STORED IN PW C ------------------------------------------------------------------- IWEVAL = 0 @@ -2573,13 +2391,13 @@ JSNOLD = 0 MQ1TMP = MEQC1 MQ2TMP = MEQC2 - CALL PSET(Y,N,H,T,UROUND,EPSJAC,QI,MITER,MBND,MASBND, + CALL A_PSET(Y,N,H,T,UROUND,EPSJAC,QI,MITER,MBND,MASBND, + NIND1,NIND2,NIND3,IER,F,PDERV,MAS,NRENEW,YMAX,SAVE1,SAVE2, + PW,PWCOPY,AM,ERROR,IPIV,ITOL,RTOL,ATOL,NPSET,NJE,NFE,NDEC,IPAR + ,RPAR,IERR) IF(IERR.NE.0) GOTO 8000 QQQ=QI -C NOTE THAT ERROR() IS JUST BEING USED AS A WORKSPACE BY PSET +C NOTE THAT ERROR() IS JUST BEING USED AS A WORKSPACE BY A_PSET IF (IER.NE.0) THEN C IF IER>0 THEN WE HAVE HAD A SINGULARITY IN THE ITERATION MATRIX IJUS=1 @@ -2603,7 +2421,7 @@ C LOOP. THE UPDATED Y VECTOR IS STORED TEMPORARILY IN SAVE1. C ********************************************************************** IF (.NOT.SAMPLE) THEN - CALL ITRAT2(QQQ,Y,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1,WORKED,YMAX, + CALL A_ITRAT2(QQQ,Y,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1,WORKED,YMAX, + ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND,AM,MASBND, + NIND1,NIND2,NIND3,IPIV,1,ITOL,RTOL,ATOL,IPAR,RPAR,HUSED,NBSOL, + NFE,NQUSED,F,IERR) @@ -2611,7 +2429,7 @@ ITST = 2 ELSE - CALL ITRAT2(QQQ,Y,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1,WORKED,YMAX, + CALL A_ITRAT2(QQQ,Y,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1,WORKED,YMAX, + ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND,AM,MASBND, +NIND1,NIND2,NIND3,IPIV,0,ITOL,RTOL,ATOL,IPAR,RPAR,HUSED,NBSOL, + NFE,NQUSED,F,IERR) @@ -2752,7 +2570,7 @@ ARH(I) = ARH(I) + EL(JP1)*Y(I,J1) 200 CONTINUE 210 CONTINUE - CALL PRDICT(T,H,Y,L,N,SAVE2,NFE,IPAR,RPAR,F,IERR) + CALL A_PRDICT(T,H,Y,L,N,SAVE2,NFE,IPAR,RPAR,F,IERR) IF(IERR.NE.0) GOTO 8000 DO 220 I = 1,N SAVE1(I) = Y(I,1) @@ -2763,7 +2581,7 @@ C FOR NOW WILL ASSUME THAT WE DO NOT WISH TO SAMPLE C AT THE N+2 STEP POINT C - CALL ITRAT2(QQQ,Y,N,T,QI,BND,ARH,CRATE2,TCRAT2,M2,WORKED,YMAX, + CALL A_ITRAT2(QQQ,Y,N,T,QI,BND,ARH,CRATE2,TCRAT2,M2,WORKED,YMAX, + ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND,AM,MASBND, +NIND1,NIND2,NIND3,IPIV,1,ITOL,RTOL,ATOL,IPAR,RPAR,HUSED,NBSOL, + NFE,NQUSED,F,IERR) @@ -2872,10 +2690,10 @@ 3111 CONTINUE ENDIF IF (MF.GE. 23) THEN - CALL DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) + CALL A_DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) NBSOL=NBSOL+1 ELSE - CALL SOL(N,N,PW,SAVE1,IPIV) + CALL A_SOL(N,N,PW,SAVE1,IPIV) NBSOL = NBSOL + 1 ENDIF DO 321 I=1,N @@ -2971,7 +2789,7 @@ IF(NQ.GT.1) FFAIL = 0.5D+0/DBLE(FLOAT(NQ)) IF(NQ.GT.2) FRFAIL = 0.5D+0/DBLE(FLOAT(NQ-1)) EFAIL = 0.5D+0/DBLE(FLOAT(L)) - CALL CPYARY(N*L,YHOLD,Y) + CALL A_CPYARY(N*L,YHOLD,Y) RMAX = 2.0D+0 IF (DABS(H).LE.HMIN*1.00001D+0) THEN C @@ -3000,10 +2818,10 @@ NQ=NEWQ RH=ONE/(PLFAIL*DBLE(FLOAT(-KFAIL))) L=NQ+1 - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL A_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) RC=RC*EL(1)/OLDLO OLDLO=EL(1) - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL A_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) ELSE NEWQ = NQ RH = ONE/ (PRFAIL*DBLE(FLOAT(-KFAIL))) @@ -3029,7 +2847,7 @@ C ********************************* JCHANG = 1 RH = DMAX1(HMIN/DABS(H),0.1D+0) - CALL HCHOSE(RH,H,OVRIDE) + CALL A_HCHOSE(RH,H,OVRIDE) H = H*RH CALL F(N,T,YHOLD,SAVE1,IPAR,RPAR,IERR) IF(IERR.NE.0) GOTO 8000 @@ -3048,11 +2866,11 @@ NQ = 1 L = 2 C RESET ORDER, RECALCULATE ERROR BOUNDS - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL A_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) LMAX = MAXDER + 1 RC = RC*EL(1)/OLDLO OLDLO = EL(1) - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL A_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) C NOW JUMP TO NORMAL CONTINUATION POINT GO TO 60 C ********************************************************************** @@ -3216,7 +3034,7 @@ GOTO 440 ENDIF RH = DMIN1(RH,RMAX) - CALL HCHOSE(RH,H,OVRIDE) + CALL A_HCHOSE(RH,H,OVRIDE) IF ((JSINUP.LE.20).AND.(KFLAG.EQ.0).AND.(RH.LT.1.1D+0)) THEN C WE HAVE RUN INTO PROBLEMS IDOUB = 10 @@ -3244,17 +3062,17 @@ NQ = NEWQ L = NQ + 1 C RESET ORDER,RECALCULATE ERROR BOUNDS - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL A_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) LMAX = MAXDER + 1 RC = RC*EL(1)/OLDLO OLDLO = EL(1) - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL A_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) END IF RH = DMAX1(RH,HMIN/DABS(H)) RH = DMIN1(RH,HMAX/DABS(H),RMAX) - CALL RSCALE(N,L,RH,Y) + CALL A_RSCALE(N,L,RH,Y) RMAX = 10.0D+0 JCHANG = 1 H = H*RH @@ -3271,7 +3089,7 @@ C INFORMATION NECESSARY TO PERFORM AN INTERPOLATION TO FIND THE C SOLUTION AT THE SPECIFIED OUTPUT POINT IF APPROPRIATE. C ---------------------------------------------------------------------- - CALL CPYARY(N*L,Y,YHOLD) + CALL A_CPYARY(N*L,Y,YHOLD) NSTEP = NSTEP + 1 JSINUP = JSINUP + 1 JSNOLD = JSNOLD + 1 @@ -3312,17 +3130,17 @@ C TRY AGAIN WITH UPDATED PARTIALS C 8000 IF(IERR.NE.0) RETURN - IF(IJUS.EQ.0) CALL HCHOSE(RH,H,OVRIDE) + IF(IJUS.EQ.0) CALL A_HCHOSE(RH,H,OVRIDE) IF(.NOT.FINISH) THEN GO TO 40 ELSE RETURN END IF -C ------------------- END OF SUBROUTINE STIFF -------------------------- +C ------------------- END OF SUBROUTINE A_STIFF -------------------------- 9000 FORMAT (1X,' CORRECTOR HAS NOT CONVERGED') END - SUBROUTINE RSCALE(N,L,RH,Y) + SUBROUTINE A_RSCALE(N,L,RH,Y) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C .. SCALAR ARGUMENTS .. @@ -3432,7 +3250,7 @@ RETURN END - SUBROUTINE CPYARY(NELEM,SOURCE,TARGET) + SUBROUTINE A_CPYARY(NELEM,SOURCE,TARGET) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C C COPIES THE ARRAY SOURCE() INTO THE ARRAY TARGET() @@ -3455,7 +3273,7 @@ RETURN END - SUBROUTINE HCHOSE(RH,H,OVRIDE) + SUBROUTINE A_HCHOSE(RH,H,OVRIDE) IMPLICIT DOUBLE PRECISION(A-H,O-Z) COMMON / STPSZE / HSTPSZ(2,14) LOGICAL OVRIDE @@ -3492,947 +3310,3 @@ C ************************************************************ C END - DOUBLE PRECISION FUNCTION DLAMCH( CMACH ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - CHARACTER CMACH -* .. -* -* Purpose -* ======= -* -* DLAMCH determines double precision machine parameters. -* -* Arguments -* ========= -* -* CMACH (input) CHARACTER*1 -* Specifies the value to be returned by DLAMCH: -* = 'E' or 'e', DLAMCH := eps -* = 'S' or 's , DLAMCH := sfmin -* = 'B' or 'b', DLAMCH := base -* = 'P' or 'p', DLAMCH := eps*base -* = 'N' or 'n', DLAMCH := t -* = 'R' or 'r', DLAMCH := rnd -* = 'M' or 'm', DLAMCH := emin -* = 'U' or 'u', DLAMCH := rmin -* = 'L' or 'l', DLAMCH := emax -* = 'O' or 'o', DLAMCH := rmax -* -* where -* -* eps = relative machine precision -* sfmin = safe minimum, such that 1/sfmin does not overflow -* base = base of the machine -* prec = eps*base -* t = number of (base) digits in the mantissa -* rnd = 1.0 when rounding occurs in addition, 0.0 otherwise -* emin = minimum exponent before (gradual) underflow -* rmin = underflow threshold - base**(emin-1) -* emax = largest exponent before overflow -* rmax = overflow threshold - (base**emax)*(1-eps) -* -* ===================================================================== -* -* .. Parameters .. - DOUBLE PRECISION ONE, ZERO - PARAMETER ( ONE = 1.0D+0, ZERO = 0.0D+0 ) -* .. -* .. Local Scalars .. - LOGICAL FIRST, LRND - INTEGER BETA, IMAX, IMIN, IT - DOUBLE PRECISION BASE, EMAX, EMIN, EPS, PREC, RMACH, RMAX, RMIN, - $ RND, SFMIN, SMALL, T -* .. -* .. External Functions .. - LOGICAL LSAME - EXTERNAL LSAME -* .. -* .. External Subroutines .. - EXTERNAL DLAMC2 -* .. -* .. Save statement .. - SAVE FIRST, EPS, SFMIN, BASE, T, RND, EMIN, RMIN, - $ EMAX, RMAX, PREC -* .. -* .. Data statements .. - DATA FIRST / .TRUE. / -* .. -* .. Executable Statements .. -* - IF( FIRST ) THEN - FIRST = .FALSE. - CALL DLAMC2( BETA, IT, LRND, EPS, IMIN, RMIN, IMAX, RMAX ) - BASE = BETA - T = IT - IF( LRND ) THEN - RND = ONE - EPS = ( BASE**( 1-IT ) ) / 2 - ELSE - RND = ZERO - EPS = BASE**( 1-IT ) - END IF - PREC = EPS*BASE - EMIN = IMIN - EMAX = IMAX - SFMIN = RMIN - SMALL = ONE / RMAX - IF( SMALL.GE.SFMIN ) THEN -* -* Use SMALL plus a bit, to avoid the possibility of rounding -* causing overflow when computing 1/sfmin. -* - SFMIN = SMALL*( ONE+EPS ) - END IF - END IF -* - IF( LSAME( CMACH, 'E' ) ) THEN - RMACH = EPS - ELSE IF( LSAME( CMACH, 'S' ) ) THEN - RMACH = SFMIN - ELSE IF( LSAME( CMACH, 'B' ) ) THEN - RMACH = BASE - ELSE IF( LSAME( CMACH, 'P' ) ) THEN - RMACH = PREC - ELSE IF( LSAME( CMACH, 'N' ) ) THEN - RMACH = T - ELSE IF( LSAME( CMACH, 'R' ) ) THEN - RMACH = RND - ELSE IF( LSAME( CMACH, 'M' ) ) THEN - RMACH = EMIN - ELSE IF( LSAME( CMACH, 'U' ) ) THEN - RMACH = RMIN - ELSE IF( LSAME( CMACH, 'L' ) ) THEN - RMACH = EMAX - ELSE IF( LSAME( CMACH, 'O' ) ) THEN - RMACH = RMAX - END IF -* - DLAMCH = RMACH - RETURN -* -* End of DLAMCH -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC1( BETA, T, RND, IEEE1 ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - LOGICAL IEEE1, RND - INTEGER BETA, T -* .. -* -* Purpose -* ======= -* -* DLAMC1 determines the machine parameters given by BETA, T, RND, and -* IEEE1. -* -* Arguments -* ========= -* -* BETA (output) INTEGER -* The base of the machine. -* -* T (output) INTEGER -* The number of ( BETA ) digits in the mantissa. -* -* RND (output) LOGICAL -* Specifies whether proper rounding ( RND = .TRUE. ) or -* chopping ( RND = .FALSE. ) occurs in addition. This may not -* be a reliable guide to the way in which the machine performs -* its arithmetic. -* -* IEEE1 (output) LOGICAL -* Specifies whether rounding appears to be done in the IEEE -* 'round to nearest' style. -* -* Further Details -* =============== -* -* The routine is based on the routine ENVRON by Malcolm and -* incorporates suggestions by Gentleman and Marovich. See -* -* Malcolm M. A. (1972) Algorithms to reveal properties of -* floating-point arithmetic. Comms. of the ACM, 15, 949-951. -* -* Gentleman W. M. and Marovich S. B. (1974) More on algorithms -* that reveal properties of floating point arithmetic units. -* Comms. of the ACM, 17, 276-277. -* -* ===================================================================== -* -* .. Local Scalars .. - LOGICAL FIRST, LIEEE1, LRND - INTEGER LBETA, LT - DOUBLE PRECISION A, B, C, F, ONE, QTR, SAVEC, T1, T2 -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. Save statement .. - SAVE FIRST, LIEEE1, LBETA, LRND, LT -* .. -* .. Data statements .. - DATA FIRST / .TRUE. / -* .. -* .. Executable Statements .. -* - IF( FIRST ) THEN - FIRST = .FALSE. - ONE = 1 -* -* LBETA, LIEEE1, LT and LRND are the local values of BETA, -* IEEE1, T and RND. -* -* Throughout this routine we use the function DLAMC3 to ensure -* that relevant values are stored and not held in registers, or -* are not affected by optimizers. -* -* Compute a = 2.0**m with the smallest positive integer m such -* that -* -* fl( a + 1.0 ) = a. -* - A = 1 - C = 1 -* -*+ WHILE( C.EQ.ONE )LOOP - 10 CONTINUE - IF( C.EQ.ONE ) THEN - A = 2*A - C = DLAMC3( A, ONE ) - C = DLAMC3( C, -A ) - GO TO 10 - END IF -*+ END WHILE -* -* Now compute b = 2.0**m with the smallest positive integer m -* such that -* -* fl( a + b ) .gt. a. -* - B = 1 - C = DLAMC3( A, B ) -* -*+ WHILE( C.EQ.A )LOOP - 20 CONTINUE - IF( C.EQ.A ) THEN - B = 2*B - C = DLAMC3( A, B ) - GO TO 20 - END IF -*+ END WHILE -* -* Now compute the base. a and c are neighbouring floating point -* numbers in the interval ( beta**t, beta**( t + 1 ) ) and so -* their difference is beta. Adding 0.25 to c is to ensure that it -* is truncated to beta and not ( beta - 1 ). -* - QTR = ONE / 4 - SAVEC = C - C = DLAMC3( C, -A ) - LBETA = C + QTR -* -* Now determine whether rounding or chopping occurs, by adding a -* bit less than beta/2 and a bit more than beta/2 to a. -* - B = LBETA - F = DLAMC3( B / 2, -B / 100 ) - C = DLAMC3( F, A ) - IF( C.EQ.A ) THEN - LRND = .TRUE. - ELSE - LRND = .FALSE. - END IF - F = DLAMC3( B / 2, B / 100 ) - C = DLAMC3( F, A ) - IF( ( LRND ) .AND. ( C.EQ.A ) ) - $ LRND = .FALSE. -* -* Try and decide whether rounding is done in the IEEE 'round to -* nearest' style. B/2 is half a unit in the last place of the two -* numbers A and SAVEC. Furthermore, A is even, i.e. has last bit -* zero, and SAVEC is odd. Thus adding B/2 to A should not change -* A, but adding B/2 to SAVEC should change SAVEC. -* - T1 = DLAMC3( B / 2, A ) - T2 = DLAMC3( B / 2, SAVEC ) - LIEEE1 = ( T1.EQ.A ) .AND. ( T2.GT.SAVEC ) .AND. LRND -* -* Now find the mantissa, t. It should be the integer part of -* log to the base beta of a, however it is safer to determine t -* by powering. So we find t as the smallest positive integer for -* which -* -* fl( beta**t + 1.0 ) = 1.0. -* - LT = 0 - A = 1 - C = 1 -* -*+ WHILE( C.EQ.ONE )LOOP - 30 CONTINUE - IF( C.EQ.ONE ) THEN - LT = LT + 1 - A = A*LBETA - C = DLAMC3( A, ONE ) - C = DLAMC3( C, -A ) - GO TO 30 - END IF -*+ END WHILE -* - END IF -* - BETA = LBETA - T = LT - RND = LRND - IEEE1 = LIEEE1 - RETURN -* -* End of DLAMC1 -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC2( BETA, T, RND, EPS, EMIN, RMIN, EMAX, RMAX ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - LOGICAL RND - INTEGER BETA, EMAX, EMIN, T - DOUBLE PRECISION EPS, RMAX, RMIN -* .. -* -* Purpose -* ======= -* -* DLAMC2 determines the machine parameters specified in its argument -* list. -* -* Arguments -* ========= -* -* BETA (output) INTEGER -* The base of the machine. -* -* T (output) INTEGER -* The number of ( BETA ) digits in the mantissa. -* -* RND (output) LOGICAL -* Specifies whether proper rounding ( RND = .TRUE. ) or -* chopping ( RND = .FALSE. ) occurs in addition. This may not -* be a reliable guide to the way in which the machine performs -* its arithmetic. -* -* EPS (output) DOUBLE PRECISION -* The smallest positive number such that -* -* fl( 1.0 - EPS ) .LT. 1.0, -* -* where fl denotes the computed value. -* -* EMIN (output) INTEGER -* The minimum exponent before (gradual) underflow occurs. -* -* RMIN (output) DOUBLE PRECISION -* The smallest normalized number for the machine, given by -* BASE**( EMIN - 1 ), where BASE is the floating point value -* of BETA. -* -* EMAX (output) INTEGER -* The maximum exponent before overflow occurs. -* -* RMAX (output) DOUBLE PRECISION -* The largest positive number for the machine, given by -* BASE**EMAX * ( 1 - EPS ), where BASE is the floating point -* value of BETA. -* -* Further Details -* =============== -* -* The computation of EPS is based on a routine PARANOIA by -* W. Kahan of the University of California at Berkeley. -* -* ===================================================================== -* -* .. Local Scalars .. - LOGICAL FIRST, IEEE, IWARN, LIEEE1, LRND - INTEGER GNMIN, GPMIN, I, LBETA, LEMAX, LEMIN, LT, - $ NGNMIN, NGPMIN - DOUBLE PRECISION A, B, C, HALF, LEPS, LRMAX, LRMIN, ONE, RBASE, - $ SIXTH, SMALL, THIRD, TWO, ZERO -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. External Subroutines .. - EXTERNAL DLAMC1, DLAMC4, DLAMC5 -* .. -* .. Intrinsic Functions .. - INTRINSIC ABS, MAX, MIN -* .. -* .. Save statement .. - SAVE FIRST, IWARN, LBETA, LEMAX, LEMIN, LEPS, LRMAX, - $ LRMIN, LT -* .. -* .. Data statements .. - DATA FIRST / .TRUE. / , IWARN / .FALSE. / -* .. -* .. Executable Statements .. -* - IF( FIRST ) THEN - FIRST = .FALSE. - ZERO = 0 - ONE = 1 - TWO = 2 -* -* LBETA, LT, LRND, LEPS, LEMIN and LRMIN are the local values of -* BETA, T, RND, EPS, EMIN and RMIN. -* -* Throughout this routine we use the function DLAMC3 to ensure -* that relevant values are stored and not held in registers, or -* are not affected by optimizers. -* -* DLAMC1 returns the parameters LBETA, LT, LRND and LIEEE1. -* - CALL DLAMC1( LBETA, LT, LRND, LIEEE1 ) -* -* Start to find EPS. -* - B = LBETA - A = B**( -LT ) - LEPS = A -* -* Try some tricks to see whether or not this is the correct EPS. -* - B = TWO / 3 - HALF = ONE / 2 - SIXTH = DLAMC3( B, -HALF ) - THIRD = DLAMC3( SIXTH, SIXTH ) - B = DLAMC3( THIRD, -HALF ) - B = DLAMC3( B, SIXTH ) - B = ABS( B ) - IF( B.LT.LEPS ) - $ B = LEPS -* - LEPS = 1 -* -*+ WHILE( ( LEPS.GT.B ).AND.( B.GT.ZERO ) )LOOP - 10 CONTINUE - IF( ( LEPS.GT.B ) .AND. ( B.GT.ZERO ) ) THEN - LEPS = B - C = DLAMC3( HALF*LEPS, ( TWO**5 )*( LEPS**2 ) ) - C = DLAMC3( HALF, -C ) - B = DLAMC3( HALF, C ) - C = DLAMC3( HALF, -B ) - B = DLAMC3( HALF, C ) - GO TO 10 - END IF -*+ END WHILE -* - IF( A.LT.LEPS ) - $ LEPS = A -* -* Computation of EPS complete. -* -* Now find EMIN. Let A = + or - 1, and + or - (1 + BASE**(-3)). -* Keep dividing A by BETA until (gradual) underflow occurs. This -* is detected when we cannot recover the previous A. -* - RBASE = ONE / LBETA - SMALL = ONE - DO 20 I = 1, 3 - SMALL = DLAMC3( SMALL*RBASE, ZERO ) - 20 CONTINUE - A = DLAMC3( ONE, SMALL ) - CALL DLAMC4( NGPMIN, ONE, LBETA ) - CALL DLAMC4( NGNMIN, -ONE, LBETA ) - CALL DLAMC4( GPMIN, A, LBETA ) - CALL DLAMC4( GNMIN, -A, LBETA ) - IEEE = .FALSE. -* - IF( ( NGPMIN.EQ.NGNMIN ) .AND. ( GPMIN.EQ.GNMIN ) ) THEN - IF( NGPMIN.EQ.GPMIN ) THEN - LEMIN = NGPMIN -* ( Non twos-complement machines, no gradual underflow; -* e.g., VAX ) - ELSE IF( ( GPMIN-NGPMIN ).EQ.3 ) THEN - LEMIN = NGPMIN - 1 + LT - IEEE = .TRUE. -* ( Non twos-complement machines, with gradual underflow; -* e.g., IEEE standard followers ) - ELSE - LEMIN = MIN( NGPMIN, GPMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -* - ELSE IF( ( NGPMIN.EQ.GPMIN ) .AND. ( NGNMIN.EQ.GNMIN ) ) THEN - IF( ABS( NGPMIN-NGNMIN ).EQ.1 ) THEN - LEMIN = MAX( NGPMIN, NGNMIN ) -* ( Twos-complement machines, no gradual underflow; -* e.g., CYBER 205 ) - ELSE - LEMIN = MIN( NGPMIN, NGNMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -* - ELSE IF( ( ABS( NGPMIN-NGNMIN ).EQ.1 ) .AND. - $ ( GPMIN.EQ.GNMIN ) ) THEN - IF( ( GPMIN-MIN( NGPMIN, NGNMIN ) ).EQ.3 ) THEN - LEMIN = MAX( NGPMIN, NGNMIN ) - 1 + LT -* ( Twos-complement machines with gradual underflow; -* no known machine ) - ELSE - LEMIN = MIN( NGPMIN, NGNMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -* - ELSE - LEMIN = MIN( NGPMIN, NGNMIN, GPMIN, GNMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -*** -* Comment out this if block if EMIN is ok - IF( IWARN ) THEN - FIRST = .TRUE. - WRITE( 6, FMT = 9999 )LEMIN - END IF -*** -* -* Assume IEEE arithmetic if we found denormalised numbers above, -* or if arithmetic seems to round in the IEEE style, determined -* in routine DLAMC1. A true IEEE machine should have both things -* true; however, faulty machines may have one or the other. -* - IEEE = IEEE .OR. LIEEE1 -* -* Compute RMIN by successive division by BETA. We could compute -* RMIN as BASE**( EMIN - 1 ), but some machines underflow during -* this computation. -* - LRMIN = 1 - DO 30 I = 1, 1 - LEMIN - LRMIN = DLAMC3( LRMIN*RBASE, ZERO ) - 30 CONTINUE -* -* Finally, call DLAMC5 to compute EMAX and RMAX. -* - CALL DLAMC5( LBETA, LT, LEMIN, IEEE, LEMAX, LRMAX ) - END IF -* - BETA = LBETA - T = LT - RND = LRND - EPS = LEPS - EMIN = LEMIN - RMIN = LRMIN - EMAX = LEMAX - RMAX = LRMAX -* - RETURN -* - 9999 FORMAT( / / ' WARNING. The value EMIN may be incorrect:-', - $ ' EMIN = ', I8, / - $ ' If, after inspection, the value EMIN looks', - $ ' acceptable please comment out ', - $ / ' the IF block as marked within the code of routine', - $ ' DLAMC2,', / ' otherwise supply EMIN explicitly.', / ) -* -* End of DLAMC2 -* - END -* -************************************************************************ -* - DOUBLE PRECISION FUNCTION DLAMC3( A, B ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - DOUBLE PRECISION A, B -* .. -* -* Purpose -* ======= -* -* DLAMC3 is intended to force A and B to be stored prior to doing -* the addition of A and B , for use in situations where optimizers -* might hold one of these in a register. -* -* Arguments -* ========= -* -* A, B (input) DOUBLE PRECISION -* The values A and B. -* -* ===================================================================== -* -* .. Executable Statements .. -* - DLAMC3 = A + B -* - RETURN -* -* End of DLAMC3 -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC4( EMIN, START, BASE ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - INTEGER BASE, EMIN - DOUBLE PRECISION START -* .. -* -* Purpose -* ======= -* -* DLAMC4 is a service routine for DLAMC2. -* -* Arguments -* ========= -* -* EMIN (output) EMIN -* The minimum exponent before (gradual) underflow, computed by -* setting A = START and dividing by BASE until the previous A -* can not be recovered. -* -* START (input) DOUBLE PRECISION -* The starting point for determining EMIN. -* -* BASE (input) INTEGER -* The base of the machine. -* -* ===================================================================== -* -* .. Local Scalars .. - INTEGER I - DOUBLE PRECISION A, B1, B2, C1, C2, D1, D2, ONE, RBASE, ZERO -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. Executable Statements .. -* - A = START - ONE = 1 - RBASE = ONE / BASE - ZERO = 0 - EMIN = 1 - B1 = DLAMC3( A*RBASE, ZERO ) - C1 = A - C2 = A - D1 = A - D2 = A -*+ WHILE( ( C1.EQ.A ).AND.( C2.EQ.A ).AND. -* $ ( D1.EQ.A ).AND.( D2.EQ.A ) )LOOP - 10 CONTINUE - IF( ( C1.EQ.A ) .AND. ( C2.EQ.A ) .AND. ( D1.EQ.A ) .AND. - $ ( D2.EQ.A ) ) THEN - EMIN = EMIN - 1 - A = B1 - B1 = DLAMC3( A / BASE, ZERO ) - C1 = DLAMC3( B1*BASE, ZERO ) - D1 = ZERO - DO 20 I = 1, BASE - D1 = D1 + B1 - 20 CONTINUE - B2 = DLAMC3( A*RBASE, ZERO ) - C2 = DLAMC3( B2 / RBASE, ZERO ) - D2 = ZERO - DO 30 I = 1, BASE - D2 = D2 + B2 - 30 CONTINUE - GO TO 10 - END IF -*+ END WHILE -* - RETURN -* -* End of DLAMC4 -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC5( BETA, P, EMIN, IEEE, EMAX, RMAX ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - LOGICAL IEEE - INTEGER BETA, EMAX, EMIN, P - DOUBLE PRECISION RMAX -* .. -* -* Purpose -* ======= -* -* DLAMC5 attempts to compute RMAX, the largest machine floating-point -* number, without overflow. It assumes that EMAX + abs(EMIN) sum -* approximately to a power of 2. It will fail on machines where this -* assumption does not hold, for example, the Cyber 205 (EMIN = -28625, -* EMAX = 28718). It will also fail if the value supplied for EMIN is -* too large (i.e. too close to zero), probably with overflow. -* -* Arguments -* ========= -* -* BETA (input) INTEGER -* The base of floating-point arithmetic. -* -* P (input) INTEGER -* The number of base BETA digits in the mantissa of a -* floating-point value. -* -* EMIN (input) INTEGER -* The minimum exponent before (gradual) underflow. -* -* IEEE (input) LOGICAL -* A logical flag specifying whether or not the arithmetic -* system is thought to comply with the IEEE standard. -* -* EMAX (output) INTEGER -* The largest exponent before overflow -* -* RMAX (output) DOUBLE PRECISION -* The largest machine floating-point number. -* -* ===================================================================== -* -* .. Parameters .. - DOUBLE PRECISION ZERO, ONE - PARAMETER ( ZERO = 0.0D0, ONE = 1.0D0 ) -* .. -* .. Local Scalars .. - INTEGER EXBITS, EXPSUM, I, LEXP, NBITS, TRY, UEXP - DOUBLE PRECISION OLDY, RECBAS, Y, Z -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. Intrinsic Functions .. - INTRINSIC MOD -* .. -* .. Executable Statements .. -* -* First compute LEXP and UEXP, two powers of 2 that bound -* abs(EMIN). We then assume that EMAX + abs(EMIN) will sum -* approximately to the bound that is closest to abs(EMIN). -* (EMAX is the exponent of the required number RMAX). -* - LEXP = 1 - EXBITS = 1 - 10 CONTINUE - TRY = LEXP*2 - IF( TRY.LE.( -EMIN ) ) THEN - LEXP = TRY - EXBITS = EXBITS + 1 - GO TO 10 - END IF - IF( LEXP.EQ.-EMIN ) THEN - UEXP = LEXP - ELSE - UEXP = TRY - EXBITS = EXBITS + 1 - END IF -* -* Now -LEXP is less than or equal to EMIN, and -UEXP is greater -* than or equal to EMIN. EXBITS is the number of bits needed to -* store the exponent. -* - IF( ( UEXP+EMIN ).GT.( -LEXP-EMIN ) ) THEN - EXPSUM = 2*LEXP - ELSE - EXPSUM = 2*UEXP - END IF -* -* EXPSUM is the exponent range, approximately equal to -* EMAX - EMIN + 1 . -* - EMAX = EXPSUM + EMIN - 1 - NBITS = 1 + EXBITS + P -* -* NBITS is the total number of bits needed to store a -* floating-point number. -* - IF( ( MOD( NBITS, 2 ).EQ.1 ) .AND. ( BETA.EQ.2 ) ) THEN -* -* Either there are an odd number of bits used to store a -* floating-point number, which is unlikely, or some bits are -* not used in the representation of numbers, which is possible, -* (e.g. Cray machines) or the mantissa has an implicit bit, -* (e.g. IEEE machines, Dec Vax machines), which is perhaps the -* most likely. We have to assume the last alternative. -* If this is true, then we need to reduce EMAX by one because -* there must be some way of representing zero in an implicit-bit -* system. On machines like Cray, we are reducing EMAX by one -* unnecessarily. -* - EMAX = EMAX - 1 - END IF -* - IF( IEEE ) THEN -* -* Assume we are on an IEEE machine which reserves one exponent -* for infinity and NaN. -* - EMAX = EMAX - 1 - END IF -* -* Now create RMAX, the largest machine number, which should -* be equal to (1.0 - BETA**(-P)) * BETA**EMAX . -* -* First compute 1.0 - BETA**(-P), being careful that the -* result is less than 1.0 . -* - RECBAS = ONE / BETA - Z = BETA - ONE - Y = ZERO - DO 20 I = 1, P - Z = Z*RECBAS - IF( Y.LT.ONE ) - $ OLDY = Y - Y = DLAMC3( Y, Z ) - 20 CONTINUE - IF( Y.GE.ONE ) - $ Y = OLDY -* -* Now multiply by BETA**EMAX to get RMAX. -* - DO 30 I = 1, EMAX - Y = DLAMC3( Y*BETA, ZERO ) - 30 CONTINUE -* - RMAX = Y - RETURN -* -* End of DLAMC5 -* - END - LOGICAL FUNCTION LSAME( CA, CB ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* September 30, 1994 -* -* .. Scalar Arguments .. - CHARACTER CA, CB -* .. -* -* Purpose -* ======= -* -* LSAME returns .TRUE. if CA is the same letter as CB regardless of -* case. -* -* Arguments -* ========= -* -* CA (input) CHARACTER*1 -* CB (input) CHARACTER*1 -* CA and CB specify the single characters to be compared. -* -* ===================================================================== -* -* .. Intrinsic Functions .. - INTRINSIC ICHAR -* .. -* .. Local Scalars .. - INTEGER INTA, INTB, ZCODE -* .. -* .. Executable Statements .. -* -* Test if the characters are equal -* - LSAME = CA.EQ.CB - IF( LSAME ) - $ RETURN -* -* Now test for equivalence if both characters are alphabetic. -* - ZCODE = ICHAR( 'Z' ) -* -* Use 'Z' rather than 'A' so that ASCII can be detected on Prime -* machines, on which ICHAR returns a value with bit 8 set. -* ICHAR('A') on Prime machines returns 193 which is the same as -* ICHAR('A') on an EBCDIC machine. -* - INTA = ICHAR( CA ) - INTB = ICHAR( CB ) -* - IF( ZCODE.EQ.90 .OR. ZCODE.EQ.122 ) THEN -* -* ASCII is assumed - ZCODE is the ASCII code of either lower or -* upper case 'Z'. -* - IF( INTA.GE.97 .AND. INTA.LE.122 ) INTA = INTA - 32 - IF( INTB.GE.97 .AND. INTB.LE.122 ) INTB = INTB - 32 -* - ELSE IF( ZCODE.EQ.233 .OR. ZCODE.EQ.169 ) THEN -* -* EBCDIC is assumed - ZCODE is the EBCDIC code of either lower or -* upper case 'Z'. -* - IF( INTA.GE.129 .AND. INTA.LE.137 .OR. - $ INTA.GE.145 .AND. INTA.LE.153 .OR. - $ INTA.GE.162 .AND. INTA.LE.169 ) INTA = INTA + 64 - IF( INTB.GE.129 .AND. INTB.LE.137 .OR. - $ INTB.GE.145 .AND. INTB.LE.153 .OR. - $ INTB.GE.162 .AND. INTB.LE.169 ) INTB = INTB + 64 -* - ELSE IF( ZCODE.EQ.218 .OR. ZCODE.EQ.250 ) THEN -* -* ASCII is assumed, on Prime machines - ZCODE is the ASCII code -* plus 128 of either lower or upper case 'Z'. -* - IF( INTA.GE.225 .AND. INTA.LE.250 ) INTA = INTA - 32 - IF( INTB.GE.225 .AND. INTB.LE.250 ) INTB = INTB - 32 - END IF - LSAME = INTA.EQ.INTB -* -* RETURN -* -* End of LSAME -* - END --- cash.orig/mebdfi.f 2007-12-15 15:37:46.000000000 -0500 +++ cash/mebdfi.f 2014-03-02 16:22:33.208828923 -0500 @@ -58,11 +58,11 @@ C C SEPTEMBER 20th 1999: FIRST RELEASE C -C OVDRIV +C I_OVDRIV C A PACKAGE FOR THE SOLUTION OF THE INITIAL VALUE PROBLEM C FOR SYSTEMS OF IMPLICIT DIFFERENTIAL ALGEBRAIC EQUATIONS c G(t,Y,Y')=0, Y=(Y(1),Y(2),Y(3),.....,Y(N)). -C SUBROUTINE OVDRIV IS A DRIVER ROUTINE FOR THIS PACKAGE. +C SUBROUTINE I_OVDRIV IS A DRIVER ROUTINE FOR THIS PACKAGE. C C REFERENCES C @@ -82,7 +82,7 @@ C SPRINGER 1996, page 267. C C ---------------------------------------------------------------- -C OVDRIV IS TO BE CALLED ONCE FOR EACH OUTPUT VALUE OF T, AND +C I_OVDRIV IS TO BE CALLED ONCE FOR EACH OUTPUT VALUE OF T, AND C IN TURN MAKES REPEATED CALLS TO THE CORE INTEGRATOR STIFF. C C THE INPUT PARAMETERS ARE .. @@ -158,7 +158,7 @@ C SHOULD BE NON-NEGATIVE. IF ITOL = 1 THEN SINGLE STEP ERROR C ESTIMATES DIVIDED BY YMAX(I) WILL BE KEPT LESS THAN 1 C IN ROOT-MEAN-SQUARE NORM. THE VECTOR YMAX OF WEIGHTS IS -C COMPUTED IN OVDRIV. INITIALLY YMAX(I) IS SET AS +C COMPUTED IN I_OVDRIV. INITIALLY YMAX(I) IS SET AS C THE MAXIMUM OF 1 AND ABS(Y(I)). THEREAFTER YMAX(I) IS C THE LARGEST VALUE OF ABS(Y(I)) SEEN SO FAR, OR THE C INITIAL VALUE YMAX(I) IF THAT IS LARGER. @@ -242,23 +242,23 @@ C -12 INSUFFICIENT INTEGER WORKSPACE FOR THE INTEGRATION C C -C IN ADDITION TO OVDRIVE, THE FOLLOWING ROUTINES ARE PROVIDED +C IN ADDITION TO I_OVDRIVE, THE FOLLOWING ROUTINES ARE PROVIDED C IN THE PACKAGE.. C -C INTERP( - ) INTERPOLATES TO GET THE OUTPUT VALUES +C I_INTERP( - ) INTERPOLATES TO GET THE OUTPUT VALUES C AT T=TOUT FROM THE DATA IN THE Y ARRAY. -C STIFF( - ) IS THE CORE INTEGRATOR ROUTINE. IT PERFORMS A +C I_STIFF( - ) IS THE CORE INTEGRATOR ROUTINE. IT PERFORMS A C SINGLE STEP AND ASSOCIATED ERROR CONTROL. -C COSET( - ) SETS COEFFICIENTS FOR BACKWARD DIFFERENTIATION +C I_COSET( - ) SETS COEFFICIENTS FOR BACKWARD DIFFERENTIATION C SCHEMES FOR USE IN THE CORE INTEGRATOR. -C PSET( - ) COMPUTES AND PROCESSES THE NEWTON ITERATION +C I_PSET( - ) COMPUTES AND PROCESSES THE NEWTON ITERATION C MATRIX DG/DY + (1/(H*BETA))DG/DY' -C DEC( - ) PERFORMS AN LU DECOMPOSITION ON A MATRIX. -C SOL( - ) SOLVES LINEAR SYSTEMS A*X = B AFTER DEC +C I_DEC( - ) PERFORMS AN LU DECOMPOSITION ON A MATRIX. +C I_SOL( - ) SOLVES LINEAR SYSTEMS A*X = B AFTER I_DEC C HAS BEEN CALLED FOR THE MATRIX A -C DGBFA ( - ) FACTORS A DOUBLE PRECISION BAND MATRIX BY +C I_DGBFA ( - ) FACTORS A DOUBLE PRECISION BAND MATRIX BY C ELIMINATION. -C DGBSL ( - ) SOLVES A BANDED LINEAR SYSTEM A*x=b +C I_DGBSL ( - ) SOLVES A BANDED LINEAR SYSTEM A*x=b C C ALSO SUPPLIED ARE THE BLAS ROUTINES C @@ -330,7 +330,7 @@ C >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> C THIS SUBROUTINE IS FOR THE PURPOSE * C OF SPLITTING UP THE WORK ARRAYS WORK AND IWORK * -C FOR USE INSIDE THE INTEGRATOR STIFF * +C FOR USE INSIDE THE INTEGRATOR I_STIFF * C <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C .. SCALAR ARGUMENTS .. @@ -346,7 +346,7 @@ C COMMON BLOCKS C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL OVDRIV,PDERV,RESID + EXTERNAL I_OVDRIV,PDERV,RESID C .. C .. SAVE STATEMENT .. SAVE I1,I2,I3,I4,I5,I6,I7,I8,I9,I10,I11 @@ -397,7 +397,7 @@ c THE ERROR FLAG IS INITIALISED c - CALL OVDRIV(N,T0,HO,Y0,YPRIME,TOUT,TEND,MF,IDID,LOUT,WORK(3), + CALL I_OVDRIV(N,T0,HO,Y0,YPRIME,TOUT,TEND,MF,IDID,LOUT,WORK(3), + WORK(I1),WORK(I2),WORK(I3),WORK(I4),WORK(I5),WORK(I6), + WORK(I7),WORK(I8),WORK(I9),WORK(I10),IWORK(15), + MBND,IWORK(1),IWORK(2),IWORK(3),MAXDER,ITOL,RTOL,ATOL,RPAR, @@ -428,7 +428,7 @@ END C-------------------------------------------------------------------------- C - SUBROUTINE OVDRIV(N,T0,HO,Y0,YPRIME,TOUT,TEND,MF,IDID,LOUT,Y, + SUBROUTINE I_OVDRIV(N,T0,HO,Y0,YPRIME,TOUT,TEND,MF,IDID,LOUT,Y, + YHOLD,YNHOLD,YMAX,ERRORS,SAVE1,SAVE2,SCALE,ARH,PW,PWCOPY, + IPIV,MBND,NIND1,NIND2,NIND3,MAXDER,ITOL,RTOL,ATOL,RPAR, + IPAR,PDERV,RESID,NQUSED,NSTEP,NFAIL,NRE,NJE,NDEC,NBSOL, @@ -452,7 +452,7 @@ INTEGER I,KGO,NHCUT C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL INTERP,STIFF,PDERV,RESID + EXTERNAL I_INTERP,I_STIFF,PDERV,RESID C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DABS,DMAX1 @@ -468,7 +468,7 @@ HMAX = DABS(TEND-T0)/10.0D+0 IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT THE OUTPUT POINT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IDID = KFLAG T0 = TOUT HO = H @@ -486,7 +486,7 @@ IF (((T-TOUT)*H.GE.0.0D+0) .OR. (DABS(T-TOUT).LE. + 100.0D+0*UROUND*HMAX)) THEN C HAVE OVERSHOT THE OUTPUT POINT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) T0 = TOUT HO = H IDID = KFLAG @@ -513,7 +513,7 @@ IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT TOUT WRITE (LOUT,9080) T,TOUT,H - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) HO = H T0 = TOUT IDID = -5 @@ -527,7 +527,7 @@ T0 = T IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT,SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IDID = KFLAG T0 = TOUT HO = H @@ -660,7 +660,7 @@ 20 IF ((T+H).EQ.T) THEN WRITE (LOUT,9000) END IF - CALL STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND, + CALL I_STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND, + NIND1,NIND2,NIND3,T,TOUT,TEND,Y,YPRIME,N, + YMAX,ERRORS,SAVE1,SAVE2,SCALE,PW,PWCOPY,YHOLD, + YNHOLD,ARH,IPIV,LOUT,MAXDER,ITOL,RTOL,ATOL,RPAR,IPAR, @@ -672,7 +672,7 @@ C ENDIF KGO = 1 - KFLAG IF (KGO.EQ.1) THEN -C NORMAL RETURN FROM STIFF +C NORMAL RETURN FROM I_STIFF GO TO 30 ELSE IF (KGO.EQ.2) THEN @@ -708,7 +708,7 @@ C FOR ANY OTHER VALUE OF IDID, CONTROL RETURNS TO THE INTEGRATOR C UNLESS TOUT HAS BEEN REACHED. THEN INTERPOLATED VALUES OF Y ARE C COMPUTED AND STORED IN Y0 ON RETURN. -C IF INTERPOLATION IS NOT DESIRED, THE CALL TO INTERP SHOULD BE +C IF INTERPOLATION IS NOT DESIRED, THE CALL TO I_INTERP SHOULD BE C REMOVED AND CONTROL TRANSFERRED TO STATEMENT 500 INSTEAD OF 520. C -------------------------------------------------------------------- IF(NSTEP.GT.MAXSTP) THEN @@ -749,7 +749,7 @@ IF (((T-TOUT)*H.GE.0.0D+0) .OR. (DABS(T-TOUT).LE. + 100.0D+0*UROUND*HMAX)) THEN C HAVE OVERSHOT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) T0 = TOUT HO = H IDID = KFLAG @@ -766,7 +766,7 @@ ELSE IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT, SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IDID = KFLAG HO = H T0 = TOUT @@ -805,14 +805,14 @@ ELSE C HAVE PASSED TOUT SO INTERPOLATE - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) T0 = TOUT IDID = KFLAG END IF HO = H IF(KFLAG.NE.0) IDID = KFLAG RETURN -C -------------------------- END OF SUBROUTINE OVDRIV ----------------- +C -------------------------- END OF SUBROUTINE I_OVDRIV ----------------- 9000 FORMAT (' WARNING.. T + H = T ON NEXT STEP.') 9010 FORMAT (/,/,' KFLAG = -2 FROM INTEGRATOR AT T = ',E16.8,' H =', + E16.8,/, @@ -848,7 +848,7 @@ END C-------------------------------------------------------------------------- C - SUBROUTINE INTERP(N,JSTART,H,T,Y,TOUT,Y0) + SUBROUTINE I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C .. SCALAR ARGUMENTS .. @@ -875,15 +875,15 @@ 20 CONTINUE 30 CONTINUE RETURN -C -------------- END OF SUBROUTINE INTERP --------------------------- +C -------------- END OF SUBROUTINE I_INTERP --------------------------- END C - SUBROUTINE COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + SUBROUTINE I_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C -------------------------------------------------------------------- -C COSET IS CALLED BY THE INTEGRATOR AND SETS THE COEFFICIENTS USED +C I_COSET IS CALLED BY THE INTEGRATOR AND SETS THE COEFFICIENTS USED C BY THE CONVENTIONAL BACKWARD DIFFERENTIATION SCHEME AND THE C MODIFIED EXTENDED BACKWARD DIFFERENTIATION SCHEME. THE VECTOR C EL OF LENGTH NQ+1 DETERMINES THE BASIC BDF METHOD WHILE THE VECTOR @@ -1013,24 +1013,24 @@ TQ(4) = 0.5D+0*TQ(2)/DBLE(FLOAT(NQ)) IF(NQ.NE.1) TQ(5)=PERTST(NQ-1,1) RETURN -C --------------------- END OF SUBROUTINE COSET --------------------- +C --------------------- END OF SUBROUTINE I_COSET --------------------- END - SUBROUTINE PSET(Y,YPRIME,N,H,T,UROUND,EPSJAC,CON,MITER,MBND, + SUBROUTINE I_PSET(Y,YPRIME,N,H,T,UROUND,EPSJAC,CON,MITER,MBND, + NIND1,NIND2,NIND3,IER,PDERV,RESID,NRENEW,YMAX,SAVE1,SAVE2, + SAVE3,PW,PWCOPY,WRKSPC,IPIV,ITOL,RTOL,ATOL,NPSET,NJE,NRE, + NDEC,IPAR,RPAR,IERR) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C ------------------------------------------------------------------- -C PSET IS CALLED BY STIFF TO COMPUTE AND PROCESS THE MATRIX +C I_PSET IS CALLED BY I_STIFF TO COMPUTE AND PROCESS THE MATRIX C PD=DG/DY + (1/CON)DG/DY'. THIS MATRIX IS THEN SUBJECTED TO LU C DECOMPOSITION IN PREPARATION FOR LATER SOLUTION OF LINEAR SYSTEMS C OF ALGEBRAIC EQUATIONS WITH LU AS THE COEFFICIENT MATRIX. THE C MATRIX PD IS FOUND BY THE USER-SUPPLIED ROUTINE PDERV IF MITER=1 C OR 3 OR BY FINITE DIFFERENCING IF MITER = 2 OR 4. C IN ADDITION TO VARIABLES DESCRIBED PREVIOUSLY, COMMUNICATION WITH -C PSET USES THE FOLLOWING .. +C I_PSET USES THE FOLLOWING .. C EPSJAC = DSQRT(UROUND), USED IN NUMERICAL JACOBIAN INCREMENTS. C ******************************************************************* C THE ARGUMENT NRENEW IS USED TO SIGNAL WHETHER OR NOT @@ -1052,7 +1052,7 @@ INTEGER I,J,J1,JJKK C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL DEC,PDERV,DGBFA,RESID + EXTERNAL I_DEC,PDERV,I_DGBFA,RESID C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DABS,DMAX1,DSQRT @@ -1192,17 +1192,17 @@ NRE=NRE+ MIN(MBND(3),N) C 70 IF (MITER.GT.2) THEN - CALL DGBFA(PW,MBND(4),N,ML,MU,IPIV,IER) + CALL I_DGBFA(PW,MBND(4),N,ML,MU,IPIV,IER) NDEC = NDEC + 1 ELSE - CALL DEC(N,N,PW,IPIV,IER) + CALL I_DEC(N,N,PW,IPIV,IER) NDEC = NDEC + 1 ENDIF RETURN -C ---------------------- END OF SUBROUTINE PSET --------------------- +C ---------------------- END OF SUBROUTINE I_PSET --------------------- END C - SUBROUTINE DEC(N,NDIM,A,IP,IER) + SUBROUTINE I_DEC(N,NDIM,A,IP,IER) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C ------------------------------------------------------------------- @@ -1218,9 +1218,9 @@ C IP(N) = (-1)**(NUMBER OF INTERCHANGES) OR 0. C IER = 0 IF MATRIX IS NON-SINGULAR, OR K IF FOUND TO BE SINGULAR C AT STAGE K. -C USE SOL TO OBTAIN SOLUTION OF LINEAR SYSTEM. +C USE I_SOL TO OBTAIN SOLUTION OF LINEAR SYSTEM. C DETERM(A) = IP(N)*A(1,1)*A(2,2)* . . . *A(N,N). -C IF IP(N) = 0, A IS SINGULAR, SOL WILL DIVIDE BY ZERO. +C IF IP(N) = 0, A IS SINGULAR, I_SOL WILL DIVIDE BY ZERO. C C REFERENCE. C C.B. MOLER, ALGORITHM 423, LINEAR EQUATION SOLVER, C.A.C.M @@ -1279,10 +1279,10 @@ 80 IER = K IP(N) = 0 RETURN -C--------------------- END OF SUBROUTINE DEC ---------------------- +C--------------------- END OF SUBROUTINE I_DEC ---------------------- END C - SUBROUTINE SOL(N,NDIM,A,B,IP) + SUBROUTINE I_SOL(N,NDIM,A,B,IP) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C .. SCALAR ARGUMENTS .. @@ -1305,8 +1305,8 @@ C NDIM = DECLARED DIMENSION OF MATRIX A. C A = TRIANGULARISED MATRIX OBTAINED FROM DEC. C B = RIGHT HAND SIDE VECTOR. -C IP = PIVOT VECTOR OBTAINED FROM DEC. -C DO NOT USE IF DEC HAS SET IER .NE. 0 +C IP = PIVOT VECTOR OBTAINED FROM I_DEC. +C DO NOT USE IF I_DEC HAS SET IER .NE. 0 C OUTPUT.. C B = SOLUTION VECTOR, X. C ------------------------------------------------------------------ @@ -1333,16 +1333,16 @@ 40 CONTINUE 50 B(1) = B(1)/A(1,1) RETURN -C------------------------- END OF SUBROUTINE SOL ------------------ +C------------------------- END OF SUBROUTINE I_SOL ------------------ END C - subroutine dgbfa(abd,lda,n,ml,mu,ipvt,info) + subroutine i_dgbfa(abd,lda,n,ml,mu,ipvt,info) integer lda,n,ml,mu,ipvt(1),info double precision abd(lda,1) c -c dgbfa factors a double precision band matrix by elimination. +c i_dgbfa factors a double precision band matrix by elimination. c -c dgbfa is usually called by dgbco, but it can be called +c i_dgbfa is usually called by dgbco, but it can be called c directly with a saving in time if rcond is not needed. c c on entry @@ -1384,7 +1384,7 @@ c = 0 normal value. c = k if u(k,k) .eq. 0.0 . this is not an error c condition for this subroutine, but it does -c indicate that dgbsl will divide by zero if +c indicate that i_dgbsl will divide by zero if c called. use rcond in dgbco for a reliable c indication of singularity. c @@ -1511,151 +1511,18 @@ return end C-------------------------------------------------------------------------- - subroutine daxpy(n,da,dx,incx,dy,incy) -c -c constant times a vector plus a vector. -c uses unrolled loops for increments equal to one. -c jack dongarra, linpack, 3/11/78. -c - double precision dx(1),dy(1),da - integer i,incx,incy,ix,iy,m,mp1,n -c - if(n.le.0)return - if (da .eq. 0.0d0) return - if(incx.eq.1.and.incy.eq.1)go to 20 -c -c code for unequal increments or equal increments -c not equal to 1 -c - ix = 1 - iy = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - if(incy.lt.0)iy = (-n+1)*incy + 1 - do 10 i = 1,n - dy(iy) = dy(iy) + da*dx(ix) - ix = ix + incx - iy = iy + incy - 10 continue - return -c -c code for both increments equal to 1 -c -c -c clean-up loop -c - 20 m = mod(n,4) - if( m .eq. 0 ) go to 40 - do 30 i = 1,m - dy(i) = dy(i) + da*dx(i) - 30 continue - if( n .lt. 4 ) return - 40 mp1 = m + 1 - do 50 i = mp1,n,4 - dy(i) = dy(i) + da*dx(i) - dy(i + 1) = dy(i + 1) + da*dx(i + 1) - dy(i + 2) = dy(i + 2) + da*dx(i + 2) - dy(i + 3) = dy(i + 3) + da*dx(i + 3) - 50 continue - return - end -C--------------------------------------------------------------------------- - subroutine dscal(n,da,dx,incx) -c -c scales a vector by a constant. -c uses unrolled loops for increment equal to one. -c jack dongarra, linpack, 3/11/78. -c modified to correct problem with negative increment, 8/21/90. -c - double precision da,dx(1) - integer i,incx,ix,m,mp1,n -c - if(n.le.0)return - if(incx.eq.1)go to 20 -c -c code for increment not equal to 1 -c - ix = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - do 10 i = 1,n - dx(ix) = da*dx(ix) - ix = ix + incx - 10 continue - return -c -c code for increment equal to 1 -c -c -c clean-up loop -c - 20 m = mod(n,5) - if( m .eq. 0 ) go to 40 - do 30 i = 1,m - dx(i) = da*dx(i) - 30 continue - if( n .lt. 5 ) return - 40 mp1 = m + 1 - do 50 i = mp1,n,5 - dx(i) = da*dx(i) - dx(i + 1) = da*dx(i + 1) - dx(i + 2) = da*dx(i + 2) - dx(i + 3) = da*dx(i + 3) - dx(i + 4) = da*dx(i + 4) - 50 continue - return - end -C-------------------------------------------------------------------------- - integer function idamax(n,dx,incx) -c -c finds the index of element having max. absolute value. -c jack dongarra, linpack, 3/11/78. -c modified to correct problem with negative increment, 8/21/90. -c - double precision dx(1),dmax - integer i,incx,ix,n -c - idamax = 0 - if( n .lt. 1 ) return - idamax = 1 - if(n.eq.1)return - if(incx.eq.1)go to 20 -c -c code for increment not equal to 1 -c - ix = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - dmax = dabs(dx(ix)) - ix = ix + incx - do 10 i = 2,n - if(dabs(dx(ix)).le.dmax) go to 5 - idamax = i - dmax = dabs(dx(ix)) - 5 ix = ix + incx - 10 continue - return -c -c code for increment equal to 1 -c - 20 dmax = dabs(dx(1)) - do 30 i = 2,n - if(dabs(dx(i)).le.dmax) go to 30 - idamax = i - dmax = dabs(dx(i)) - 30 continue - return - end -C-------------------------------------------------------------------------- - subroutine dgbsl(abd,lda,n,ml,mu,ipvt,b,job) + subroutine i_dgbsl(abd,lda,n,ml,mu,ipvt,b,job) integer lda,n,ml,mu,ipvt(*),job double precision abd(lda,*),b(*) c -c dgbsl solves the double precision band system +c i_dgbsl solves the double precision band system c a * x = b or trans(a) * x = b -c using the factors computed by dgbco or dgbfa. +c using the factors computed by dgbco or i_dgbfa. c c on entry c c abd double precision(lda, n) -c the output from dgbco or dgbfa. +c the output from dgbco or i_dgbfa. c c lda integer c the leading dimension of the array abd . @@ -1670,7 +1537,7 @@ c number of diagonals above the main diagonal. c c ipvt integer(n) -c the pivot vector from dgbco or dgbfa. +c the pivot vector from dgbco or i_dgbfa. c c b double precision(n) c the right hand side vector. @@ -1691,14 +1558,14 @@ c but it is often caused by improper arguments or improper c setting of lda . it will not occur if the subroutines are c called correctly and if dgbco has set rcond .gt. 0.0 -c or dgbfa has set info .eq. 0 . +c or i_dgbfa has set info .eq. 0 . c c to compute inverse(a) * c where c is a matrix c with p columns c call dgbco(abd,lda,n,ml,mu,ipvt,rcond,z) c if (rcond is too small) go to ... c do 10 j = 1, p -c call dgbsl(abd,lda,n,ml,mu,ipvt,c(1,j),0) +c call i_dgbsl(abd,lda,n,ml,mu,ipvt,c(1,j),0) c 10 continue c c linpack. this version dated 08/14/78 . @@ -1780,64 +1647,14 @@ return end C--------------------------------------------------------------------------- - double precision function ddot(n,dx,incx,dy,incy) -c -c forms the dot product of two vectors. -c uses unrolled loops for increments equal to one. -c jack dongarra, linpack, 3/11/78. -c - double precision dx(1),dy(1),dtemp - integer i,incx,incy,ix,iy,m,mp1,n -c - ddot = 0.0d0 - dtemp = 0.0d0 - if(n.le.0)return - if(incx.eq.1.and.incy.eq.1)go to 20 -c -c code for unequal increments or equal increments -c not equal to 1 -c - ix = 1 - iy = 1 - if(incx.lt.0)ix = (-n+1)*incx + 1 - if(incy.lt.0)iy = (-n+1)*incy + 1 - do 10 i = 1,n - dtemp = dtemp + dx(ix)*dy(iy) - ix = ix + incx - iy = iy + incy - 10 continue - ddot = dtemp - return -c -c code for both increments equal to 1 -c -c -c clean-up loop -c - 20 m = mod(n,5) - if( m .eq. 0 ) go to 40 - do 30 i = 1,m - dtemp = dtemp + dx(i)*dy(i) - 30 continue - if( n .lt. 5 ) go to 60 - 40 mp1 = m + 1 - do 50 i = mp1,n,5 - dtemp = dtemp + dx(i)*dy(i) + dx(i + 1)*dy(i + 1) + - * dx(i + 2)*dy(i + 2) + dx(i + 3)*dy(i + 3) + - * dx(i + 4)*dy(i + 4) - 50 continue - 60 ddot = dtemp - return - end -C--------------------------------------------------------------------------- - SUBROUTINE ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + SUBROUTINE I_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C *************************************************** C C THIS ROUTINE CALCULATES ERRORS USED IN TESTS -C IN STIFF . +C IN I_STIFF . C C *************************************************** C .. SCALAR ARGUMENTS .. @@ -1872,7 +1689,7 @@ END C-------------------------------------------------------------------------- - SUBROUTINE PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) + SUBROUTINE I_PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C ********************************************************************** @@ -1903,7 +1720,7 @@ END C------------------------------------------------------------------------ - SUBROUTINE ITRAT2(QQQ,Y,YPRIME,N,T,HBETA,ERRBND,ARH,CRATE,TCRATE + SUBROUTINE I_ITRAT2(QQQ,Y,YPRIME,N,T,HBETA,ERRBND,ARH,CRATE,TCRATE + ,M,WORKED,YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND,NIND1, + NIND2,NIND3,IPIV,LMB,ITOL,RTOL,ATOL,IPAR,RPAR,HUSED,NBSOL, + NRE,NQUSED,RESID,IERR) @@ -1922,7 +1739,7 @@ INTEGER I C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL SOL,DGBSL,RESID + EXTERNAL I_SOL,I_DGBSL,RESID C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DMAX1,DMIN1 @@ -1963,10 +1780,10 @@ C call resid(n,t,y,save2,yprime,ipar,rpar,ierr) IF(MF.GE.23) THEN - CALL DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE2,0) + CALL I_DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE2,0) NBSOL = NBSOL + 1 ELSE - CALL SOL(N,N,PW,SAVE2,IPIV) + CALL I_SOL(N,N,PW,SAVE2,IPIV) NBSOL = NBSOL + 1 ENDIF D = ZERO @@ -1992,10 +1809,10 @@ C IF WE ARE HERE THEN PARTIALS ARE O.K. C IF( MF.GE. 23) THEN - CALL DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE2,0) + CALL I_DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE2,0) NBSOL=NBSOL + 1 ELSE - CALL SOL(N,N,PW,SAVE2,IPIV) + CALL I_SOL(N,N,PW,SAVE2,IPIV) NBSOL = NBSOL + 1 ENDIF C @@ -2043,7 +1860,7 @@ END C-------------------------------------------------------------------------- - SUBROUTINE STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND, + SUBROUTINE I_STIFF(H,HMAX,HMIN,JSTART,KFLAG,MF,MBND, + NIND1,NIND2,NIND3,T,TOUT,TEND,Y,YPRIME,N, + YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,PWCOPY,YHOLD, + YNHOLD,ARH,IPIV,LOUT,MAXDER,ITOL,RTOL,ATOL,RPAR,IPAR, @@ -2052,13 +1869,13 @@ IMPLICIT DOUBLE PRECISION(A-H,O-Z) C ------------------------------------------------------------------ -C THE SUBROUTINE STIFF PERFORMS ONE STEP OF THE INTEGRATION OF AN +C THE SUBROUTINE I_STIFF PERFORMS ONE STEP OF THE INTEGRATION OF AN C INITIAL VALUE PROBLEM FOR A SYSTEM OF C IMPLICIT DIFFERENTIAL ALGEBRAIC EQUATIONS. -C COMMUNICATION WITH STIFF IS DONE WITH THE FOLLOWING VARIABLES.. +C COMMUNICATION WITH I_STIFF IS DONE WITH THE FOLLOWING VARIABLES.. C Y AN N BY LMAX+3 ARRAY CONTAINING THE DEPENDENT VARIABLES C AND THEIR BACKWARD DIFFERENCES. MAXDER (=LMAX-1) IS THE -C MAXIMUM ORDER AVAILABLE. SEE SUBROUTINE COSET. +C MAXIMUM ORDER AVAILABLE. SEE SUBROUTINE I_COSET. C Y(I,J+1) CONTAINS THE JTH BACKWARD DIFFERENCE OF Y(I) C T THE INDEPENDENT VARIABLE. T IS UPDATED ON EACH STEP TAKEN. C H THE STEPSIZE TO BE ATTEMPTED ON THE NEXT STEP. @@ -2068,7 +1885,7 @@ C HMIN THE MINIMUM AND MAXIMUM ABSOLUTE VALUE OF THE STEPSIZE C HMAX TO BE USED FOR THE STEP. THESE MAY BE CHANGED AT ANY C TIME BUT WILL NOT TAKE EFFECT UNTIL THE NEXT H CHANGE. -C RTOL,ATOL THE ERROR BOUNDS. SEE DESCRIPTION IN OVDRIV. +C RTOL,ATOL THE ERROR BOUNDS. SEE DESCRIPTION IN I_OVDRIV. C N THE NUMBER OF FIRST ORDER DIFFERENTIAL EQUATIONS. C MF THE METHOD FLAG. MUST BE SET TO 21,22,23 OR 24 AT PRESENT C KFLAG A COMPLETION FLAG WITH THE FOLLOWING MEANINGS.. @@ -2103,7 +1920,7 @@ C MATRIX WAS FORMED BY A NEW J. C AVOLDJ STORES VALUE FOR AVERAGE CRATE WHEN ITERATION C MATRIX WAS FORMED BY AN OLD J. -C NRENEW FLAG THAT IS USED IN COMMUNICATION WITH SUBROUTINE PSET. +C NRENEW FLAG THAT IS USED IN COMMUNICATION WITH SUBROUTINE I_PSET. C IF NRENEW > 0 THEN FORM A NEW JACOBIAN BEFORE C COMPUTING THE COEFFICIENT MATRIX FOR C THE NEWTON-RAPHSON ITERATION @@ -2130,10 +1947,11 @@ C .. C .. LOCAL ARRAYS .. DIMENSION EL(10),ELST(10),TQ(5) + DIMENSION Y0(N) C .. C .. EXTERNAL SUBROUTINES .. - EXTERNAL COSET,CPYARY,ERRORS,HCHOSE,ITRAT2, - + PRDICT,PSET,RSCALE,SOL,DGBSL,PDERV,RESID + EXTERNAL I_COSET,I_CPYARY,I_ERRORS,I_HCHOSE,I_ITRAT2, + + I_PRDICT,I_PSET,I_RSCALE,I_SOL,I_DGBSL,PDERV,RESID C .. C .. INTRINSIC FUNCTIONS .. INTRINSIC DABS,DMAX1,DMIN1 @@ -2225,14 +2043,14 @@ HUSED = H C ----------------------------------------------------------------- C IF THE CALLER HAS CHANGED N , THE CONSTANTS E, EDN, EUP -C AND BND MUST BE RESET. E IS A COMPARISON FOR ERRORS AT THE +C AND BND MUST BE RESET. E IS A COMPARISON FOR I_ERRORS AT THE C CURRENT ORDER NQ. EUP IS TO TEST FOR INCREASING THE ORDER, C EDN FOR DECREASING THE ORDER. BND IS USED TO TEST FOR CONVERGENCE C OF THE CORRECTOR ITERATES. IF THE CALLER HAS CHANGED H, Y MUST C BE RE-SCALED. IF H IS CHANGED, IDOUB IS SET TO L+1 TO PREVENT C FURTHER CHANGES IN H FOR THAT MANY STEPS. C ----------------------------------------------------------------- - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL I_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) LMAX = MAXDER + 1 RC = RC*EL(1)/OLDLO OLDLO = EL(1) @@ -2243,14 +2061,14 @@ C NRENEW AND NEWPAR ARE TO INSTRUCT ROUTINE THAT C WE WISH A NEW J TO BE CALCULATED FOR THIS STEP. C ***************************************************** - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL I_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) DO 20 I = 1,N ARH(I) = EL(2)*Y(I,1) 20 CONTINUE - CALL CPYARY(N*L,Y,YHOLD) + CALL I_CPYARY(N*L,Y,YHOLD) QI = H*EL(1) QQ = ONE/QI - CALL PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) + CALL I_PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) IF(IERR.NE.0) THEN H=H/2 IERR = 0 @@ -2263,7 +2081,7 @@ C >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> C DIFFERENT PARAMETERS ON THIS CALL < C <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - 30 CALL CPYARY(N*L,YHOLD,Y) + 30 CALL I_CPYARY(N*L,YHOLD,Y) IF (MF.NE.MFOLD) THEN METH = MF/10 MITER = MF - 10*METH @@ -2306,7 +2124,7 @@ C ********************************************* 40 RH = DMAX1(RH,HMIN/DABS(H)) 50 RH = DMIN1(RH,HMAX/DABS(H),RMAX) - CALL RSCALE(N,L,RH,Y) + CALL I_RSCALE(N,L,RH,Y) RMAX = 10.0D+0 JCHANG = 1 H = H*RH @@ -2323,7 +2141,7 @@ END IF IDOUB = L + 1 - CALL CPYARY(N*L,Y,YHOLD) + CALL I_CPYARY(N*L,Y,YHOLD) 60 IF (DABS(RC-ONE).GT.UPBND) IWEVAL = MITER HUSED = H @@ -2348,7 +2166,7 @@ IF (JCHANG.EQ.1) THEN C IF WE HAVE CHANGED STEPSIZE THEN PREDICT A VALUE FOR Y(T+H) C AND EVALUATE THE DERIVATIVE THERE (STORED IN SAVE2()) - CALL PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) + CALL I_PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) IF(IERR.NE.0) GOTO 8000 DO 95 I=1,N YPRIME(I)=(Y(I,1)-ARH(I))/QI @@ -2371,7 +2189,7 @@ C ------------------------------------------------------------------- C IF INDICATED, THE MATRIX P = I/(H*EL(2)) - J IS RE-EVALUATED BEFORE C STARTING THE CORRECTOR ITERATION. IWEVAL IS SET = 0 TO INDICATE -C THAT THIS HAS BEEN DONE. P IS COMPUTED AND PROCESSED IN PSET. +C THAT THIS HAS BEEN DONE. P IS COMPUTED AND PROCESSED IN I_PSET. C THE PROCESSED MATRIX IS STORED IN PW C ------------------------------------------------------------------- IWEVAL = 0 @@ -2436,14 +2254,14 @@ JSNOLD = 0 MQ1TMP = MEQC1 MQ2TMP = MEQC2 - CALL PSET(Y,YPRIME,N,H,T,UROUND,EPSJAC,QI,MITER,MBND, + CALL I_PSET(Y,YPRIME,N,H,T,UROUND,EPSJAC,QI,MITER,MBND, + NIND1,NIND2,NIND3,IER,PDERV,RESID,NRENEW,YMAX,SAVE1,SAVE2, + SCALE,PW,PWCOPY,ERROR,IPIV,ITOL,RTOL,ATOL,NPSET,NJE,NRE,NDEC + ,IPAR,RPAR,IERR) IF(IERR.NE.0) GOTO 8000 QQQ=QI C -C NOTE THAT ERROR() IS JUST BEING USED AS A WORKSPACE BY PSET +C NOTE THAT ERROR() IS JUST BEING USED AS A WORKSPACE BY I_PSET IF (IER.NE.0) THEN C IF IER>0 THEN WE HAVE HAD A SINGULARITY IN THE ITERATION MATRIX IJUS=1 @@ -2467,14 +2285,14 @@ C LOOP. THE UPDATED Y VECTOR IS STORED TEMPORARILY IN SAVE1. C ********************************************************************** IF (.NOT.SAMPLE) THEN - CALL ITRAT2(QQQ,Y,YPRIME,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1, + CALL I_ITRAT2(QQQ,Y,YPRIME,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1, + WORKED,YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND, + NIND1,NIND2,NIND3,IPIV,1,ITOL,RTOL,ATOL,IPAR,RPAR, + HUSED,NBSOL,NRE,NQUSED,resid,IERR) IF(IERR.NE.0) GOTO 8000 ELSE - CALL ITRAT2(QQQ,Y,YPRIME,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1, + CALL I_ITRAT2(QQQ,Y,YPRIME,N,T,QI,BND,ARH,CRATE1,TCRAT1,M1, + WORKED,YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND, + NIND1,NIND2,NIND3,IPIV,0,ITOL,RTOL,ATOL,IPAR,RPAR, + HUSED,NBSOL,NRE,NQUSED,resid,IERR) @@ -2589,7 +2407,7 @@ ARH(I) = ARH(I) + EL(JP1)*Y(I,J1) 200 CONTINUE 210 CONTINUE - CALL PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) + CALL I_PRDICT(T,H,Y,L,N,IPAR,RPAR,IERR) IF(IERR.NE.0) GOTO 8000 DO 215 I=1,N YPRIME(I)=(Y(I,1)-ARH(I))/QQQ @@ -2603,7 +2421,7 @@ C FOR NOW WILL ASSUME THAT WE DO NOT WISH TO SAMPLE C AT THE N+2 STEP POINT C - CALL ITRAT2(QQQ,Y,YPRIME,N,T,QI,BND,ARH,CRATE2,TCRAT2,M2, + CALL I_ITRAT2(QQQ,Y,YPRIME,N,T,QI,BND,ARH,CRATE2,TCRAT2,M2, + WORKED,YMAX,ERROR,SAVE1,SAVE2,SCALE,PW,MF,MBND, + NIND1,NIND2,NIND3,IPIV,1,ITOL,RTOL,ATOL,IPAR,RPAR, + HUSED,NBSOL,NRE,NQUSED,resid,IERR) @@ -2661,10 +2479,10 @@ NRE=NRE+1 C IF (MF.GE. 23) THEN - CALL DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) + CALL I_DGBSL(PW,MBND(4),N,MBND(1),MBND(2),IPIV,SAVE1,0) NBSOL=NBSOL+1 ELSE - CALL SOL(N,N,PW,SAVE1,IPIV) + CALL I_SOL(N,N,PW,SAVE1,IPIV) NBSOL = NBSOL + 1 ENDIF DO 321 I=1,N @@ -2758,7 +2576,7 @@ IF(NQ.GT.1) FFAIL = 0.5D+0/DBLE(FLOAT(NQ)) IF(NQ.GT.2) FRFAIL = 0.5D+0/DBLE(FLOAT(NQ-1)) EFAIL = 0.5D+0/DBLE(FLOAT(L)) - CALL CPYARY(N*L,YHOLD,Y) + CALL I_CPYARY(N*L,YHOLD,Y) RMAX = 2.0D+0 IF (DABS(H).LE.HMIN*1.00001D+0) THEN C @@ -2787,10 +2605,10 @@ NQ=NEWQ RH=ONE/(PLFAIL*DBLE(FLOAT(-KFAIL))) L=NQ+1 - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL I_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) RC=RC*EL(1)/OLDLO OLDLO=EL(1) - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL I_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) ELSE NEWQ = NQ RH = ONE/ (PRFAIL*DBLE(FLOAT(-KFAIL))) @@ -2816,7 +2634,7 @@ C ********************************* JCHANG = 1 RH = DMAX1(HMIN/DABS(H),0.1D+0) - CALL HCHOSE(RH,H,OVRIDE) + CALL I_HCHOSE(RH,H,OVRIDE) H = H*RH DO 350 I = 1,N Y(I,1) = YHOLD(I,1) @@ -2832,11 +2650,11 @@ NQ = 1 L = 2 C RESET ORDER, RECALCULATE ERROR BOUNDS - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL I_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) LMAX = MAXDER + 1 RC = RC*EL(1)/OLDLO OLDLO = EL(1) - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL I_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) C NOW JUMP TO NORMAL CONTINUATION POINT GO TO 60 C ********************************************************************** @@ -3003,7 +2821,7 @@ GOTO 440 ENDIF RH = DMIN1(RH,RMAX) - CALL HCHOSE(RH,H,OVRIDE) + CALL I_HCHOSE(RH,H,OVRIDE) IF ((JSINUP.LE.20).AND.(KFLAG.EQ.0).AND.(RH.LT.1.1D+0)) THEN C WE HAVE RUN INTO PROBLEMS IDOUB = 10 @@ -3031,16 +2849,16 @@ NQ = NEWQ L = NQ + 1 C RESET ORDER,RECALCULATE ERROR BOUNDS - CALL COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) + CALL I_COSET(NQ,EL,ELST,TQ,NCOSET,MAXORD) LMAX = MAXDER + 1 RC = RC*EL(1)/OLDLO OLDLO = EL(1) - CALL ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) + CALL I_ERRORS(N,TQ,EDN,E,EUP,BND,EDDN) END IF RH = DMAX1(RH,HMIN/DABS(H)) RH = DMIN1(RH,HMAX/DABS(H),RMAX) - CALL RSCALE(N,L,RH,Y) + CALL I_RSCALE(N,L,RH,Y) RMAX = 10.0D+0 JCHANG = 1 H = H*RH @@ -3057,7 +2875,7 @@ C INFORMATION NECESSARY TO PERFORM AN INTERPOLATION TO FIND THE C SOLUTION AT THE SPECIFIED OUTPUT POINT IF APPROPRIATE. C ---------------------------------------------------------------------- - CALL CPYARY(N*L,Y,YHOLD) + CALL I_CPYARY(N*L,Y,YHOLD) NSTEP = NSTEP + 1 JSINUP = JSINUP + 1 JSNOLD = JSNOLD + 1 @@ -3112,7 +2930,7 @@ IF ((T-TOUT)*H.GE.0.0D+0) THEN C HAVE OVERSHOT TOUT WRITE (LOUT,*) T,TOUT,H - CALL INTERP(N,JSTART,H,T,Y,TOUT,Y0) + CALL I_INTERP(N,JSTART,H,T,Y,TOUT,Y0) HO = H T0 = TOUT IDID = -5 @@ -3123,7 +2941,7 @@ goto 30 endif c - IF(IJUS.EQ.0) CALL HCHOSE(RH,H,OVRIDE) + IF(IJUS.EQ.0) CALL I_HCHOSE(RH,H,OVRIDE) IF(.NOT.FINISH) THEN GO TO 40 ELSE @@ -3132,9 +2950,9 @@ 9000 FORMAT (1X,' CORRECTOR HAS NOT CONVERGED') END -C ------------------- END OF SUBROUTINE STIFF -------------------------- +C ------------------- END OF SUBROUTINE I_STIFF -------------------------- - SUBROUTINE RSCALE(N,L,RH,Y) + SUBROUTINE I_RSCALE(N,L,RH,Y) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C .. SCALAR ARGUMENTS .. @@ -3246,7 +3064,7 @@ END C--------------------------------------------------------------------------- - SUBROUTINE CPYARY(NELEM,SOURCE,TARGET) + SUBROUTINE I_CPYARY(NELEM,SOURCE,TARGET) IMPLICIT DOUBLE PRECISION(A-H,O-Z) C C COPIES THE ARRAY SOURCE() INTO THE ARRAY TARGET() @@ -3271,7 +3089,7 @@ END C---------------------------------------------------------------------------- - SUBROUTINE HCHOSE(RH,H,OVRIDE) + SUBROUTINE I_HCHOSE(RH,H,OVRIDE) IMPLICIT DOUBLE PRECISION(A-H,O-Z) COMMON / STPSZE / HSTPSZ(2,14) LOGICAL OVRIDE @@ -3306,953 +3124,3 @@ RETURN END -C -C ************************************************************ -C - DOUBLE PRECISION FUNCTION DLAMCH( CMACH ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - CHARACTER CMACH -* .. -* -* Purpose -* ======= -* -* DLAMCH determines double precision machine parameters. -* -* Arguments -* ========= -* -* CMACH (input) CHARACTER*1 -* Specifies the value to be returned by DLAMCH: -* = 'E' or 'e', DLAMCH := eps -* = 'S' or 's , DLAMCH := sfmin -* = 'B' or 'b', DLAMCH := base -* = 'P' or 'p', DLAMCH := eps*base -* = 'N' or 'n', DLAMCH := t -* = 'R' or 'r', DLAMCH := rnd -* = 'M' or 'm', DLAMCH := emin -* = 'U' or 'u', DLAMCH := rmin -* = 'L' or 'l', DLAMCH := emax -* = 'O' or 'o', DLAMCH := rmax -* -* where -* -* eps = relative machine precision -* sfmin = safe minimum, such that 1/sfmin does not overflow -* base = base of the machine -* prec = eps*base -* t = number of (base) digits in the mantissa -* rnd = 1.0 when rounding occurs in addition, 0.0 otherwise -* emin = minimum exponent before (gradual) underflow -* rmin = underflow threshold - base**(emin-1) -* emax = largest exponent before overflow -* rmax = overflow threshold - (base**emax)*(1-eps) -* -* ===================================================================== -* -* .. Parameters .. - DOUBLE PRECISION ONE, ZERO - PARAMETER ( ONE = 1.0D+0, ZERO = 0.0D+0 ) -* .. -* .. Local Scalars .. - LOGICAL FIRST, LRND - INTEGER BETA, IMAX, IMIN, IT - DOUBLE PRECISION BASE, EMAX, EMIN, EPS, PREC, RMACH, RMAX, RMIN, - $ RND, SFMIN, SMALL, T -* .. -* .. External Functions .. - LOGICAL LSAME - EXTERNAL LSAME -* .. -* .. External Subroutines .. - EXTERNAL DLAMC2 -* .. -* .. Save statement .. - SAVE FIRST, EPS, SFMIN, BASE, T, RND, EMIN, RMIN, - $ EMAX, RMAX, PREC -* .. -* .. Data statements .. - DATA FIRST / .TRUE. / -* .. -* .. Executable Statements .. -* - IF( FIRST ) THEN - FIRST = .FALSE. - CALL DLAMC2( BETA, IT, LRND, EPS, IMIN, RMIN, IMAX, RMAX ) - BASE = BETA - T = IT - IF( LRND ) THEN - RND = ONE - EPS = ( BASE**( 1-IT ) ) / 2 - ELSE - RND = ZERO - EPS = BASE**( 1-IT ) - END IF - PREC = EPS*BASE - EMIN = IMIN - EMAX = IMAX - SFMIN = RMIN - SMALL = ONE / RMAX - IF( SMALL.GE.SFMIN ) THEN -* -* Use SMALL plus a bit, to avoid the possibility of rounding -* causing overflow when computing 1/sfmin. -* - SFMIN = SMALL*( ONE+EPS ) - END IF - END IF -* - IF( LSAME( CMACH, 'E' ) ) THEN - RMACH = EPS - ELSE IF( LSAME( CMACH, 'S' ) ) THEN - RMACH = SFMIN - ELSE IF( LSAME( CMACH, 'B' ) ) THEN - RMACH = BASE - ELSE IF( LSAME( CMACH, 'P' ) ) THEN - RMACH = PREC - ELSE IF( LSAME( CMACH, 'N' ) ) THEN - RMACH = T - ELSE IF( LSAME( CMACH, 'R' ) ) THEN - RMACH = RND - ELSE IF( LSAME( CMACH, 'M' ) ) THEN - RMACH = EMIN - ELSE IF( LSAME( CMACH, 'U' ) ) THEN - RMACH = RMIN - ELSE IF( LSAME( CMACH, 'L' ) ) THEN - RMACH = EMAX - ELSE IF( LSAME( CMACH, 'O' ) ) THEN - RMACH = RMAX - END IF -* - DLAMCH = RMACH - RETURN -* -* End of DLAMCH -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC1( BETA, T, RND, IEEE1 ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - LOGICAL IEEE1, RND - INTEGER BETA, T -* .. -* -* Purpose -* ======= -* -* DLAMC1 determines the machine parameters given by BETA, T, RND, and -* IEEE1. -* -* Arguments -* ========= -* -* BETA (output) INTEGER -* The base of the machine. -* -* T (output) INTEGER -* The number of ( BETA ) digits in the mantissa. -* -* RND (output) LOGICAL -* Specifies whether proper rounding ( RND = .TRUE. ) or -* chopping ( RND = .FALSE. ) occurs in addition. This may not -* be a reliable guide to the way in which the machine performs -* its arithmetic. -* -* IEEE1 (output) LOGICAL -* Specifies whether rounding appears to be done in the IEEE -* 'round to nearest' style. -* -* Further Details -* =============== -* -* The routine is based on the routine ENVRON by Malcolm and -* incorporates suggestions by Gentleman and Marovich. See -* -* Malcolm M. A. (1972) Algorithms to reveal properties of -* floating-point arithmetic. Comms. of the ACM, 15, 949-951. -* -* Gentleman W. M. and Marovich S. B. (1974) More on algorithms -* that reveal properties of floating point arithmetic units. -* Comms. of the ACM, 17, 276-277. -* -* ===================================================================== -* -* .. Local Scalars .. - LOGICAL FIRST, LIEEE1, LRND - INTEGER LBETA, LT - DOUBLE PRECISION A, B, C, F, ONE, QTR, SAVEC, T1, T2 -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. Save statement .. - SAVE FIRST, LIEEE1, LBETA, LRND, LT -* .. -* .. Data statements .. - DATA FIRST / .TRUE. / -* .. -* .. Executable Statements .. -* - IF( FIRST ) THEN - FIRST = .FALSE. - ONE = 1 -* -* LBETA, LIEEE1, LT and LRND are the local values of BETA, -* IEEE1, T and RND. -* -* Throughout this routine we use the function DLAMC3 to ensure -* that relevant values are stored and not held in registers, or -* are not affected by optimizers. -* -* Compute a = 2.0**m with the smallest positive integer m such -* that -* -* fl( a + 1.0 ) = a. -* - A = 1 - C = 1 -* -*+ WHILE( C.EQ.ONE )LOOP - 10 CONTINUE - IF( C.EQ.ONE ) THEN - A = 2*A - C = DLAMC3( A, ONE ) - C = DLAMC3( C, -A ) - GO TO 10 - END IF -*+ END WHILE -* -* Now compute b = 2.0**m with the smallest positive integer m -* such that -* -* fl( a + b ) .gt. a. -* - B = 1 - C = DLAMC3( A, B ) -* -*+ WHILE( C.EQ.A )LOOP - 20 CONTINUE - IF( C.EQ.A ) THEN - B = 2*B - C = DLAMC3( A, B ) - GO TO 20 - END IF -*+ END WHILE -* -* Now compute the base. a and c are neighbouring floating point -* numbers in the interval ( beta**t, beta**( t + 1 ) ) and so -* their difference is beta. Adding 0.25 to c is to ensure that it -* is truncated to beta and not ( beta - 1 ). -* - QTR = ONE / 4 - SAVEC = C - C = DLAMC3( C, -A ) - LBETA = C + QTR -* -* Now determine whether rounding or chopping occurs, by adding a -* bit less than beta/2 and a bit more than beta/2 to a. -* - B = LBETA - F = DLAMC3( B / 2, -B / 100 ) - C = DLAMC3( F, A ) - IF( C.EQ.A ) THEN - LRND = .TRUE. - ELSE - LRND = .FALSE. - END IF - F = DLAMC3( B / 2, B / 100 ) - C = DLAMC3( F, A ) - IF( ( LRND ) .AND. ( C.EQ.A ) ) - $ LRND = .FALSE. -* -* Try and decide whether rounding is done in the IEEE 'round to -* nearest' style. B/2 is half a unit in the last place of the two -* numbers A and SAVEC. Furthermore, A is even, i.e. has last bit -* zero, and SAVEC is odd. Thus adding B/2 to A should not change -* A, but adding B/2 to SAVEC should change SAVEC. -* - T1 = DLAMC3( B / 2, A ) - T2 = DLAMC3( B / 2, SAVEC ) - LIEEE1 = ( T1.EQ.A ) .AND. ( T2.GT.SAVEC ) .AND. LRND -* -* Now find the mantissa, t. It should be the integer part of -* log to the base beta of a, however it is safer to determine t -* by powering. So we find t as the smallest positive integer for -* which -* -* fl( beta**t + 1.0 ) = 1.0. -* - LT = 0 - A = 1 - C = 1 -* -*+ WHILE( C.EQ.ONE )LOOP - 30 CONTINUE - IF( C.EQ.ONE ) THEN - LT = LT + 1 - A = A*LBETA - C = DLAMC3( A, ONE ) - C = DLAMC3( C, -A ) - GO TO 30 - END IF -*+ END WHILE -* - END IF -* - BETA = LBETA - T = LT - RND = LRND - IEEE1 = LIEEE1 - RETURN -* -* End of DLAMC1 -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC2( BETA, T, RND, EPS, EMIN, RMIN, EMAX, RMAX ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - LOGICAL RND - INTEGER BETA, EMAX, EMIN, T - DOUBLE PRECISION EPS, RMAX, RMIN -* .. -* -* Purpose -* ======= -* -* DLAMC2 determines the machine parameters specified in its argument -* list. -* -* Arguments -* ========= -* -* BETA (output) INTEGER -* The base of the machine. -* -* T (output) INTEGER -* The number of ( BETA ) digits in the mantissa. -* -* RND (output) LOGICAL -* Specifies whether proper rounding ( RND = .TRUE. ) or -* chopping ( RND = .FALSE. ) occurs in addition. This may not -* be a reliable guide to the way in which the machine performs -* its arithmetic. -* -* EPS (output) DOUBLE PRECISION -* The smallest positive number such that -* -* fl( 1.0 - EPS ) .LT. 1.0, -* -* where fl denotes the computed value. -* -* EMIN (output) INTEGER -* The minimum exponent before (gradual) underflow occurs. -* -* RMIN (output) DOUBLE PRECISION -* The smallest normalized number for the machine, given by -* BASE**( EMIN - 1 ), where BASE is the floating point value -* of BETA. -* -* EMAX (output) INTEGER -* The maximum exponent before overflow occurs. -* -* RMAX (output) DOUBLE PRECISION -* The largest positive number for the machine, given by -* BASE**EMAX * ( 1 - EPS ), where BASE is the floating point -* value of BETA. -* -* Further Details -* =============== -* -* The computation of EPS is based on a routine PARANOIA by -* W. Kahan of the University of California at Berkeley. -* -* ===================================================================== -* -* .. Local Scalars .. - LOGICAL FIRST, IEEE, IWARN, LIEEE1, LRND - INTEGER GNMIN, GPMIN, I, LBETA, LEMAX, LEMIN, LT, - $ NGNMIN, NGPMIN - DOUBLE PRECISION A, B, C, HALF, LEPS, LRMAX, LRMIN, ONE, RBASE, - $ SIXTH, SMALL, THIRD, TWO, ZERO -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. External Subroutines .. - EXTERNAL DLAMC1, DLAMC4, DLAMC5 -* .. -* .. Intrinsic Functions .. - INTRINSIC ABS, MAX, MIN -* .. -* .. Save statement .. - SAVE FIRST, IWARN, LBETA, LEMAX, LEMIN, LEPS, LRMAX, - $ LRMIN, LT -* .. -* .. Data statements .. - DATA FIRST / .TRUE. / , IWARN / .FALSE. / -* .. -* .. Executable Statements .. -* - IF( FIRST ) THEN - FIRST = .FALSE. - ZERO = 0 - ONE = 1 - TWO = 2 -* -* LBETA, LT, LRND, LEPS, LEMIN and LRMIN are the local values of -* BETA, T, RND, EPS, EMIN and RMIN. -* -* Throughout this routine we use the function DLAMC3 to ensure -* that relevant values are stored and not held in registers, or -* are not affected by optimizers. -* -* DLAMC1 returns the parameters LBETA, LT, LRND and LIEEE1. -* - CALL DLAMC1( LBETA, LT, LRND, LIEEE1 ) -* -* Start to find EPS. -* - B = LBETA - A = B**( -LT ) - LEPS = A -* -* Try some tricks to see whether or not this is the correct EPS. -* - B = TWO / 3 - HALF = ONE / 2 - SIXTH = DLAMC3( B, -HALF ) - THIRD = DLAMC3( SIXTH, SIXTH ) - B = DLAMC3( THIRD, -HALF ) - B = DLAMC3( B, SIXTH ) - B = ABS( B ) - IF( B.LT.LEPS ) - $ B = LEPS -* - LEPS = 1 -* -*+ WHILE( ( LEPS.GT.B ).AND.( B.GT.ZERO ) )LOOP - 10 CONTINUE - IF( ( LEPS.GT.B ) .AND. ( B.GT.ZERO ) ) THEN - LEPS = B - C = DLAMC3( HALF*LEPS, ( TWO**5 )*( LEPS**2 ) ) - C = DLAMC3( HALF, -C ) - B = DLAMC3( HALF, C ) - C = DLAMC3( HALF, -B ) - B = DLAMC3( HALF, C ) - GO TO 10 - END IF -*+ END WHILE -* - IF( A.LT.LEPS ) - $ LEPS = A -* -* Computation of EPS complete. -* -* Now find EMIN. Let A = + or - 1, and + or - (1 + BASE**(-3)). -* Keep dividing A by BETA until (gradual) underflow occurs. This -* is detected when we cannot recover the previous A. -* - RBASE = ONE / LBETA - SMALL = ONE - DO 20 I = 1, 3 - SMALL = DLAMC3( SMALL*RBASE, ZERO ) - 20 CONTINUE - A = DLAMC3( ONE, SMALL ) - CALL DLAMC4( NGPMIN, ONE, LBETA ) - CALL DLAMC4( NGNMIN, -ONE, LBETA ) - CALL DLAMC4( GPMIN, A, LBETA ) - CALL DLAMC4( GNMIN, -A, LBETA ) - IEEE = .FALSE. -* - IF( ( NGPMIN.EQ.NGNMIN ) .AND. ( GPMIN.EQ.GNMIN ) ) THEN - IF( NGPMIN.EQ.GPMIN ) THEN - LEMIN = NGPMIN -* ( Non twos-complement machines, no gradual underflow; -* e.g., VAX ) - ELSE IF( ( GPMIN-NGPMIN ).EQ.3 ) THEN - LEMIN = NGPMIN - 1 + LT - IEEE = .TRUE. -* ( Non twos-complement machines, with gradual underflow; -* e.g., IEEE standard followers ) - ELSE - LEMIN = MIN( NGPMIN, GPMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -* - ELSE IF( ( NGPMIN.EQ.GPMIN ) .AND. ( NGNMIN.EQ.GNMIN ) ) THEN - IF( ABS( NGPMIN-NGNMIN ).EQ.1 ) THEN - LEMIN = MAX( NGPMIN, NGNMIN ) -* ( Twos-complement machines, no gradual underflow; -* e.g., CYBER 205 ) - ELSE - LEMIN = MIN( NGPMIN, NGNMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -* - ELSE IF( ( ABS( NGPMIN-NGNMIN ).EQ.1 ) .AND. - $ ( GPMIN.EQ.GNMIN ) ) THEN - IF( ( GPMIN-MIN( NGPMIN, NGNMIN ) ).EQ.3 ) THEN - LEMIN = MAX( NGPMIN, NGNMIN ) - 1 + LT -* ( Twos-complement machines with gradual underflow; -* no known machine ) - ELSE - LEMIN = MIN( NGPMIN, NGNMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -* - ELSE - LEMIN = MIN( NGPMIN, NGNMIN, GPMIN, GNMIN ) -* ( A guess; no known machine ) - IWARN = .TRUE. - END IF -*** -* Comment out this if block if EMIN is ok - IF( IWARN ) THEN - FIRST = .TRUE. - WRITE( 6, FMT = 9999 )LEMIN - END IF -*** -* -* Assume IEEE arithmetic if we found denormalised numbers above, -* or if arithmetic seems to round in the IEEE style, determined -* in routine DLAMC1. A true IEEE machine should have both things -* true; however, faulty machines may have one or the other. -* - IEEE = IEEE .OR. LIEEE1 -* -* Compute RMIN by successive division by BETA. We could compute -* RMIN as BASE**( EMIN - 1 ), but some machines underflow during -* this computation. -* - LRMIN = 1 - DO 30 I = 1, 1 - LEMIN - LRMIN = DLAMC3( LRMIN*RBASE, ZERO ) - 30 CONTINUE -* -* Finally, call DLAMC5 to compute EMAX and RMAX. -* - CALL DLAMC5( LBETA, LT, LEMIN, IEEE, LEMAX, LRMAX ) - END IF -* - BETA = LBETA - T = LT - RND = LRND - EPS = LEPS - EMIN = LEMIN - RMIN = LRMIN - EMAX = LEMAX - RMAX = LRMAX -* - RETURN -* - 9999 FORMAT( / / ' WARNING. The value EMIN may be incorrect:-', - $ ' EMIN = ', I8, / - $ ' If, after inspection, the value EMIN looks', - $ ' acceptable please comment out ', - $ / ' the IF block as marked within the code of routine', - $ ' DLAMC2,', / ' otherwise supply EMIN explicitly.', / ) -* -* End of DLAMC2 -* - END -* -************************************************************************ -* - DOUBLE PRECISION FUNCTION DLAMC3( A, B ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - DOUBLE PRECISION A, B -* .. -* -* Purpose -* ======= -* -* DLAMC3 is intended to force A and B to be stored prior to doing -* the addition of A and B , for use in situations where optimizers -* might hold one of these in a register. -* -* Arguments -* ========= -* -* A, B (input) DOUBLE PRECISION -* The values A and B. -* -* ===================================================================== -* -* .. Executable Statements .. -* - DLAMC3 = A + B -* - RETURN -* -* End of DLAMC3 -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC4( EMIN, START, BASE ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - INTEGER BASE, EMIN - DOUBLE PRECISION START -* .. -* -* Purpose -* ======= -* -* DLAMC4 is a service routine for DLAMC2. -* -* Arguments -* ========= -* -* EMIN (output) EMIN -* The minimum exponent before (gradual) underflow, computed by -* setting A = START and dividing by BASE until the previous A -* can not be recovered. -* -* START (input) DOUBLE PRECISION -* The starting point for determining EMIN. -* -* BASE (input) INTEGER -* The base of the machine. -* -* ===================================================================== -* -* .. Local Scalars .. - INTEGER I - DOUBLE PRECISION A, B1, B2, C1, C2, D1, D2, ONE, RBASE, ZERO -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. Executable Statements .. -* - A = START - ONE = 1 - RBASE = ONE / BASE - ZERO = 0 - EMIN = 1 - B1 = DLAMC3( A*RBASE, ZERO ) - C1 = A - C2 = A - D1 = A - D2 = A -*+ WHILE( ( C1.EQ.A ).AND.( C2.EQ.A ).AND. -* $ ( D1.EQ.A ).AND.( D2.EQ.A ) )LOOP - 10 CONTINUE - IF( ( C1.EQ.A ) .AND. ( C2.EQ.A ) .AND. ( D1.EQ.A ) .AND. - $ ( D2.EQ.A ) ) THEN - EMIN = EMIN - 1 - A = B1 - B1 = DLAMC3( A / BASE, ZERO ) - C1 = DLAMC3( B1*BASE, ZERO ) - D1 = ZERO - DO 20 I = 1, BASE - D1 = D1 + B1 - 20 CONTINUE - B2 = DLAMC3( A*RBASE, ZERO ) - C2 = DLAMC3( B2 / RBASE, ZERO ) - D2 = ZERO - DO 30 I = 1, BASE - D2 = D2 + B2 - 30 CONTINUE - GO TO 10 - END IF -*+ END WHILE -* - RETURN -* -* End of DLAMC4 -* - END -* -************************************************************************ -* - SUBROUTINE DLAMC5( BETA, P, EMIN, IEEE, EMAX, RMAX ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* October 31, 1992 -* -* .. Scalar Arguments .. - LOGICAL IEEE - INTEGER BETA, EMAX, EMIN, P - DOUBLE PRECISION RMAX -* .. -* -* Purpose -* ======= -* -* DLAMC5 attempts to compute RMAX, the largest machine floating-point -* number, without overflow. It assumes that EMAX + abs(EMIN) sum -* approximately to a power of 2. It will fail on machines where this -* assumption does not hold, for example, the Cyber 205 (EMIN = -28625, -* EMAX = 28718). It will also fail if the value supplied for EMIN is -* too large (i.e. too close to zero), probably with overflow. -* -* Arguments -* ========= -* -* BETA (input) INTEGER -* The base of floating-point arithmetic. -* -* P (input) INTEGER -* The number of base BETA digits in the mantissa of a -* floating-point value. -* -* EMIN (input) INTEGER -* The minimum exponent before (gradual) underflow. -* -* IEEE (input) LOGICAL -* A logical flag specifying whether or not the arithmetic -* system is thought to comply with the IEEE standard. -* -* EMAX (output) INTEGER -* The largest exponent before overflow -* -* RMAX (output) DOUBLE PRECISION -* The largest machine floating-point number. -* -* ===================================================================== -* -* .. Parameters .. - DOUBLE PRECISION ZERO, ONE - PARAMETER ( ZERO = 0.0D0, ONE = 1.0D0 ) -* .. -* .. Local Scalars .. - INTEGER EXBITS, EXPSUM, I, LEXP, NBITS, TRY, UEXP - DOUBLE PRECISION OLDY, RECBAS, Y, Z -* .. -* .. External Functions .. - DOUBLE PRECISION DLAMC3 - EXTERNAL DLAMC3 -* .. -* .. Intrinsic Functions .. - INTRINSIC MOD -* .. -* .. Executable Statements .. -* -* First compute LEXP and UEXP, two powers of 2 that bound -* abs(EMIN). We then assume that EMAX + abs(EMIN) will sum -* approximately to the bound that is closest to abs(EMIN). -* (EMAX is the exponent of the required number RMAX). -* - LEXP = 1 - EXBITS = 1 - 10 CONTINUE - TRY = LEXP*2 - IF( TRY.LE.( -EMIN ) ) THEN - LEXP = TRY - EXBITS = EXBITS + 1 - GO TO 10 - END IF - IF( LEXP.EQ.-EMIN ) THEN - UEXP = LEXP - ELSE - UEXP = TRY - EXBITS = EXBITS + 1 - END IF -* -* Now -LEXP is less than or equal to EMIN, and -UEXP is greater -* than or equal to EMIN. EXBITS is the number of bits needed to -* store the exponent. -* - IF( ( UEXP+EMIN ).GT.( -LEXP-EMIN ) ) THEN - EXPSUM = 2*LEXP - ELSE - EXPSUM = 2*UEXP - END IF -* -* EXPSUM is the exponent range, approximately equal to -* EMAX - EMIN + 1 . -* - EMAX = EXPSUM + EMIN - 1 - NBITS = 1 + EXBITS + P -* -* NBITS is the total number of bits needed to store a -* floating-point number. -* - IF( ( MOD( NBITS, 2 ).EQ.1 ) .AND. ( BETA.EQ.2 ) ) THEN -* -* Either there are an odd number of bits used to store a -* floating-point number, which is unlikely, or some bits are -* not used in the representation of numbers, which is possible, -* (e.g. Cray machines) or the mantissa has an implicit bit, -* (e.g. IEEE machines, Dec Vax machines), which is perhaps the -* most likely. We have to assume the last alternative. -* If this is true, then we need to reduce EMAX by one because -* there must be some way of representing zero in an implicit-bit -* system. On machines like Cray, we are reducing EMAX by one -* unnecessarily. -* - EMAX = EMAX - 1 - END IF -* - IF( IEEE ) THEN -* -* Assume we are on an IEEE machine which reserves one exponent -* for infinity and NaN. -* - EMAX = EMAX - 1 - END IF -* -* Now create RMAX, the largest machine number, which should -* be equal to (1.0 - BETA**(-P)) * BETA**EMAX . -* -* First compute 1.0 - BETA**(-P), being careful that the -* result is less than 1.0 . -* - RECBAS = ONE / BETA - Z = BETA - ONE - Y = ZERO - DO 20 I = 1, P - Z = Z*RECBAS - IF( Y.LT.ONE ) - $ OLDY = Y - Y = DLAMC3( Y, Z ) - 20 CONTINUE - IF( Y.GE.ONE ) - $ Y = OLDY -* -* Now multiply by BETA**EMAX to get RMAX. -* - DO 30 I = 1, EMAX - Y = DLAMC3( Y*BETA, ZERO ) - 30 CONTINUE -* - RMAX = Y - RETURN -* -* End of DLAMC5 -* - END - LOGICAL FUNCTION LSAME( CA, CB ) -* -* -- LAPACK auxiliary routine (version 2.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* September 30, 1994 -* -* .. Scalar Arguments .. - CHARACTER CA, CB -* .. -* -* Purpose -* ======= -* -* LSAME returns .TRUE. if CA is the same letter as CB regardless of -* case. -* -* Arguments -* ========= -* -* CA (input) CHARACTER*1 -* CB (input) CHARACTER*1 -* CA and CB specify the single characters to be compared. -* -* ===================================================================== -* -* .. Intrinsic Functions .. - INTRINSIC ICHAR -* .. -* .. Local Scalars .. - INTEGER INTA, INTB, ZCODE -* .. -* .. Executable Statements .. -* -* Test if the characters are equal -* - LSAME = CA.EQ.CB - IF( LSAME ) - $ RETURN -* -* Now test for equivalence if both characters are alphabetic. -* - ZCODE = ICHAR( 'Z' ) -* -* Use 'Z' rather than 'A' so that ASCII can be detected on Prime -* machines, on which ICHAR returns a value with bit 8 set. -* ICHAR('A') on Prime machines returns 193 which is the same as -* ICHAR('A') on an EBCDIC machine. -* - INTA = ICHAR( CA ) - INTB = ICHAR( CB ) -* - IF( ZCODE.EQ.90 .OR. ZCODE.EQ.122 ) THEN -* -* ASCII is assumed - ZCODE is the ASCII code of either lower or -* upper case 'Z'. -* - IF( INTA.GE.97 .AND. INTA.LE.122 ) INTA = INTA - 32 - IF( INTB.GE.97 .AND. INTB.LE.122 ) INTB = INTB - 32 -* - ELSE IF( ZCODE.EQ.233 .OR. ZCODE.EQ.169 ) THEN -* -* EBCDIC is assumed - ZCODE is the EBCDIC code of either lower or -* upper case 'Z'. -* - IF( INTA.GE.129 .AND. INTA.LE.137 .OR. - $ INTA.GE.145 .AND. INTA.LE.153 .OR. - $ INTA.GE.162 .AND. INTA.LE.169 ) INTA = INTA + 64 - IF( INTB.GE.129 .AND. INTB.LE.137 .OR. - $ INTB.GE.145 .AND. INTB.LE.153 .OR. - $ INTB.GE.162 .AND. INTB.LE.169 ) INTB = INTB + 64 -* - ELSE IF( ZCODE.EQ.218 .OR. ZCODE.EQ.250 ) THEN -* -* ASCII is assumed, on Prime machines - ZCODE is the ASCII code -* plus 128 of either lower or upper case 'Z'. -* - IF( INTA.GE.225 .AND. INTA.LE.250 ) INTA = INTA - 32 - IF( INTB.GE.225 .AND. INTB.LE.250 ) INTB = INTB - 32 - END IF - LSAME = INTA.EQ.INTB -* -* RETURN -* -* End of LSAME -* - END - -C---------------------------------------------------------------------------- - odepkg-0.8.5/src/cash.tgz0000644000000000000000000021024112526637474013376 0ustar 00000000000000‹%;dGä[[sÛF–ös~EÏSH‚DJ²ã¤Tµ Ñ$aã dæ–(‰ %jHÊŽÿý|çt7иPñÔN¶vk‘X¢ûô¹_[׋Ýýñ«¿÷9Áóîü\}[ý®ŸW½“w§½“Óó³Óþ«“^ÿ¼wöJœÿÍxñó¼Û/¶B¼Êï7‹ÝáuõÞb¾ÿy®IþËÏ7·7‹¥{û·œAüx{vÖ.ÿ³ÞÛÞY¿&ÿ³³~ÿ•øaâÿwù‹|"Å0ñ¥ˆäÀùžñ,’i0ôÂp.²$¼”™ðDgšdY0 ò`4êþp-²y–ËH$#1 Ò,'©/S‘¤~{é\øX&Sç ùëÌ˃$Î6&)†A,½ð‚hà 8û‡k¼nž¾mWw÷{ÑvEÊ">,ooEêŠ!ÔUt~wImÿkõð´Ü®kwqí>ÿAáÿü~µOÛÍÝvñ ðãív¹»Ííþëb»üE|Û<‹ëÅ£Ø.oV»ývõùy¿«½X<Þo¶Øÿ°¹YÝ~£žo–[±¿_Šýrû°›[þeÏÄxù¸Ü.Öbúüy½ºáêzù¸[b÷gÓg»ûåøü7ŒƒLc FÀ]ìW›Ç_Är…÷[ñe¹ÝáwÑ}ˆ†èˆÍVt{B{+6O´­ \¿‰õb_ît™muÊKoÄê‘ÁÞož@Í=‚¾¯«õZ|^ŠçÝòöyíÖŠ« Ÿ$³\xñ\\yiêÅùü¬Ýßoðvùe© ÷냦íâqÿ ˆôf8Áo„A>'ìGAË,#úÔKó`8 ½TLg)TJºBdKBŠØw˜·â°6`àÍr¿X­wŠfýöF +Q>ޏ4œ&̘…#ðÜï÷O?ýúÕ½{|v7Û»ãµÚ´;^=ÞnŽÇÓðK³ yÏýþaÍÇÏ¡M;0f}#î_–Ъëåê ÐYˆk(ñ÷©ÌzóxÇ ÆÚR‚¿ˆÕ­xÜìñu»‚Žî7MeÂîR<^»Ž8ïaÑâñ5dží± F«[€­7›­#›Ýž–Gž'ý^ïä¨wzÒ#ùÏ2OóCüÀ_E6¤Ð˜ªrØÉOœIâÌOœ/œ\ƾœÀ|'¤O«$ýè¨ýoð þ¦¾FÚãeêÛ'8 'È“ÐIé‹Ç?AOœ_J0ÎÈ™bå%mt™¦]–ÀëÿÌó…DDCf{˜ \ÀWû-G½ò—O‹íþaù¸'õˆêx½ÓïíÕà×ëåÝRno ¹ÙÕ;Ñ÷~SŸÉÇ»53¢xŒs¼VnѼ$ÓX<Ê·â++,,ÿi½\ñ@·´æ²o¹Þ<z;êÝfsCFóyqóÐ ö6÷ðwŸ— “½æJÚ/þX’Ã1`à‚7âj±&®6XNH…ø°¸þçóž * }Þ«•ÞÃ+o.c½‚ÝÛXÑf±{¾»[îHíwH߯lo'E–öùùN-ÞÃvð„Øp½¹Yº6GÄWòÙ;ÍŠÍ ú–€–¹_íŸoŒ>¸ÿ;Uÿ Y\©¼ 2„[1 ²š_tæ^×™wúô基ü~‰»]ƒkì·¥9öY^8–ƒÔ †SI>º[œ­_Y^Y3' ~CÍ+Å¼Ñ kÊ@h]at†2+Vô`\N¸¼lâh¶ærœz†Óœ¶óÚ¢ Dâú®t3èiC”€þ@úB~¢Ø@™†o3†N± €”Iƒt©FS—yù$s9_Þ”x›!ú½wÄÛ³ó£·çïÑé½ÿé´ëj8}¦I“İ{~#tÅ€ìÅ*Ä©d˜…H¬ÐÒF,v#‘#QÊ'èã(=‰Âæ¨wþ“cÁNïûN§6NdKåp–fœ,[Ôéô9OHÄ"[¯‚VuBÖA˦)’1X¼{_`q,ލ8XS° Ü^î]$?®ÍœåÍ®+âÍv4Aôƒó¶¤¾9âéé¢=uÑžƒ$’/H¡ÿ$Ú1ËcdŒ)^«­[‡—J8šÊ^~Tšk!~›Ó'ù-Ð]Ü-Eÿí;·°«£ÿæSõˆkÃåJ5H' ³âJo8ð`¼VVr¤âÆSÃ,óY ÷>¢ºKåTz9É€ŒŠAriiõÆJZ”O #§y‘„à„°Å5b‰ùë…Z 5%o_‹ ‹í~×ÈO,UëS©Ï¡ªäI⼈RuGסþÀ Cui—B‚è5Nu’ØØÂ;±ªŠ ñ٥ٸúyMèsCŠ'.aA s"”ñö“Ê=5mÊjÞNQ™æÉ”¦t•Ì^.®&A©(Ð&_f° Ÿ©wë'Úþÿ*CDð4âÁ8YŒ'9~Èy£¡CëȘ¿AEBýXµ—"ãNßux‡£3 BÙhdÉ*9IÀ£Ð“ƒËÙ÷‘–D£‹~Ïé÷þ)µ"úg ½l&WÒw R&Y¯ÿ€¥´@G-ùئ&Ð{ƒ™rî±ÎQ&…+ofkÃdÀg’À ú0ÛlJÞøR†s—?>=1„«JŒ`• ºT%ÞïÀb§…xè@œ\@¢Ó 4-mdoRúì(ƒ`4·S.ÓD0Rˆ2‡O€¨Ld œ€6N­É “¦x§2Ï|>eq“N7”²Ç8R2šÕ̺Hµ£nl=±¶ÆIÞ¾²§JÙÐhÛ,”C&c♬ŸxÔûž „C'™ Þ‘ëÎd.bWª›WÏ^wÌªÒ »¤¤ 'Ð顜&^®ÁTAe4,˜ùÉ’¡wâÄ2^M½ŸÄ²Û8’Ï2(w¦qÇÒUæ0KÉ|DÓÛœ¶¡IœM“DSðRÌç`\mG‚L$¼QNšIÝU(Æð`ÒwÄ ò ÛÜ8=¶|S ¬†°W9;—t= 0˜eúe -ZFÉòzYµ†Ñ.¿ŒÔ•§ ¼±Eh6 ×3T†¤üu#/`“;N šÈÎYWR«i©†¤½â+äZÊ­s ]¢:³¥³/z¬Õ€ÛðUð*C”%Ùhª`l0¬°à¸ùdÖpä¬f„A±Þ§GdT‘ÍKD›OÕ·ì¼ùxc¡T!¸‡#>KiÜ…é‰sNtJúí P †®µ‡ “1göZfÔ[¥ò0¦Œ¢(i,"à VÞœº·s±D2ævÀïÒˆý2®8ÿfýŸ§Á'bT‚Q5\³ˆB€÷Æ ÂXƒ÷‰ ªb1²›b*ßfå>Å6 ¬î#Æ6û ¥8HŽq“ßõùݬõÝ©z'ÞoD¯uÑ-ê¿æ¼Ö,S=̘„j˜ý±>Ó|mØ‹‚¥ ð 1þ7™&†éÖ^ìúJ)p#Hð6½BŦ AßǼ‰œ³ -`5Äo‚®ôô &IzrøùäÐišÛf×l:­ì-x4ˆTh©8j{'•ˆÐÓ‡³CÅ)nï’TI?aŸ¯€|ÏÅ«7šz¨ÐA¾ª$WfÀIUÚj«ÂåDÆvÊOf­Ék–P¿)ÉJv¨F»I½O€è",Èô[«ՑÁB¯Âí–!wn% «P›S9L¢H%г8¤‘PÀaN"—Ò×I˜ÒLZ‘ÑfXCߊْ)À©8S…ÌÐˤ¡ó´nïLd“dú¤.தä ~Ý+xDSU¤Å&U…š‡Ê4Ň:qW]eõSžr³˜¸FÉ™9*-Ž¢~§GI¼†Œ5ÎP–u5õ‡”ñGx ᥽ŠP/´A¨ðXÀKP)E•×ܤ>—Ä•6mSïTqi° «ª{ j:ËΪÛeYf9Œ8o6°Ã„Â.£Á öMxÃ!wÆÓY?¨]Áš¨„plMÚ¢ìˆ2&ZÇG/¿î¶àFÌaûO`^‘Ä9NIUòÖbé•:—äâ²_Ó¼ÓA¶-¾àׯûÝ㸠^µ–@…9¢1â>rÙ ¸’TÖgÚ¾¢Y3IòÂ+oNF,àmRc§(RÊöEEô ‰F'ñQ,Çl :«Æ>J×8¥¢<'TY¼M’' ‘à2ð•ÂÌá:AWiùH•û8œ¸É~H›¸DÜ?²¸¯>Í‚js¨)Õød'£v®I)Ã+ÊC‘U{­yká¦GB%É0ÁÎÛº Ô#\Û”ÀÚ À3ŒÁ™Ò‰PÈ-€­‰y©#Tr{ Ö»‹“FÊ]}Ÿ6ÛEU驊µlÍê5ëÆEâSÄ 5ÅJakÔ™Rd7Ü—õ(EÖ`‘M½fŒWÝCè€qJÍÏT«Ã¡ ÀÛýfM†µø¼ë|c£y#ê³Ö(¡Nû nîƒK]óQ@ÉDGùîcöÚ uÛ) Þˆú?¨? öHû…ekË®\Çz¼â§vq»À„û'â‚Å¡™Ð ã´ CŧÃ0¡rVÙ\E¥"P9¯Â¨ Ò£]qtVn?“•HGþSaæ˜ú¹9ù䄵¨”‹06òcfz¦[P´Ó‘’P I׆eCÝt½3Õõå'òÒ9­6-(}‡¾ž «¹I ,ø‚ 6i®Ù9mör8:“ŠL‹DG–YœA¤™ dɼ¥/­Å}ë­ 9AÃylÑÜzZn-R¬) aJJËvØfɬ“X¿*•Èb7&§aEïRÚY¬gsx“_‘CµdÙ UÑÖR<¨Žu[«:?,Ƈ•éŸ5¸RjÎU“,‡•º“lº¢uÑN˜Š˜ŸtZÇ}”Rò o¢l;ô²Ü¡“VRA36jmÖ¹c‘ñYs;îËÖ†Zf 7‹ßáKÒÙ=¥‘qÑ›­ŽL.Dc\¢¹gÏK &³0Ï–ÝËÙHÙhæ:en)žŽT&~0O¢/”D 1½Ú/GÜ=aªÀêî Y ¯íeä¡®¥¨Y7F] SÖŠ²?Ö›`³!íšP^Sì­=WöNl†Zé«…e¿0Ý,‰JÛÕ¢nÞx¹ªfèš­ûƒy9B·P'"að³©2ÁõÔÊl‘…sÉMŠ‘ ¢B·=rÞpÎ Ö…J€±CÚŒ>ý7í 'Ôh±ñà$’™ç9¦k@/°ÜÞ[pÿ{YNù[¡ŒHU KÐ6ô¨My‹¥í°-Ø'–~Bw4躑ª{áÖYçÆpˆ!PIíœbæifÝʳšÙnPr˜ó.‰œN4ˆ×‡=tœÊïÔܵ!™åê:U¶9„–VÃ+ˆÈÓ¹rü•iŽeX¨;orä•F½¢(3yæÍEJ™[ÚÊÅ^R7ýj}V{ÎQÄ^:¨Ø¥3˳.nŽGȳó•jzŠÒ.ˆNY¡sCc–R»©{Ü;9éÚz=vTÙl4 øÒ‘Jõè°lê Ë[‚!Öî~}·q² ÀxgÈÉ÷映 }éÔœm‘^QjƒÜ›%ëV„僕âtÄ‘è [TÐËÜŽ¡ÊlZkR¹ V86QV~/÷ÌÁs•òqò-s¨«×¯iz\žj½ÍšmI­›e׆Õ0Ë’a`ùJÓJ-R!¨K~¦À#€Ôõ…。"%•Y1ùÒt×h2ÇOíÓMHWq‚£à Ë÷Cs3~ôHá¬ÏûÆùûrX¶‹pF/“HuÑ(º“¨ÎŠÛX»õ_æ¨û¥Å¥Vïõ'œ9Ðî [Ñ+⊾9ft\£mé#O˜3•Ã狦Ɍ¯ý™¾I›»JüÈ0ˆ‚¸2+ø,,Ài¤ÆUÂ@ןŸÜ`åqG6£[’ä<õåAH×#´áØy³øóé›#nv׋µ#V7‹‡ÅŸøõf³·½Ô ¶¬®Jgëþ4{¿#|«Ý é„^ªü=].Jc/,ÍŸÿÖ™;s?Éùo!ø¯"ˆS…R2bÈ"Yah]yIù…÷—©k 5 [°ðE¦…­®ê¾°—zŽä1°Ï#¨ŽhfOŒ-Q%Ù!"¦¾;‘$ÑVh©¿ŽËÙrûøÏzT±Â™Ž .¹HT†JWÞ¥š/Å@â _L‚3ƒ“úû;Æ‚-[´óÜ„ÁNù’E5˨=\è‰óëæŠ—ä©èÔï·ny3Îì%MHt詳‘KÈWõÍ›¥„õèÁšw>t[n Üx §+ÏmWªwþB¯[HñG .È•¶÷¾ IjÓYýþæáùMÏùdé[mYyÙ–Û/ŠrÁÔÿ¦‹`L9–‡ EѼ¸ª?<FÔÃf€ÀœÏ*@äeð^änÛ¶*¦eϹ#%#š^_œ]ó<·rÝ ¸_PD Õ»:ÁþÓ1*Uû€é¶hôKdÛÏ‹£¶v®´·“#a_ˆÛ®°/ñ[4æñ”ééêNg¤ÌðB¨_¢¨[¿q£–…C;9i n˜Ò¬ŒO?úð†/ @y?t/4•€V^S!Gê3‡‹†®G#2?ǯ_÷õßn2™µ¬çØæ'Æ}—¬}kš¬: ›å¾Æª®åv…[ÃÕܪ‡H'ó.e¿öaÁeÙUåûgÅnksÙÖ «äÚq_Õ¢zÃt†êVð‹Dÿ@ye²}’W> rÂЋ†“Îòi·Zo4ŠBÌÔîÙýs»7UO)¹ 7Dç‚«©>OM¸[U¶°*Ý*^®³’2×5£€_ùuçBÕfg ©{! HfçòžsÞW–ŒµK{`Ž÷±3Ô—(A¼­ ·4N íÅIßÌŽwõ& ’T éñDíÌ?Õ ñêÈØ,ïkê9|c;] (·÷Njû¤²F¥Á?Ö·RYbmíÕ¶æAĹ3UøVµd,Óäû ÕÔ ‡êY\•gõ[ÏâtQýÁ¿¾J¥.µ³JMZ¬@ÕºáY9–L·â‹OùÀæ=%­vš"Î.ʯ °,Ÿ–ÀÎÀôÕ~[cYM‰h+76§.÷R쉹¯&Ѧãa®—-»Àúƒ±÷®Ýi$É¢èþì_‘w­³–A*U¼¤½·öº(™‡ÈjÍ7,a›¶Þu·ç×ßx仲’Õ=3÷ =cAU>"###ã•‘ÎéãÚšªÐKÛJršÚÛ)F®ñ¾E!Sï¥7æCW›«dœÔ»w·ïäQms¾ÐÓÎ ­R/•þ¦˜Ìÿ¼ê£·ŽÔvN‘ä©âÖù@¿7í{†ëNÀ锎„r(/Ît¸Éï¸áÑ7SÑ¨í¤©¸œLžÓ öøß¯ú¼“µËeåanÏ®dz(—®¥•ˆO½ó©vëðûàµs²]7iÚf¥ дٗ %j±ÝT Ãb$ƒè<þEI¯°Çbüõ¡ãRnAm͑٘pqà<4à8Ja“­Fi-JëQÚˆÒf”FéQ”Æø?¼mÉ Ð8m~˜úR:¯ã€Ô]ê·lS³ÏÿÚÂ݃§d¡5­Ðó—m}* 8Ãåîe9.r`ˆ¦2|9Ä®Šÿ>×cýEH⨒@÷½2ÌÖÞ™ÇÝ>H‡N¡S†Êýi9©Õä{íÄ(¶îÅɾ¨º/ #ŽÅ>½u_UñUB¯¼75|SÅ7îó:>¯eŸ7ðy=û¼‰ÏÙç‡ø¼™}~„ϳÏã ¾8°Y%IzbfeK FbÂ’G2[å2· ŒŽ€’\Å{-Å7ƒ' ßy¥rhUãÜÂ}ÜÍ34Äe•?ξs‰¬D†£Åètª!ìCï<Ò’/ Ã}Ø%ƒèîÿÛyŒ­8¥ðÌžÓqæ^`´À~ŽwNa•½Â×X8HÚ¶ôGœiÄÇ•T/%ƒB±“쩘~‹"€$öt,÷»á®$ÿ“É=´o Û¡¡lverÉ0Û!w ‘LµÈS`ð&ÿ‰$¾4Qou±šúRW_êK3Ô¡z{¤;ªèoЧœ 5;Q‹ÝŽŽJ¥"¿HÏäsqšq2ºH$R:E¤4ƒH ü‘’Û°i°+ú›ŠDËHË…YÔÄ Pž—²L4óS‚S@ˆ¢NŠ?åL0æe‡´ÂwTð÷HÜôFýŽü ¿†ò'ÿÂ0Ç¡Ö#)p~ÃÒŽÍ×D~Âìò×Ö¸Ç_.® š‹ª–.®Û£‹빈Zƒ‚ÅJUAiõƒu1F‡î…Šdao¨Ê²¯ÞÌŽuCåŸÚ\ŽWžä&ïòFRt`¢ƒè='\‘î'þnû9¥­ŽtÖì}„µljâ=Óƒïm¤´(–;à]F’±»¼Gñä=]–û£ánGÈkÿ´ÁdŸo9žFx<ÉŸ8;¦€O.jÏ1n“û!óÞ¶DOÑlUŠè2™Yv`ú7-l¯Zü´Ä#cêš8¶.ZÃ,ßèâ:â…h¤5 £˜ÃÌ)”4²BA¾m#ÄÉmîÍ–¤ˆ CÙv O»Ñðþßé¶#²…DdÖˆØà± b>b+Ð ™À<þüXû¹[cŠ0T¡˜ð÷Âhý/ÑÿpßHåc^iþ ÓŠúäÂðMqóHñr‰ HnRQ´¶ŠHn ‘µaøŠ¤jmÇØ‰ÜžÓ//¾ßJ³Œ>œ¢a¯­j^¡*rDDFgF(:F“B[›] äËNëduƒq  GÁ QPa”NZƒ‹èœè-ú€ºQfuHÒçEὓ«ƒWM÷—ž^I¼|xñùÕÆ](ÍKÒÖ…§Q/ê Ò!ü\…áaà¶©¿JÉÕF½´Ü-‡Î{1CFÖŸVާævŽúEAŽXšVŠ{q¥\éìW\ý 0-!ß,îõ0k—ðÁqlÿ;éœÀ üÄ,@v£§ˆ.ƒm†j¢zä{'Ž}SqÕ@©êœÓŠ ô´ŸöFð´ç<Ò¢‰ú¸šZì'AìK´£heoà†Aʨ˜ôÒ©Ãøª‰(L÷{E3Óüɸîò|´úyœ;/èç¹æL0NN˜LÕ£sñdTØâ(ÆeQà!ñ{´Ódv’¸Âöx«ÙCT£?E]âå&òHI©Il!I 6!mMÞ)Xœƒq)´wh%A"±G‡XKO“ß ÂE­pj‹Å{™9ä}fHx‹”`"E!¦I)Õì¹Éà‡‘H¦‰—Ø2+«ûŽúŠ•·âÄO§XFkõàdŒìuRè¡1ƒ¥“EaAã YP è(ndæ_Ù]šÞsÇB"”iGÿô÷ëù§Oy®¹ç[`Úíý+ Ps˜òBêÏðQ߆¤“gh¢êO×[¡5úWÛ„¬ »3‘k±t±oI¼ÊœÔîuÛdL”’‚é¼ÅXSþu®ŠâÒ1qtÀZ8ì–3ËV/£ ®cééÛBÉM´šBñë|nà3;΀ ý¸Íõ QA´è v³X¤Ç pyþ’ÂÓ"™?ó((«Ã†ò`Üþ–aÖÂÔa¬çJ†s?;ftŽàŽ«~$öbãØÅEÄEë¸ð2èŒ Y hÔ³8ö†£O2£¨¿,U¨ tlÁº;ˆ;Í‘ÍîHÖzÐï>¢®‡ÉI˜qÀt€u:²!Lƒ(ÍŒ&›Ežó^Qq²ÓŠr@"“§dÖ|”ÞÛÌøxý>—«ç_ m¨'œÃ9t¯}äit+¥I9g[™BhÁû¢='aAئû€²—¯wTþPf V滣€ãá¼ÄŠÄP‡]ŒîÃDÊ“QnÐzzj…¥Œ)t^’ŽuÁ™pÈ$gBPÔ±gô £XƒŸÊ¼©’(‡ƒÊBSöc³% 3š¿ýmýÂ'µ(—º#«A¯YzÀ ÔYˆCPR©²ïy7bDH-DY±"R £Œ;^ VÚkn*î.å:æ=MþŠP«FŠ‹+¦dv£™­N‚T÷eê©Fqñ˜Àx‡e¶6LÇ+Î#_}ÆG87r|½‘§-Ú¶*ØhÔ€­JXZŸû˜ŒËîSOçá_9žHõ\P@+x§0ØÿÉ-“ŠÜ÷{$)Nƒ‚‚Äá‚"ñ‰”Îózl·% ®cSœæ¹ë³Îž©åŠº‰†váí¾&ãgj ²^+é®B‡yœØ1òL@À©ÝÂVÒsþ#« +IºŒØÄŽ#F/®Iø‰ÌÕä†md"?NEÏÓ‡³÷(9z9¼±—¼em²DÐùL¾u@ȵÕJÆ\!›Ì؆ÛÄ‚Hýåß&fÕØ#ƒ‘©;@»7ºà˜ž*#îðý”Òúâ{`üëC×È…®êCÇò‡„ˆBõ輯Úä)<˜fÎ%Z¦„œu“AªçÃÕÌ…«ö<Öìñ]9T€-h1l¦Z1ì±ýÓØ·hÉ –u€¨ÌejSÁÝìê¢Csev¬c 2µe•:â™í6i“'ex£öÔîdJ9€¥àU`ò^ ´QæE­|„•Dí³Å„üº“t8éŽ\·§ôTÉSÕtÈk^¢-š Mn¯~ ʧ#Åå>‡}k‹vÉ‚ÒÅñi;ø¦¨3u ÃÝÒIp>j0f€ 3eŽi+ åÁðy6ubo™J“X«Tj¶AÒK\¢Óê¼&úle'ìô4›•M':!4—Ìʾ0ú({Ó!öx½ ŸŒ¼7¾õJEeñD°ëI¥üFËÊl´ ¡öÂû‰³E?9VFéÀók#¨Sy`HLh7©êÝDÉþwŠw0çÊp±Ez̘z@h«äÈÖMª¥G’Þ, +,8Bâ»Z…éÀëìϱŸ“Vjâ¦}^Œe}çýŽëŽ?Ž^fö`B2£™v3f½œaëxÀ4hªÅmjá1c ßÔC8YÓº«‹ùó·Å©»ÅŸKÚæ;Ò‘æ6þ\÷,`ÊiãŸ×=ûoo,}þ•¼±»8ÓvœãÍnx¢üI Ìþ¶5Îì.©¼{›}HŸb¢%=çIxt‹™É±d盘𯋳•°‚©¹QÊ­)ìªSN(Ñ›>·¬6xN G: kHb/—(ÇtÄËÒ¸¶¼ð,ýȧÀÞr¯nT|æLfrÔùi# S”@ïòµ×Z±I'£`84 , ü×ÉÖJ9ÆÕd)=ëé»ì¢ÊV³ÂtÙ(0}€Òa›¾½6¼Ã7¼Ãœ OºÃ­EìáT:r‰Ï¼É: ¬ÀÀÒuL&Î"‡Á–ÂùñŒ­es¢‡¨e#=œmµÌ˜)ó{¥lpZ«?S­¬V®·¡¬cª4ž«b9ñø¦&»Ãæsµyq½Çc–ªÎa¦ŽÎK§oý"™ÆÈvÇþ¬L±yMôÌfœ'=ó3[ÛUO1_Y·8åž›p/Ô¹Jh«Ù:O nœr==‡-ËS‰Ái$Ol9 N¯[fyÉZ"I0Îw‘Ö3¼H´‹Á×WCÌ2@™þ\NĶƒ´{åìø 'vcCeq†“e}¨Éš Y¦ò v䪒Yëþ‰~NU8Q.ESÓ_¤'­Ž¤C÷H^fm)5ïëýu¨R4ãt‹•éÒʈ¬BÏes¸)áÄþ"Õ2¼$ñ}ñ%~ÈoöÙüíÛ«ÚwͰӉÙÕP×yÙùçÃkœG¡£9Áþ^À{Ø÷–ŒâŒ¤‰÷Hym_¨³²K“:P¢ÈO,Çž¥ùœÌ&âÊMbÐÀ&û˜–ÂiÔšÎKI±Xo£\(+(¥1Õ À2YuØ<Úª”™VÎJ+yެFòÄ)è9/¥L]éeÚÚýmYß¹¹D©ûv¾¾ËýØ\ÈÃðŸ´p¹à}Øê¶R}:Úkh2u›Jü¦èZm÷ú”é¥I…'kÖø°áGÌÓἪçW“©º;ÍpÀ¡Œ€äŒ«/Ó1¬m~N2%e'Ø%ïsr.ºãéd*“ŸÚW ©|†]¾1@=5Xð-Ã(% BèÕðÒ©èL9¦ãè–2–jÌ‹¹dZ'lÚ!XÚ&Ÿ½Ê%#Q&/éR·\á£3X/°¿ñ~.£ÒɆàÀ [^–â(ÿeô\íýøÔõ—ì'ÃËWî$Ý>Gò¤/ÓËBý;ʇÜ*2oM¨ožøÂaT õøÜf´ëñ2‡/ªS’_í)±!Å È6µƒ¸|Éß þNÌï*þ®•ëžX(ßÖðm³\­Vu…:5X)×ý¨Aªåf¸‘&½n–㺮qHPTÊÍÚAèÄ2Áߤ†:Ñp…áN| ëôˆ€Ô`'®0܉ÒÀjåZ#îª g‚\üUñ‘- zÕƒ³NOü5ð‘- zUÁYÔŠ„`SÒÛ-v7ÉÃc세| 0H¥îk^hÚIÇ:½';•úûÂ)‡Â‹7cUrÉ¡%ñRºQ Q9ËŽ‘Aq™6íX}1V?6ÛŠ’JT­DµJT¯DJ„§ ¾”)‘c}éù?®{é:ÍRÅ~Î]²Y21m6Ê ëÓì”*±n‰Û©"šSBpT®7êIܬ¢îUË”¡v’²~_k&~J´YŠËv™†UÈ@Õ  ^®ÕÍõÀ`YÙEÜ5Æàalýb³ÜˆkI\šG£zpˆÕrrtT;Jª‡õæa#ÓõuXn$GµZÜHjÕF½e»LÐpTŽ«ÍJ½~XmÖU»ƒ†šA°…C›  ÄyZ㧉[¶ÎOã$D^r½Yhuœ¿ðÜÖÊ•JR=B#Wõ°8ió¨Ú8LêIãèðÈ/SçvjÕÃ$©5ãJ×|ä49õr³ÞlÀ$$qRëyÈ©äÔÊÕæQ½‘YáßÔËú-þmˆ¤ °GUþë©óš•ÃJró_·Hƒ§¿Y?Š“ZåþVý!Jš>Šã# £¸×3KE¢¼vTLU«õFœ„—S‚dT¯%Íj¥YkT‚(?*W€¬1L`³v×+ÜT£V9Jšf#Ž›M¿L“fȘ@Ü„˜£¼™iØ3S9ŒÕ¤QœÆI%85GñaµÑ¬Öõ‡ÍZpj›Íz=®ÀÜA{ÕÀÔ$e ßJ¥³ÒLâJ3051`´Q?:lT€‘4½"M^ ²þáa­Z¯fV¯âHµfý°Ù¨Ôb˜ÆœÙ;¬'õ:tXmT¼®Ìì9jÖ+Àtªqf†%ÅÁ»j³Š­V23Ü`x`Ö`éÂÔ“àÔß…€l°c¿Ì!MoV9ðÀ:´5r§·i¦&J׎ªq L¯Zyr\«&u˜™CàxI˜ÞZ9‰kˆÏjµ( OïQbýœ]˜Ü¸RoÀ€uT³—ã#X/ÀT—1êåzå¨Q‹‘‰^CP9::‚—@sÕZê°4kÀߎªõä0Î!à—°6pשfˆDlÛµÐdûE†;Kžs4Ô„ŽjIµžYßMž©£:¬Ü£Pv¥‘ἇÜWR©¢Õ¾ ë0ÉÒIR†qÕIrT‰ÍþvXAÔQE| ”IJê׬(…MP7?íé(cj‚’rûB¹f~&ŽÁkxY´wA*“g"P¿;ÖÍ—â(öâus€ ‹¸ma8åÝ‚Õhiu£Ža%„ç(m×ÄNŸÜÔt›¸R#­ˆ¢3@¯&|MôŠÇf3§ç…çžÌä0F2%_IRjêžö œ*÷’¥¨s~•hèÅqò»Cq~,¹G’}( •bwÿÜÈzü› À¦úà:Ç1 æ@¢Q‡â©ª-ºŽ%ÿnçðWVYH¡Åavb DÁ—Œ›¤Áç#½YQSÂ÷éf–â í]ÒØqRpÆ}’Io_’¦Ÿä×1¢²²L†t'8)ÇùqZ¢Ö£t?[®ÏqŸ£tXä# ‰?ÔBZŠ1‘ò¹²ù½'Gõº·Ó ¨.&â<ÄØ`c ÇÖðÉ6‘^ûHª}˜ºGòHõØû P[Œ_)2Ù?/É…ÀCˆ›î!8ð{$›=$ÀŒ@eSÑ¿jEs ã¥&Çô¯ÅÕ °P7ª ‡fiuX³±~[dÞúð‚ rH¼$,¶“H¶“‰Çu—­‚—-Úü%‰°ÆÞbܲ ãÐLâzfñå/;¢¿¦³ä²f3w¹mYhͦÈ+™ ¾­ÆîOŸB]6”G3ƒíœ•ÇØRÒéàäX/µ KÚa£Á©„F¼ †žè¥ù[ÒCÐcMê2qb@Oâ AHý1cÑø6e†zl¾âdoaHU—®«IˆÌ­é¶†›ý ±ÜÍ ›DH+ãø©=)ï;™gŸK´­Ôk#'oP¨‰4Œœ„AÛš0P0/¯Ö(ßñ”e$dXÓH‹{ê4^±¸ÇwvÔ‡ì£ãŽÂÒž¾å££õÝNñ #­iÚH†1ö2ûÅ^gO¿Þ“G€ªÅ=çÎ èo\¡Õ"ëŒ+ÇŽçò#Ø*¦´»‰½Õ-bjpœwwNAwçÆŽ·5{ÆXž[dÍ~8žƒVU9`ìkº±âå>·¥ Ù‚UÑI“Á`jxaîÆÎlÂ\˜'œ%O^ïªÍrašw@u8d(ÐÕ¹- âžñ FJí4-–ÙÖ\S3MPhÛÔ ‰…Ìt8„Ç/‘aàs[Åßï¨73ɉ;çÐBNx ÿßw§ôMÅÓ$£0ÓÃ&íÁƒ>ÊyÿUÒ€l¡ÔµòxO¸iÎÏȪ iÏE±Ëi餣Q‰Ç¹â¥1߸¹8…ú úÇ®iŸ]»¶9¢ÃWûÖv hi†R_Ñ|«QÓVAùµ¤ª%¬6‘ÐRs=Ù;lOfÝK»ESJ6ÕÂŵֳ`÷#XäÃ9µ §«Iù™&k9>‹'’¿¬/èn9×Ô”{z”¤‰s…ý,B°“sÜè/ Õð™ ˆŠn»€6 @Æîƒ·Úyæ|gÀ|±‹ ˜¿Yµ$œtµR†óä(•®<ø§5<»ê§™OâFœµ®&ºê¶Ÿ 囔ަ©”B€U}É«ôªW0FÔºí~ Ä9W¦I·Š*Û"ÎÐWIÁÔ§rNQ1N=¡;oÑ•o;èwËH9Wx6UW–çÏ"q¨œ‚ØÙUš¢ÓrŒ–{<;l!-õµò¢€×eòÕx¸~éÜŒêô'€øFxÿóµUzˆ„_@)j¯`ÎŽÑAy8°ˆ~»Š®BžÏ _?®ÄxRl’2Tÿ€ïÙ+o—–oC®¸#nuj•û“+Žè8íèÏÒæ;U>¶QháphX{- ‡?`½'Êôü×7vaʉƒJS§ˆõ8N’~ÄÛ[øð8œqGê]Õ\»|RL—¡;{4N§½¨%ÕH­\Û‚®J†bír«Ü.«<ãq]â£fSxQNšÚ·üfëjgï#3‚WzÉÈEŒX£ý¯>÷!úp"v4„.³y—nÏœw¶NÒ¼ú% ƶXýtAü €¥}¨$\sáPÔø@;ÖgÇBõAÿ24D„½3éœDÈ@>)•‚ü=ÀßEj)ÅqPõ%¦ìF?A#×~g÷2Àa~(š\V „“ϰzÌMP[ì¶ø§$JJšÚ&¯gÃ$wæ©•j+Œ „‰¿íQ¥jl¨8´¢q^tšÆrXÞ›üfêÍO`hu;Á§1¦ù¨}º£/û§·rÆ”1px†‹&SàÐ"`ÉóñoeA)ŠV„"NÍB}?AnYPþÉa]ÜjPf6jd–Z^(±àg·³\o£“ÂÛ1EXC'чA¼•3æó¶·ÞZr·hØ ÷0÷퉉”ÂÓê?-¨IùÑÔ\áLa!r` hMUA¿ÆSIR¢A—´óJ#"¡b@‚“<þ™Û¬aL¡pÅÞ¤gÌ[H06JÆ] K‹†Fw‰_Þpó7 ÝÝ©ê[wªä%;m"³>) ÌüAÏ>ØÏ>¸¼2;a“Ù‰´mZü/³¥%+-3×'Ù±èQ81±ûAì PBм½Œ²9Ã2{RŒËs§Qdö%ÏI,ý„#·ñÏ˶;³[üX. ¸¹ÄbØíãÓ§õêi³XÎÅÝ—OŸg…Ù§»èþn-£‡ûèá)Z|ÿm-–ŸW ¢År3ÿ2_ ¿FŒa9Yìnõôé~.¾¯ç·‹ÇÅj) áÖ¡É»³¹Gñyv»Y­Å,[çÓly'f›õâñ釘ß/ËÙ^•½FâéñivÿCÜ¿ó;,¯nW‘øô´‹ <‡çòµª¼€¾6Pé÷Åæ+@ð8ûm±üc›ÅÃ\,> ±¾]ØÁrµËùün~gz çËÍú‡~i4|±,š’ê]lf‹å£Ø|«ñ.äð9³/ó² —·«û§‡åc¶‰Õg»òl=§Š€‹©ˆå@h;Û –¼[̾¬–³ûÇ­m®W¿ x¸ß¡Ô—¯"ÙƒŸOðDuZÎÖxœÏ%|€ÉG˜¢ûÕïâój-îæ€”ûDz[@¡M…áÜÏgw8w0‡K"$9’Ùz=û‘ ¶ýðô¸A*)#Æq°–ž_Ž¥ü»ŽÕúŠì{µ^|â½—ètZƒn¶·¶|zøÄM™ÙaDñÁ„¨UDù†½”ï7e€ÝíüéÏ>­~›¿¨ó'«s¿ÌP•˜þ¼¸] в#`e=³ÖÖóÍÓzZl°¸Ÿ¾0¹³å—§ûÙ:g%åSþÃÓýfñý~1nôû×ÅíWñû{z’߬Äê.R`%`ufd‹¿ƒRìæ÷õb³™¹Ì€¿ßï®ÿ ¨xÌ ØÅêîévƒè†á<_¯Ô<hϨ”ÌŽ²ä Š šiß/f°_l‡A2³ÇOãbóàÝ&r{.#q& ÌÚT#¦ÛÕ=2L ½/ëÙ0å/ÈŒ&hÀó ÕZ,¿?mÊ^?ê«÷X¨§ßw°µf¥XE€Æ1#·¢a4ÏTD'®áÖÁ2w+ %áM,s-(7>Ìþ¨€&~-=<…;Ä’‰ò/¡äþChÜ3Ȳ (¾€FI^9ø|ÃB µþŠcÙRå©oѯ(6Î ø’-ËQÖ(\,–OÂd˜—ÎkZ$@¬´ÏË}]oìÖΞ³µmÏîxÑI[¬ac¥ŽZ\,•¢ìgoýÎïç,|™/çkX}wâîiTŠíN&ùmÙƒЬ6À[ÌæEý²üfº‡aZ£É6"è®pþ"éýüóFÂpO v*ÃH©’`†-`÷ÿjWa#ëZÏ?Ãn°¼µeÊ{XZ³Ûoež…ß`7ÂUG(¨ĵƒæ¡õö~ëãau?_G¸APñÍðrþ»x˜ÿ±@x¶†µB>KÏ€—ûÙ'Ó£a‡¼i|~ZÞb¹GÃ[îgÂßDwÀ΢ÅÝ Öˆ| s·YÆUá‚Ðõÿ¯—Äã×ÄÞ°IÆPFÚßxÆBv-*ѯѯOѯ~…¯q»Á÷8ºî"øï!Z>ÄØ£¬ï³Õ*ìHhÇR 1ˆ-uà}îVªÐòµ¬õ+FŸQ;Ê%ÿklýCÑRªÇà%I9¿VŠâË …-™Ïß¡6 š1ºã‚:!!ÖýßÍ ‹m@•‡{Çtˆ ÖýßùLj客5f³ª…»È)£öO¤ÀùõÉAÒ—ÙÓãã7X£{±²ô}¶&Ì‘ ËR£ &šXz˜Á§„šXa&¶Qƒi¿IÆü`¡æ™ÄbéfÑÜ-çl¼‰Ó@èqýÝuÓ$ýÄ{E$¶µ (A9`xk<-qfVò&¦¦N¼ØØ¯{ Ú ð3ì÷7Q-bÍÿ°wÿ ¨4ðeé›e¨ÆZ¼p ÷ûq„à<€øÄñÖ¼ý ßR\0ßàåCÏ„”Œ™)1¶Åì~ ªÖ‡£ní¢“4[~»Ó«#®T|IÆíWhŽÔïå$ËÇ)Ø6ޱÝ{nðA5Ö¨ø3±ÁÍQöš%‚Š‹<äyà"ij÷æÐ…tX‰=Â*ňƒ`§(( b°0wцçæfÏë ö³ìª”Côb/JþÐâfªAùX*l{’ŠÑÒ…åH,ƒõ_¥"+S!þ(³<YÌB. úx½'r+yg…t—¡Wf*ÍÌ“M ššjB· &ý!К,FïÔÌ7­Ûé<öižÓÜá™;þ¿~-:Y̽öŽBù#LŒÃÂLºyÈ}îËì±»ÄÙÝ€¨a‰_ZÜ›‰ué.aÙ“*'usþ1_6`Û =ËènÝÁ¾¾¼ý#ºûê€7³å†Lp¨°H}óûý“ù¥Ä’QŸ–ëÙüîW«ï¤OA“k)?Îÿ÷ ¶)Ôß—ZÉüä+ Óå´EJâŠDõ ŽAº*ç )w ¡óîý;ËJ,8$OÚÕâŠ'°̾´ø\X¢]£RtÐ…8¾›9ØõÞ°e|—AB+cô«Èt‘8|ôvu7'4<-yô:à©ÿÌf+(”jŒÅî?,¯øâ‡ýC‚<¡R¤b…Òr?.îáS[þ¢r?d¹N¹V9#ó ÞX€ðŤJùeHiæbñ‡Y¿ÔùûÄ®ÍcìkñƒÿB’GõY»’Fá§°Õ9±Èi*ÜÏgË(ÇH„Œ7XlÄÐVw@òúl óT Å­j–,TUÃp†¯Fo ¾–è°‡%séš°) „¨\„×¹G"Õ¨¶S¯º¶¤ ñw]~ú…«pâN2…«Váª[¸ªâýÃSÉ?rØÜbm>dø¾²ù¨q3Í”væ;¯d;T fñyÁFÇÛÕ]hšFôÀ»ürþöüßæ¦»H$ñÁQeëÂÁšpSŸiý±;³2Ìh;û1ØØ‰­ìÈIr81$—Ë8@`N_¸öë?µöÿ«p×5_ùš¯oíM¿Ðk=¸¾ÿ°ÖwpMÿa­ioÛjnÚKº"leÅPÚÐ2»ÖQÍz”æL˜Ñj"íNâ+»¡bäÁG«A²w,ÕÿàÕÌ‚aÜa›õl­d€Æc‘K,|aCŽm>@«ýŸ‹0”w0;^ñE»9_0l#±Øöf·€üÖª¿URáeaYhr`¨‡Áx ¾#ˆ¹x›\û†‰ä3;ÊjåeÃ,æp¡Ìš ¨è> ‡|Š~]}z6Î`¯ˆÅò–… 3€RŸ0i&ðx/`ÿ6ç6xüñ¸™+³ûLì ö'R³@çcaVTOµ( ŒÒ*’AÚL V§8…· ÇòÓzŸ×«‡ÜÞÄÛzÎÿEüàšûe®SlmhR¢|nª>É¿:Èk _‰€Ñ!©udS–Ìv CVµ¹â•¾Ç UX®–d ´+¨E¢êå¹»!¨……îöîÀƒï«Ç¹³^²¬R! ëÐ+‘ kÔÔ&÷°q;‹Ž¼¸Ì¤—\«ÛÛ§5*æÚÙ)×·‰Ö™y†ÒÕÒqT¢—z~ûu¹¸¥¸$öXKWò£íµľèÆÞ`ÔÀíŒ|cØâdt=ÍÖ_ž´&¯žšç´Æ#%㊠=n½Î ¯=G2‚J +:Tb’ý:{$?0»±É’^)WLmEÒº Ù‰¤Ìk¦„¤!¶—B t[Í™·ÞÊ ßÈcÍœBÖ#‘I|ÁP(¶Á!˜á†`Žþî.#4¾ð`üV@ÊÐŒÚËe×+Èb„t"÷ [·ÅÝÈX± ˆ^ØvàÝJh×ßk½~ØVä»þ¾Eß>¡oÝb/»ø¸ÔƒòÉ‘—O‰ƒa¿ò½2hà¢b9s<¾ˆ¬/ÒÏÛÞqŒä¼æ÷÷{?ø¥ëãxöç¬ßOäù¿ª«yŽŒ?[<>²ŠllËÏ?î·ο—LÀ Ž•©lÛôÖøH⟵”Cc ëK#y²‡Ws½fKä4>Ñ䏨m÷퓊ÿ°AúF¤ÃØoŸœWrtø'ì´13óÍõË×3R¾KPÎ}þ‰<›™çä#Ê`:„3Xe÷ŸÆj!Ï€q¶ W“Î,¹¥Gþv¾}QÈbOÅìâ¸C¿œ&ë?oÈL?yØñ'µ@KbãNmc ã!ß³täGÏòƒ MSk–!|“ÜÎ#W±«°HmªÒÂËÐi<ŸÍ„ØIÐ —ÇÄK8MžOn‹cÍv}Uv7(eö,mY"Ô-ó}Y°;>(usc‡vn~_IAõñŸÃ•Zï÷CR¾7 ‡o9à#lÈ{¶ƒùÿN§–ÄÿÝ—&ì=vqm±^xŸqkÉù±'÷/ðtýœµ;€FÉn†o;䥦ïü®å/2xïYž.®»',ë÷žãÛR6î=lj¥ ÛêiÐÄ ›PþüñËÂfý¡K1éÊô2êv†Q7ê^]D˜Ã¿Ûé _~¿ê«Rˆë$âéD'e×i¿'FÖM'ú¡Lœoã¿ ª¹ÛùÏWýÜùê¾l‘Ý.~\öF˜ÂÙ¾eÂä&êàáJ€H’ÿ¥âÒäæ­ÉdÔN1Û:çÜWgŽNÕÑL˜J@—sÓI‘ï¹Øµi ¡ ¦‹§vMcWÜ\•š«îÜ\/=ãÜò%C­zæFkÔhM6º'¯ÓHê ižöøß{öQ4½)qÃò`žì%t|ùbÜIÛÓ^o|õ£ats1Ú¡<à&Òé«n­xÅ‚È_#ÐB:-ëNëLÝQ˜î÷ŠêrÐ^æc2oÔ☊©^æP¢‹µiucYuÞÇ.D>p1²äÅ“«þ”³ò§jåË(L¯EÀÞ˜A0î o8ö—0ˆþÏßÍð³Yõw`.0itž¼>7þi êsÙëO»>câÄç‰p/‡_T­RÌ •v<ºä9Q·V?wÈÏábz|uYµ“N.«pR1;‰cì.Ýmk7Ž[Ó¤pyya>aXÃ'Ýi+‚†qm{Q t£)ÿD×£ñ‡®lˆïWà[oˆ»8wßà¼v1_Ôà”¯ÜÑ©Ñä;v#¹oLõ'‘w'Žy÷Óhx2§n3x÷À%½Õ çìfç…2ÀtÝ“|ÜÑ]%;¯ZB€l\6„Hü·^è«+ô¥[®ºÈÜoAs€•^rÑÅ[Üsñë6ÂIíœL^p¼î]åÈáK,ép§t.ðLV3ÐUWúª‰¤kf1·Òg9ðyÉÝuy^œŒx­dDœÞ‘ØÎ³é7™,8'çËT³à¥_Nòª©ÔÆØ÷¾ØžÃ¹ú‚FvÌ%í7’îIýØù¤)‹F:dS>ß´•LnÛ˜6âuûòB±}úåõ¬¾[{sâ[¹ôd·Õp·‰éVP ¥“¤û»@Q 0öö€ãçò–(À¨u:L¨T·¨Ï$Ìô€ÌnžVNÙBIÿ€­7¤=• gødZ,Àž%wÕüë°«š³Xòº‘­¾ƒâÁyœb¼$)·†r~B㣣8Îô—p:™8 É«s¾½çBæLÁÃ}hCf–-¾£Ú[PЦü­vQõ…š¶"„aI^By§JÔMdw’N®(¯ fɶèžê´ÉôÃÊœŒÍÉ¢”a»ì¥ûíxï¼dg^ÎÅч 9é>£'ÙJÁ˜Qó00èØmœÈ >·€R΀Rü „TBÑIßK(Ê›A$Ó¡²ðÃrTÅØÆhOÂeMƒùEU2›J^Ü ¶hçêÌkÊF ¼Ç­Ì‘‚ýË’ aIÝ׸3²²°’éÇÄ`«)Z¹ñY¥¡’j8,.gRîLÎ}‹®(Xó—}AÕÐpy(Áx`YF-ÑÛ–jw‘¾“ÐÕ,B©òt|Õ-¿cçÞÖ;0\Ö¸§`gžé3Íìí.Ó¬½ŒiÖ~ŽiÖ^Ì2k?Á0kÏ\9ó2†Yk¾v¼‚f «Üß•QÖÔ­4šM"—Ýkǯb”5÷ %‰—QÖvc“µ|©AZ>Oñ¾ÃÖ¸+èæO2ªÀ’š¦­þ„ÊŒ‰ð ˜©Š W}¾zü§sUe†ƒQGׯ>,ZðS·ážƒ‰˜Þ\`‰ñ@>ˉ·QüÕœ¸þRNü–Y•Ñ„N–;ácw|F¹k‰¦hu`„ &VžLñ Ù®ºÓ*¬ßâ[x ÚÜî³5F86vÐå²dÏK->´…”ßhL }˜—4#ÎË7_!…ÚÌ8µ¥|„W43¼ƒNl)-B¯Üí; öPè쑲,¯š‰úÃí‹ÈØ"¤/­!KQ#ñ3ƒ¡Ì®þå1´8ôy | [v^º¶,3Âw¡_Φ®s¾Öü>ó;ÌXάƑI¬þ‚?ÓÊ™îÎ.rzQ­m=˜¬è(w<>œö[gÚȦf„AÍ»ÑzM¡Ùh ]‘ÝÏ©õœ=ÏÜh}Óõ½.o†ô -‡lÆÃ~ ÁNwì›ó9ìpÚ07mKS¬ÑîE4 }_5^xM©Oû7>¬¡[®uöðÌÐrHÀC;8zä^ ­o™Ö–¶ž àã”.Ä™_ö«ÒˆŠ;O Æ ÙýrTŒ¾8ÝŒŸŸùx7‹“VûÃukÜ1·aC§¸N>Eá;‰QÞî·/Sؤ§¬õÈûƒ&Ýnæúú²_\ûк»Î°pºè.GI%B fÃ÷‡\´ƒ]]tȳ˜ï¶Ú=¦œiëCw¨€é™vðå$ý[W&·oMñJYß»¿L©Pf =º©½?펻úvrö.âÈÆ£¾•7¾s5†™ò[À’HËØ^»…ž0ÑMéŠm¾Šýc—îÚîž±oíäjê·’N't;7Hé Œ»L²¯wôi¦ã¬Sât§TßhZé.y9Å­JeÛ• J®>…2ÝFëlƒðG.8UouŸàµô7t‘7ÝDÐA'akx“ÁH: 1r²~LÁ‹ó&º@í©¸‚u×7³Ò“©Áhž*¬¹8AfÔ´É··_¨ûíG;ãô£I]¬çÄ\žpšŽ'Ò½í®ýÌŠœêêÒ™Œ®*œ¼6¡;¥Lëq”$QB×Ò'5Dƒt¢Ëvht90.ÁJcÐm!0 ÃùT ‹kÌ^|Õ†u>9½êJ—b!µ—W ×Á)R¾êwh"pq´{i÷cøvúŒ|wìïH\!ÈJI¸/> F ÿ&—CnO ¦yîkjC³KÛN—UäÉã1§}¶¤`g€¹½©óVBã;ŒÝWRhbTðdªÅË®pZJZK¸Öܰ€ö·ƒÓÖâ…{Ò=K‡Äî­«íÝÊ4Óš±àÞ“LœŠs âvº˜‹Q´Ù(­eZ*˜ã[“¹®ínõûtJ9²ý=K<%—XPåNϵiÍeX.k5ÌZ€ÓkÔÀ°3JâB JÃÓ%ÁRkšÅõˆáúƒìþ’N#…%¼'…e2jOg<·V?áûʺUæ†Ù$a˜'*àÕ7Ò‡F \÷Ò6¯r¥¹uØÉèãLTa¨ r L«®ÄZP¹)˜X°Ååx=ârD˜¨P<ÆE VÈÉÃDðÝá|Ó˜¹¸V«ƒ}ˆTfÔ–â“Þ¤Eà Ñ‘_ +Œ(šÓÀªÞ¬†(s{:DB¡N4éÂô¥lp‚$¼(s» PÂ;»Xã4òÙ•Ž[†Î®ðÞIçH Ê‹s³P†ø8u¶A1·xèÃQ™rrOé)J4¥Ùžò`…ŒDPPœd6GaÃK_}ÓúÏq>QwŸXa=0¿c¼Ž‡5Þk´0én³ÍK8,T€X#û4}Áèÿ´¾†.®‡°Ýv‘̈¦0µ!!Û½8+°Q¨fÿGn‘C‚@ ´Õ¤$ˆÁÓü-ApåagÒÀ² ¨ 8ÁrˆÐÌó­0[ºžŽ†¥që¢7mEžõ¡sœšwº—Ô¼Ë@.nX›âIˆPSѽ†•,äT;nÚéÞòkù­èn']+ŠÌHí«1JY´’þ1ñ_ŽIU÷Séòª –èÝ“¿’(6Wn[WŠC^äýÁà­ä«(A67$&b#<„g~HLk€-ëvt„ŒŒ‰Q12bTlŒx‹à4:D)R9ÿ›D)l`#@ódGçqtžÀ µ£ÿCsŒcÜR%‹ˆ´«àÆ …vº£1’½”ªÈ¤SPr&½hôqœvºQnœƒ.7ÄPœ_X)ëO¦ô%/4xÇh6Ó´/nZ㦃ItõÚ=xqpš?vo‘ÑgÌV)2iÛÑOœ½*‚ˆÍw˜öýÉGÃR”NZƒ‹(½F“2»ººˆx—úÑ Ž‰²`ùóß½lÃkøJ\ÆShlp™àic¼²Œø—ß‚6£‡—ÚÔF;„²¸M=*jQ²Øáú¡ )\­&HÊŠòÆç‚ •¬„DÚ¹oÅímQ@?PXÔU8RÐËÁH(]”1j%6û8&FÓea|£j"„5žq/#DSl6ó˜ÿ$´bUdE9$2‰HÁ?PIþ:ç_çr5¦8²« Û:Ëxä%>n+„~D5ž± N"h¤?’ÌWî+BÒ`Å&rÁÂ_jò º'/!sJ¢‡! »2€-Šý86åOÀ]Ò\yúžu·\Nœ[N<[^üÚíy?"HÝö£“ŸZïÙ× íèol¡•ö0ÖÎÐÚoë>éDÙ7begÔÆØ›ÎHicPL;ý:e!Δ‚ªlN$ó 0Ⱥ²Ši o{ÜmIé£E7<öµ¡I5‡  †nŒËš‚¥¯djxŽr°s”T´½ ­î^D–(ébÌ1¾ÆÛ™cs…&Ê8H¿Wè®mƒè2MC¶øÉŠºÎ`÷•f+ jp“²†‡ôT5â7¿BʉªVÞÜœ»ëñ§¦/ºK®¢0džV^J ÖÛ³°Ù05ܵ1¬\ýD^ËàWöðµÖű «#Fr\)'xøCƒ‰þñi9ŽÕûؼ^Zq-ÌçÑ ¥E â¡ð»ª×6L±µ¶iÊàCÜAÕëŽu™¶ë³fNJG%- ˜±úO¥Ži:”*®›á§Ì‚Ýn˜»Ï£Ê[Ð=9°x;ÀÉä°ÚØøÁá#X âqlW°ß&øûz‡Bz<$5´ÒL«)!‹Œ3; õ„wíw|Ó²þI·$[¿iã·°ü`7çÍh[þvð+õj—Rÿõžáæ(ç=ãM-ãi¥m×Þ9`Ó;VüûªNÛŒè˜Ç#€Ñ]é¤p˜ë®z°+ÓF–É—_†Ä< öL Üaÿ& Äy¸TL‰úÝ“+·÷V ÑðrR?Ù ªòU E¤¢'È©2ÝÏãExÔMÙE`×Ã3jÊÜFžiOÖ¡—HÚçRÔëOÕ6 l»j¤ÞË;êð²Ì'騍¥Ã=äv ì TXí$xDËÀâÈ”)ó:e>±›Ì…èhµ‹±-‚¼„[ð;æ !@6Ch(‘nÒáØ2GËâ‘d³FvèïÇøçbÜýh"§Wcr‡É+¬qßïɲ…FŽá ÛÜÞl´·Gâ#…!Êá,ŠƒHî8ÕÕÙçAºP­ÃGȦÆí=T‹$vÊ7ôC•¬H}ó±D¸¢¶VY¼‹M1Çâõ<° Ù:’¯lÍÉdÇ™ïhOõq^Ä· àº=ZÛ#Ù÷gD;ã“±Íø¯ÓšçN:sÑp˜.i2¾“âtáÛl<-Ï<)ã…á^ÏœáÞ¥Ú¿LI¬±çíE T.S»Ì‘P! Ÿ•°UÖbÙ“Â3FÿçEYIû1Ñ4ßàeëfü,ž÷Êþ[Vøï}Ôj%‹DRuolµ¤5&É~L’” §v~¢-¡y Ê dÂc$D¥J½BíÉJê —KDŽì œuj€ô·;ØñDvÛÙSX`œɴè¿ÐR(Jq¥Ø{‘^øb™ÈîÈ"+‰€&,¹­’‘(zH½IŒ‘ zøÊ<¤gÎ2X¢»æ~\5ÿN\«Ö§=êxÏR ÜóÐ5wŠÛ’›—ö¡Ú~U‚âÖ R¤ïQ ÜA‡Ó*4U–ÀpÆ1ÇË©dÞQ ‘ÖéXÚIAY½Hµšc[–¬@s°7V+Qíxú[(P¡Á%†Zœ?µ/Y@F­zãcñ ŽcÂÚ+bÒ£ ØÔÚ]oÏÓî Ó ÏySzÙÃ,]*‘Œùî¡ °×êØÆƒS7æC:ì8âxR¬—Žà­´EÏLdïŸâ5q—„œO©÷ýèÏЖ‰ïyEê·K°O‘HE/†Yô›Ëø&ÎÄM2{K×L` Ó"@·&BãÆî)b¥7‰P’ˆHÀ¡ÛÁU”ºî,—î¤ÝëLdR[Y¹Øº%ç ÈIÊ qÝx*ÈŠå2ûU¡=ÞÅ'ô£½l0¢C@k Ò\tV1û‚D¯Nº EmÞJá 5!@=‘\E÷æf§Ä'`áÍÈpÍÐ=áGqŽ;\—º‰s¼òü<Þ7ž| %1ÐÉ/ûØ+Tç~Ï­[}ì®g€ýÏ´O ëun1¥F™D:Âtéfë LËúTœÈÏÆ1¥Ã%ÖÏ^Ä¢ujãµÂªÈ—Wí­VƒI‘qÕXqPr ZAƒÌåN:§y=lí½)òœì'E&¦UÝ–›”$€4ÝHÕn¤uq1ý’r” .‡7§‰µEõÉ1ƒá<¬òCK !;iˆÐì³IHZؤ“ ÏI§mã[y'”Gl"›<Ã0o¼ ©‘YžIÓî‚8©¼ÑImKQq2FJF# $™hý'EÌÅxެD&àD^kÃDW¸ †Å )oÚašï«Ô‘‡]d`ÑDˆ4sq’¼Xì(?+_Ê9'f.®ß‹rq©­Ñ±)Øê½Ò‡HY(IåÂ~™È· ½M¬·È•Ȩ:¢Äµn9{ÒÌ2‹²úq`¥«¢N‹ÅlñDO2Å ì-Žpn÷fÆíµë¼ðÄÿ±ƒO4Z‘G2 PÀœëcg;¢CZ„wE‡4 oA‡]à.0Œ‘±€ÂÆç¼—I D¡À‚—)ÉöH «”« æž²äº&ÚêI ¨à!&Ý*ÒV ¬&íŠj‡óC~&)úÀÐ9Öcà?Y˜Zxö@¡ƒ¾ýZ ‰€Fª$°q—#ÃOð| ÈOdÉô«°äBQd>Šò4 ù6è¢q_g|5½Ù‰oéz[¿~§.Zß@éŽ 2ÆhÊå©°Ë&mz{FGWÆ#ï(ÃO@‘»p_»:º¾ Ï’1£ÊÇJÇtsL…Àë;Ýçž#.®žjÇÍ“qêi[ñoý01•LÁš¢ñ˜Z/šz‡¸.SdA‡ßäy7³„³'ß0ã4²N˜q$ y³O»™6œo|,ŽN¶y§Ùd Í9罣ȔM;/u/_^^_ªDJÃÑ”íÐò.Q:g ʤ5bÌ<…0ËðåœE!Л}.ÓH,ÝñÿTXèvutô¤\õ[ãtz£¾L@±M$)€tìÒ Ç2U¡C}’Røï¾G„,›ÕêÛÔ;¾ÚEÊêÖ|FEâúæ„´\5Xß÷ÈÇÒµðêeâ4°˜p,!Ž»ó->ùbB&ÈI“/61­ÐQ!ÜX|Ëê âÀó~€èÃk·¯Ò½ÁD [úÈÃÑÈ-»Ç8(tdªŠIúÑhôG#Ðið:½v£6&Ùû7J‘‹ËomìÁRˆËì­ÎØê(DÆÏ— ÈÉ”'ãÂb ßã(Kæ-¾Xñ¢|yªr€!'Š_–(/#o~$èÀÝ”‚E‚öä<‚rðSù‹ñSõ˜–ä.Ò±Á÷‘Û Û3b f† Uíâ¡=dÌSï›<µ|ªedúT›å³oo=ÎQPÉÂËwÅØpñÖlèP×é@àãdÿ@®Á\³Cy }>ëã(ËÒÔaÚ  ±éø¦,F(0_§ÒÞbñ£q÷¢[#¹ÀÓ£lŠ1M°®,O”•Q%ƒGWXÏÖŒ&“”Nݦ§Ìâ, FÃ’ÍÆñô™@‘É¿5Ç’Ó/ÍÝËr)ce‚÷sRüyg@YLá;R:Ôâ°SL{{ZŠ»±‡lÕrÙoïý@»¼´;®/N Àã"έe/(£¸Z2 !0Œ>ÏxCÁl#7óï"ŸætOÞk0œÿ¾Y-„…Z°6¶~9¶Lhå¯ Ç’]Lgr)©TL‚*„ÓJ $‘MÆW«çå0âŽö.<ºcÚˆ•·eSÚ= TœŸ*©÷ŠüO7…scJÿôVÙŸâ—ÍP¿Eæ'lJµs?½,IÞ æ}"”N¶g~нÔOLÇ^Š¡Pö+ ¸–›5¯­…䎶à›Ô h|îÇ&Ò¨P®A;ó9 îç%ǵò$É в¡ÚaùÿÄõS÷\Ø¥þ©–†Su€›e[Ê@çÖl€ãgký«³µ"ÒE–æ¶çkm$¯MØÚØ!Mê–Œ­j~ÊÖØJÙZÍ…ÃNÙ ­å®6!·=ÚáZƒA—ÎáóOªý‚´Ð‘‡~|l'ª¦„Yø9Éâ€ÏRg#:ïKjõ‹ÅwŒÓŽÅ¶SÞÀ‚ªŠ+7ì 3‡ã]4ÇP¬»x·ÃldH(3$¤ÈŒ® ~ªÚì:à•ç ïÙ—‰Íÿpp‡ÖàÛö¢°UðÎ9£Œ'Gr´»C9κq1ª0Îõ( ߥœ]}»ø”“ŠÇx“졉?'ºÁDN¾Äœ”d£)³¦vŒSRݮٌߚÀ–ÞÕQéêZŠÚŸ}²N¦Áî'LýäˆÕľ›Ò¨Ójƒd‹Òý†*÷_c’Püw1žU©Û½nûÃn:µêó…µ°>»i_g¿Í•z±Z²zá;‘>3º„å1I4;£krÖ›dšÿ…RI͵2Yÿ}Öyë"`fÕÞø $ÉšV·%¶MªÕŒlI¡¶ãßÕATïZþÄ6ÞF Iªq.4¾HeÏ·Cž„5fÀúo§Œ$Õfòã,Üo•ƒZ ("˜„V8šˆEÛwô…f!®*’T³ªHéÏ’K®FbçjecÔµ—éŠA4«©ïÅR0 ZZL)OU+/b?ÕØÇ©“îZ¸L×™R»úÜ=Õ8ke‘¨>T£ “æÍ¦Ù°óZÃwTìÈùªùWŒ:ëû5ÐÝŽ^¾»¿%Ϭ&Gј¡£·a¹Ð’f¹Ç?w9õ»|gÉ"”õ› nW‚E!/÷›eñÖÊÈŸu3NÀ¿-:¹E‘.²ÄºÕ¢SM^kÑÁšÛWÆv‹NÕ²èü´A§šoБ=fì1Õ€6›Íþîaø“½ÂÎ2òVsïàØÅÚbå »N’ñ³ŒwËt.Bæñp}ðoâ­X\ÓiW(]Õ(‡žSikHŠRÈö+aUÒÊ⻆ªè‰±¾J0‘_‚i÷ûƶ«ÀÛŠ“òXeLY›øoéíÀ&ë,Ÿ­&ÇoYöç¥ \õ§)f€ôùeø:¥“úÔ‰òÜâ™mÊߌ¤ÓÑÕÄN¯ Ï.øä®fÌ)‰9+(º2ËÚ\O§Â/0ÁdßÊ ªíÊë\©Jû9Ty^Ôµ±o‹·=$<Ïà¸g··OëÙíyò/ø·7â¯ß»ê¯ðFÔ_í¨ÿœ7¢Þxý«¾Å!{$ã•ßz=êøÏ”—ÁWÕTuþkœ 9››nZ~QîK±_…:{õf§ÔùÓ˃p‰2=4qª<´!ÙAñÃK{5rXT§ã-5Jfws‹õu™gΪËc°‰“éGhìÑŽp1™|bŠ¡÷Càý\×hçV4|þ€¯]Ɔì¨h•UÌCöÐ(¶_ÔÒ„p–ªZ°¨ùN—DëtÜž<Ôš'W:¿(DÂnv£Á½PSÎAs3íu‘ŽhŠ€ œ\GåF§ÔPÕú¦Ò¯©ÚbåS»ò~\nšª6A8ŠÔE…6 &q«MÙè˜[5z«iÿN·O=p[Ø CZ”ó éV-Æ9Ô¢®ÈêÀ³qwÌRyŒ$«~Ã/“¸rÜ;ƃ‘²¹=‹|K4©F»Íïrß«vN^2nç§(9¶ t/HÃaiž8;ž‡Ž¦ã°D‘³m\6›”Ræ›'« øôÃD¥.ÁãÕ°|(W?®U²{(ß›9!ËnìÒê¨göD™~‰êf£‰Íacö¨”L$’éà…ÊæÓãðh¬Étcrí»%u²-ÒÒd&a_™%NxæbV»Å3 qÅ´BR#UÀ0FrU¦ƒéh J7u}‰³ÖÐ ´Rä—’7c®Ö*ßÈÀ¦ÈlØ¿oÀ¿W@^)àÇÉ‹ùñŽÉìsìóç‹Yˆ§6vn‹Ȥl°²GØ©#">cä,vNBK)$dòMõÚËû`kr´¾>¥`H›Cu>ËdôtÛ6nÞrò;;JáFÜ«+ÝB!Ÿ^°§œ1:ßfŸ‘Ô3áS£ê aFÅBB‹+2_Dš@‹W2ßBl˰“¨Û†t N’CNi¨ÕÊ®Ì-Á“ξ&åU»ÇÛ¤¾ÚigQ§€®ÅùÕ€B£9+§š4¸æÐnV£çOPõ­o•ºœÃ¿ñ ¿ÔF9?šr;+R±âü¤rÿ¶ìÏ Õ“ŒB‘OÞÃíaO |¢ü„ž3µê©ÔTÿÙ/Hºdù¯<-£Úx30. ê£àUc™-ãës¦²0v§(Ï1vùf.ÇÚ`ÙæŸæwwðl¾^¯Öbþ¸Y<Ì6ógŒ :@ú6òÊ8O(b¨oj&ƒøønˆ"?.§?¸¨±Ã¼f"}ÏBÇÔtØ@Ÿê(”\ÂUq$&ùoÉxüLj§ã^ªXBÝ¿Ô+îY?¿§yhQgNêº ï;A¼Új¡Õ¦eœJi™nrR± “•ÿ:vnjïÑtZÍžñ³¼„þ g‘L¸†ÔHˆGA$ŽàŸDšŒ«‘N´mêöäX'„þ>º¬Me%-Aê;‡åýøeí˜\àÑÉÝþ ŸjdAïR#þ1¡´¨Ì=ò§L¼!S"™f¸i@‡¨1ÑÅgؤäBzÙqÊ"“üÚ:Eá_ hÊ&U.pÙà›¥h©â¢+wökâa:è©{˜„âÁ‰ìT™CÿÌ0]‚\̯°c”ÊËKŒØªÎë-Ø[ZØÑ|í´ð*Ûõ–ÒíA[x©â»š¦+ÑœoË®ÕHD²MÙÒ’ìõ€!®1›ª¾Ú– µÝ1›®­8†@§–éšê>ÓsÞ^„û ƃ¡~°/ÌvÔ¡Ä ®«¶V‰=›¶]ÿÕVmŒ¡W»VÆÀíð'›I,תùõZ ö²¶ïÇE·(/öBÛÅ ŸP¹¸W€µ¼ÏÈ"Y?;–‰1;dx™ee=öüÒþaëLvýpÚBéË®ùù ðÃשfªL­ /\ ljç-ªaþŠô}KÔ’}+€<}ì·Â‚!ÑnYáÛùØê‰n‹ŒÏè¥ÝÖAQÙÃP`g ׌cŠf°Ya@¦•¥ªv)ç`+Ó½ºÀ‹Â'’gp}œq.¦ÖøÉ %£FLv£éh$ú-Ì2å\æå&Päqlñk6mëµ# Ûk £Ð±csÔXgMbO·Je§Ò,A9ôòûîIaÇ输(~FÞ„ÂtD™!Ï̈ ΄ӭ1ÅÖ9½Å»;ÌEדizzº…¼ƒ/˜Å‰BüKôÞ";t #¡iÇð{Ëâ(¿Yev5üÁE„Á©ÀÍ O »Ú)^P\h•U¥¿™+‰w¸õº uqSo‡Û­Õ-ÔÚÏÞ÷<çÖÛrs'-F‡¡.CÁ¶}Œ¦”ûå$èG¹ ©Ï¿ºE;•÷Oýc_„ ÕRqC2¹#—^ Nd5'jkt»eO¦oeGìË#´RXHËݲ¼%XûoA\ÿ¦P¤å™mÛî%¹(dÈko³÷ÝÊŽ—q®vém<Жsj Ê ìÁš ~Jã^ñ ñ„'(Yå’Ó–+óSo!ÃÄô„J;ª>kºO`"¥*+PrŸû-42WZ25ÁÖ/U“¥N2êÁr"¸mªãƒ« ®ÓXLÖ 7 Àq¬¿[åýÅû"øIjÙÑ©Öõ Mޱ€†ãÃl‡±?G¨öfíÁ˜r±Ååë²×Lrn³zgˆ6ºq¹@›Â\ƒ­2â¤PN©Ö æêòùV´ AR3XŽ“ŒÏAfCOZÕà²fQ›™¼í=À§­<Ä ‰{9Þºé11=6U3'a"rF¨y лm¹Jƒ«L;×z¹óŸi—ꇧRÈÙlJiéâÐäpè‡æ3“(„½æñ{Íú.‰bû¢O3ò…œÿ¦YL…B#Ñ3R«[tø\Oq|dJ—âª]·^;܉pÅÁ³ÐÖ$´'­ZòD3õ©T¡ZÔVSØÌá8ºóºì ­›ÅRµÆ7wj€wÛ}5¸¯Dr§Îs´ÍµšÂŒ5š?šÂ›Û(>ÓSj%—È…¤óC¹CÙTnya‡É€éx†ÌwhbÏÚpê•Ú62’îmºGbJjJ‹ûqÕ"‘ÒÔ›äÀìõpÔ¬Ù´µµ!Éö“׌¦&GÃë¢P3Ä™4_°ŠáÓH¬§a“vRMvm{ކ¹.an˵\Ñk¹zô2ÔÛ@ªÛúr‰qÿ¸¶ ‰î½T«[ÍÕwž¶zlrrr`}=¿¨…ÖÔÓp¡áuë›Ðã€;?ÿf ŒÌ R§+lýŸ Ÿÿ-뢦{¥8{Íyäz®¸øßß0tÔ´œ»ËØW²@ëô û>M|ËO6±UæÀCV‰U².èEÑdt5nw£)Ú)¦Z›ßAÕq›©¼·Õ&n/„J¾T§¨‡‚©ÉG{,°T¬›ëŸôAI×ê>Þ|![(°fHj—R•~,ú ^ÙÆÆþéz'-œðóJ=\"š(JäÊ_¯ÐÍM›Ð$©víS1²'"bREb/'”-æžç DHò ! $_L@E>=ú;m\ÓçFg)b€û0 É\6á!-tDô¥Â0|ÈÄI“ÇwÖêоP€1$h×õ9rï“I“ãÔ}"›‰£´xl¾ËŽºS&±Ê$Tƹc@Óá¤;žÊ[ñFxÿ:EQËïúÆgÛ.¨;M ñkT™"øðØ»fNE óÌNG›!ñ)›*ÜÙ.no<1Ç®mY;ò-8)Š"M¼½ è}Oš‹Õ’Ììåµ’µìŽ{ÇV;½`nd°c]ÕC¼šûtyÅbÎYtþ2§WCÎ÷ßé·í^A´x­JñÝÞ»=¼Tô[˜¿böôÇâ~1[ÿëÕÓf±œ‹ÂoóõãbµD%²%±<|®–‹ßÊxºb:_.çóydžµg÷‹Ï«õr1'óõ·ùýüG$†­3ÑßÜ•#ÙB{õ´ž-7"]>n›§ 4ÐZYAkb8Û@³{ÑŸ}ŠÄly'Æ‹Û9µÀl~È&F·›Õ§ùZTãHÄGG F0C¾ÝÏÖØäÓÃ|¹y4ìhuÜj#§×†®ËÍ\<­¿¯çøõ˜?üœ1(îæ›ùú0ô(îVOŸîçâûz~» T=Ìn¿"î¾ÏÖ³,÷(ÛÔÐX­ªvyF`Z,¿?mŠ̽XBFŸÉwèåóºÝ|‹ßf÷Os±Y‰Os±žožÖK<ûòCùŸvÅcñ¾û^¬Öâýü}$ô8þóXÌ¿?z'\ðQ¸?À½¢'\ô“׿§£Î*yÁ%¿g{ß ”ré¥Wzãs±µWl½¼ó ¸àƒß{vDW\òÉo2[²Ï%ï3mÎþðJޏä*Ó&–´Ëþþu¾ž»AÔÈz~ëâ·¹!/Epviš#(ý8û %ËÅÃÓC$Ÿn¿ÁÌ6">àw+ ¡åj#V°¦>߯~·ÁùÀ.é/,h$5Ù«] Àb¡)ÜÈ¡/Ÿp‰B#,Rw‹/ XŽ·ºÜ,gvU˜<ªŠ×>–È‹–w‹å±Ât1Twvw·@&aüXASëß.8µØŒD‚˜ÿñ}µ„Åk˜p¶/ëÙÝÓì¾( õ,Ö²º~ ð®ç_W÷w¢D˜ÙÛ+`%ØAng`=à>_æ›L·!|¯eõÎê íŽÜ6 Z\t3ã>~‹Í4/4ò¥Co/Á+¶º‘­­\§tŽZ ‰JEÆqi,)НU}÷WÀ±%Û¶ºWœõ‘·¦÷Çz¿S¢­õ9éN[–­_ðßtÿNóGtÒšÀºTºK¥»“ˆŠDèèn÷èÏ/ø/¼å†þÈ~Æx-Ù䔚 £1‘˜ÛýŒ;ÜéÓòéxûˆûÀ¯ÚåD÷üzK/“§Or?·ú 4Dœ) ´4Á„ΛÙfŽÛ—i_{³Cø“ˆ`ìÂCÂã÷b7ò‡M¤Ä¤ˆ‘ÔWÌÐñ4žWwÊTŸl«NÐ>ÓÀɶ¬ùÏ«±­>ñ†íõ‡ÛêOŸ©<ÞVÙl@yÕ[qÿ<î®¶vÿ|ýþöþõúÈ«?ÚÞ¿©ïR¡”p¹¨«ïªÕÖ)Õ5Q‡…oôÑÍZV(ê0VìXmJi·ÛÿïÔ‡òa#;X¶pó¬ÖÛZ³¥SXвøcZ¢ ëÎÏŒ:le+#X˜5Ò¦ål= Žl}*K=ׂ¥ßp¯yŽl7P§]‰þ°Ž›ª6 i­¾Ã­Ag¼K¦(@¿´›¸ýºúþÝ-É ͨQeËâ`ý:¡£ Îa ÕsA’Ñ—§ÅÙpd¿Cihã÷¯ R.­é„-oµ~p¬ ˆ‘Ùz±ù 󼸕ÈàU÷Rdh,̾Ÿ£²Àö;P³Ò±a»‘÷T .¡¨eïArüq¯&ûôiM-wæJk”MV8ኺz(±éÆz,ìã•  áÁìþvuÿ ¨w±¼]ÁòXƒtù*ùÔIö‡¢g@É÷ â-i}fëÕo€VX·scPÍ Ê¢U…ø¨™EëþËŠðJxXÏ›£`zÙ ÖVŸ "@™¡ôQú¾Z«±æØÏÃÃcY­…V{,¤‰£ÚQ騮–| œ×eĆVLÊâ„«Å€´]ÐB¦ˆ¬D჊8ÐPÙ´‚¸‰¤Ù(%ÍfùÏÑ_®¥J^º][í3·ëoQQáõI$@=X¿œŽ#RÀàÙz˜†·mŠf bÓú~ß±ú& ¢FŒ:±ù~HYþPô¤±ÃV%ä¸ô8al´XXOžÝŠ{¢(2ä> ¤fÞùLC²>׆ºe»—éWà;_¾ïÖ€%i.ôû\`þ#ìⳜz5}Å|ùø´žgVäýü7B$8$`òõŽö¯óû;d³ëù—Å#nÙ‘»÷`,7ûüy~»asôêûfñ°ø»1„ó§MÚt1ã µ½½ʵv‘Wk#³ø‘„6ãÑ[þ‡O‹ 0ePር, é a”Îäû”m¼÷€Œ©÷]ÃÝ©5¼´µÁño\©Øµ±û½ƒ„íDÞ·õ‹l}C^ßÑ rŒKa€_¯¶NÒ¡`a @ü?A¨ìtpœ;Ðî¼xtÚ5-¹ßZR® N`î ò×Ùýg˜^2U÷3ä€÷³[­"n~_e×f‹à"R.+MéÑ ƒ»ŒÄ¢ ]}=r£HD¦!ØW‘i+­îîP%Cû#(B Ö°×ßójŠž-¿X{80°O ¸U¸MYkH`mRÆÙyvfqšd pÓöúd…íÊÓXͰšò)%—uäS’нUüyRM¢Ò#ÛˆH7j¨îivˆÛÒ÷Ùzãh:÷«/JM%Ÿø^8Ëþëêw˜”µÀ™~$GéËîa{™G|‡òÈ¥ñèë +‚ðüøÌfùÙ–‹HMl•’‹[¢ üuæ¦?Nv¹W /ÕW /üãG@Z{‡o&ØTwÜvÝò§Ü)làxò3[+“¤Ë4¬JåÛ,ã¿Úf™ø6Kr YÞ4ËiöoK¦ÐÓœ¯u3:m¯ïý›°m|À/±{&;Ø=¥™‹t+6–ñø°•{дþmõüÿ‹ÕÝy2|jó±Ýa$þóT0ÞYp?)Qo°Ýö§e| 0(`@R§Ô\îØ  €pŠúç(ãÑ6rF‰ø쌕%`øÒßb‰èÑžšH{ì6”·šFF*K$ñÁ>wRuÐ|âZ$ )v;°p h<÷ÝO³?‚xÒþb9^«Ï!GEùóÌÐHö`%ÅŠŠvÁÝG'ºøy‰© ‘¶ 2ŒÑ±íî3½™aÒp”¶äH®Ëâà µ3ÉåÌãíb ÝªìŸÅbŒòü{Ýw5Ÿ i÷:»à8cTå½­Ï›[º%àf(ÛR#ùÛ 6C÷ZýSl·Å>ï‹}CÈ:=¦ Ÿüþ&é/Óž‰‘Â;§#¼¶J–™ÝUáñ¯²d¿.h ¦ˆþÖäßz åt¹5âqqÌê8NR+οu¨%ĺò†jïå6wETAê°f2Ú俹^Hàð‘ܲÿ*Û½ M4O”5_?ºDÛaÀ¼ß×á\´› `Ûç MÙù3¿­©æÈô,׿ØÜ/­ý†¹_Æpœ:ï–ìc”‘¨þz.LL]\Ì‚ÈÁ ”õ‘@z;`ø9#*Ž'(”úN[ŸÃÜZ¾]êqõ¨_/n¿‘éT&-ÂÂv‹Ø¡ÉZ(ñj½T‰ $HiÂJ „lV›N­È<⟶IÔh*kÛl«ZeÏ¥ªE·cUFrâL['n_Á60I“kBÇåx‚²gߊaû?â1þl/ƒá9£@/Ñ„=p¦IAwˆÐú-îæ¢qÑ–)§™€ùCF7æš0W{¼þ 8ƒ{{uQÜã¾ööcåÌ© èn{EN2EüF|ïÅó¼Ìƒä ²•3­¡EÒv9,²Ý=¬|‡Có²À246ßÐjÜÇ…2:Û ä¯^v/¥øRµX´b>ÌçßA#ýmÁzcKò±YÜoSFH%tܨ»g£N`´·³%®iXÄ(¬3[Ï[¬žEËYÑc¼Ë«×ã2V°ªyÈ„ù,¦Í¬òz5S„(ËY¼‰ÑDäùÆZVU’и=?!"K1)JaŽý.ùEYt,íPV¶Úz¶œl2TLû¶“À¡K:Ú32k¥ë7êE0Á¨Ý} BnöÇJfv4phƒ~_=–˜ÔIF“jl¢Ë•Dhhð¿<%^ÌË_ʰß~t#Â…¯)‡TJ8éJ†À%•wßD3èeÒ‹Eä]‡Eý€¸¸¼›­ï@1½¿G;ûc`¤y ¡Jø·Ämÿ þm¹úÝœÎËä`4xÇ‹g\vg K,ŠÜ˜þv 7Ú9‡j*‡z*ãg¦/÷ÐP}Lß‚Û7˜â,©Ô_;?yÐý´ »4U}gw^¡–`p´»­Èà4šºã|îºüžAô?õŒz‚Y.TÚ’…qwó S ¡³†Âq´N¶ø,>¶÷ ¿€èÏýænRÜr€(´–šEÈõ8‚܈Äé‹Á¤^ëšå~||zà¨X;¢Do&¹ÈÑLfT4­ißöìˆ8V,È]PÉjtxJ­8Þwr°Ãö¥Ö™ÜÅR뮬˕aV6ë' ¤šéïýŠ–ŽO+ nDñò‹I uþK¹t#ñyöt¿ùa–Úö©6ÆHÛ'uåÞòèOy4.+'`(VÔz'ŸnoˆÐ¶Š‚&yÒà1Û:Å5êyäqçz¦!ª?{ šŸÉ}OÚ¤€Pï(TÈVá°,©ë3óÒ°ŒvF²éz¤cÕRr"ýΊ˜žÙÂÒéb9»¿ÿŒ|/-aU$±G6e¶l´~ÉQëë®ZÏÖ)6Š*‹•sFîg¿¬¯ô­“PŒ=/}ûÈ Kë[§hdÊpOã]ȼ`9qlÔñ^ H‡ge2w³¹œzG²ý4ç€q4ügé½k”{¯Á|È9ŒÄ÷:ý ºÒç †9,ѷȇ¥7n7÷«Õ·ÇLË3 ìïldƒ-}…·óŠã((âþTr½.ºõ7Ô—¸X¥Íãn.ÊášÏtÉnÒÆ‚íéãܰȾ¿ÿÁÀÎÿø~¿¸]lœ±ñØnùäÏvË?“[à cü¿ÍŸã/x‰¯¼J:7FÐ,ï8† ÂöÀáÓ'Bž÷–ÊïëÅŠãwV’="Á)/ùLU`±èCS)P&Œ÷iÆvxvlÃ%¶ó°øòu#è>íäJâš3mÝÁ-Oô Lfç|wÒËqe'’[êYKºœc€c§ÐggÙ¥Wý«#bj¶A‰Óùœùÿ /Ü"SlsæÞ^²:k¸8g õ­CèBqM1÷Ýa9d" ºžñä…a‘ XpÝØó ù¥QàÔú@ª¬‚hD‚a“ kÅs‚JfK²áSŠ2ùÍïä0¹ÕÝWö#z"L(¢M Çø´kÜ=ìa7-¿4’çÍùEŽ;@xéöÍèú$‰zLaÇôœ&ŽÓøï~†5j‚’¿]¡kôµ¸{ŽÆ®+¡ŸØ¸­¬Ð ŸvL†uù#±~tì7~Ã&|í=i«€\é;µjýæÑ+ HtKwTi.`;[²·uؽ™ˆïÄÒqËX"¡ÝŸ¯$K´)µÉ¼ ¯õÛE¨œ £ ©Ú³ï…K‚3pS½›ÇBU臂`Ê3ËŸ$ÏN»šl d‚É-ràLBpV·ÀI-'gÐít9ÏS三²>¢-RBí¯–ê*nöBEy²2ùïhÙà¹ÿ-E&\ö"Ÿû²,¡ØA²¨‹ÙXí÷Í£m/ยЦ,CîQfl†mVì„@EUEÒñÉ™À9ð¡Ë7Ï>=p EÐ3é`Ûìû÷õêÅ0}Ð:1ç ÿãd&ÜÎï‹û{ñyƒIô§Ô‡tR_ßIýÐiÖPˆhÇŸÿ1CÓ-«ý'ÍêÉØJÉa#©G:–ðX$‡Íø°hu?»\1 ‹Ï–JOÊ2Fc/ÒòHúÐjÅèi¡àïÛûÕ#…´âá“b„¡¶Ÿ`ÇûÁ6dºÄï&¨äŸ@—4"ë<Óœ‰?¦† ˆp2ú°«{c\·wþÙÔÊs`7ˆ~ná‘•ÊXŸïg_d8ø” 3¡$s m޼ûÒ3Á(¡ZIjZUfíu{aÌêó)í°Æ«cUÃK[ÎõŸ$Öî˜ñwÔÈòÔ3ÞY9î*‘•ÿîEùî|·ûËI:۰_.&WŽï„ï°•ðóéø6x’ÿ¨ß¹ÁHÛ°ÿG¾þí(_¿4r0ê¼LJç2§‹5•ÚIc´…^ê6¿¯˜µSÒ‰„·…OèõõÞ@¶ü †~𒳃c–Ûˆì$t#Ò‘ÚGfÁ´HMg²zÚ—cz¹IEo=ÿß§ÅÚD»Ó½–F;¡‘½‚©G=ÈÊëxµÛ1UÒš£1}rA”ºaï§ìÊY²¹êK~qŒ…%HÓ#6ˆB¿êÐíë2wÝW/Gû ÝwT¢Êéì/íÅÍúÑ— SÄPéJü²žÃôªXÈ`²êZN¥µoá^µœÏÙÈ)Û “¦3çndk†¹Ï µƒ7GÌ1èØ{.â¬2ñ$ ù¹ÆÓ¤‘Gì ª*Rr‰÷V<žnF$ÅÙö¾¯Ò Ýâ+5fÎl¼nVè3»¯J ÈÝ_lDÓ)(›xáòÐ>K Ö-7XwA;ö†¤BŒEÒ¸»ó|z —a$Ï-Ð)x¿u´ 8ò~â“F–Ÿeê@ Dëù÷õüfM‡áI'¯Õ.'6¼·=¾ ÿíõÌ8U‹B"‘2l†!.ØCC)ý&l¿.ôٙߊ³?L£6óõ×ÙwšYÓÊà x%›X2yu‘w*®,lÏîiÃC/VôRúÙÄடXø¦`>$Nœt÷t+”¼Ä3ØÄÑ šÆù|xz$c áOƒ¡_K!6©ÉI`0Rr|³ V#KƒÀ‘š#„ i† b³ †É´ñ "úŸA¼ÿáxRÕ²âu—áx†ÈiF²Ä+ƒ$+‡ã”çyÂñ®Ã`ö¥á c¨Àò3ˆÊ>¸?œ _¦aÌ·Äf·¨‚îòàY¦Ió㟇Õ‹£Ţؓ¿ˆ<ÉÁ¯†ù6pZn=ŸŸîy?7¤ xyºß¸ 6aÏb˜±G5^ëBºLÉ’5o\‹Žcvº0ø¤+aö¸móçù#u­CØÒʇ2!Ô±öËËÈ„2¢( ˜øn0ª×(ÖÇVé{l,P‘!¸Û0!_æ/À1UYŽu¶=6E8 V8‚D3ØÆz®Iªî˜¤ø[Æ:¢Ý¼*E&Ðþ—÷óNæß‘ñ¨BÆ£ÚnÆ£Ð% „m>„D}DžZÀ¸[jK½ –ÐC`{T¯ç_@g¥UÅhn1­Îó&ж<|”s³Bûdëk3kH{úŽI7à4eÎàhpï:jJažàEC”ìváj’©|âW>q*Ÿp宬÷Œ¬QT«>RG!U‚'%+²À[¡%NŽ|¼T›©#<˜òµº_¾^ÝV¾‘øåiÜ6Þ÷E£Æ»ÝÉVèN2Ðl…î$݉žX‚nËÄÆ‡ÞÄ&õÊÔVrî0ÍP’ú89 Ìý®Sž$þ2Ä»-§ö‰SÛ] a¶ÙG±yê0……5ŽÂ›¼ëÂ’×þãߟâPß׃‡ù§»Ï‹òç?© |µým6êÎßJ­Ò¬&ñÄð'®Tëµjò•¸'•ÿ•? çó„A?BüÇôëêaö˜_î¹÷þè¿ÿ"Ÿ[º#¸Ø {Ò9MÅðjУnÕÇÛ=û»Ñ…‹Ñd’žà£izzZ|w+&7“iw€—ò¡ˆÊ¸"éhÜI‡­ñè@±î¸ JhiÝË«*gGch°Ÿ»­1´gî–lußÝÂëöêû5…vÚEÐn+Mq>ÿüYËn±ŠÂ¯e$ÚÿwñÜs1»/ÏnËOß"ø% ú¾^}YÏÈÓöy=GKÐçÍï Kþ—ø±z"É$ÿÅ#ˆèŸÐ€Âh°ߊ‡Õªðˆ|„l¼œ¯•qÿlx…9¶çkË/ž>݃ˆßÍm :Ô-êAßñÙãW>hN‚‰„@œ¢‹t¿ÿRÛÖ>¡Ù‰l‘̇Yì5¿®–E€õ‡¸GöªY&´ù#7Ô&Ư«ï:c(;D>QZ‚ÏO÷´€‡®Óiot5­á ¿[ÃéÍi—=æJ¤–Èn†!ä³5j¸hcƒ€nð¢Éië$í§Ó„þ4»“ †Õä_´ÆÓ´}…7±^\¤º˜³oNÖA¨Ÿ[R(©çç¥á1Ë·wr²¦[ç'¦2B!4ƒÛÔ×Íæûüþûïå/˧òjýåàž+=,–ŸWgýß’ÕºOu¾nî©û &ûÌz~;ýÓÞïF2÷+ô!³û×Ìà¡:´\m"ñûz±ÑÉ­b‚Ú†œ@÷[Þ–#Q¡Ðlùíæ|²âÐÄéâ34z¿Z­#q²zÜ`ñhÝ•$Ž+¥¸Z‰qþ¯&-‰!·o+T‡™CaM+QoÝT¢›‹q:èFS(Ma£§QÚI;QŸô¯GãòŸU§ý”þ𿃬ÕúøF”NGýhŒÿ´èJ”â?vPòc4îN —´;qä· QzË–pÄ0u¾-cZ>/‹Ö§»§û{NÇ{^–ü,â²9æÈ|nÄê®0±·ò}*¹°Çûûù—¹|Ü_-'×M‘´þÆÏºË/÷x£Á-ÿÜ”gÜïÿ *4ñJAÅ„â£ò©*«hö ~-~'Úþ¤Î™HC<9±!yîäQR }¾¬VwØü§ÙÝÿóÿüsμ*wÜýȾò^:™ŽÆ7ÿ)ì[|ñsÞ^á^–T`ýÀNÔøO¿Ú£ñ¸Ûžv;¼™öºí’ãM®NOawêÍ}Ѱe†ÛÁÚã.ì—¸<&­v·.8hÁ~=¹¢«„»âckœ¶ÈýßïŸBít{Šoú·n'Ü‚¼‰ø¤‹7iãÝÄ9=»%¼©†¨HÔ!lüh{w;Wí¼^&é°ÝUÜòØÝi+íÔÑÃH£vû ‡]ûÕGíé4WãÇ b¾šþXÈ`çꈋkëÚp¤ÑÀ`ÌA”8o8š:• ì“. ¡ÛÇ[ÐZS¯¤_mÒ½!‰f ÅGGGÿ)å¥q·ßÅhO5öÑÇÎ8ý¨ZÀͲý¡uÖ%Bb¬úWSI?ø[‚./™¾`@Uk±„6Áò–˜eËe­þY÷dÜJÛFB“kù¬°‰n¢›÷ÅãJ$nŽ 7…¸Ýü§ZŒÊø¯CLëÁ=Z›…JŠ’#~ƒá«W]-‡˜{þŒ»f»;Ñ%b€"ãlMz‘D´{6n)¼tÆÌÄê'…ÂåN¹[žÀdã Œ/)ÐÊ ;H H@aØ‹Õ ža¦á½çe"½AkÚ›”IŽÂëèçØ•"ѨÕKz3ÂëW«k I‰Z€Úm˜ØJ1ä¾5ä­—EtšU¢3–Ð`#¼qELA€›L@=§S4×’R\?Œ¬6 –ªaªÚ0Ñéd.Àá®Æâ­ÑI±~:ÂÙ°ZÔZ„C¶<=’.Dàc ¾ à “/¦9ÒPÔŠV¹]ö<ì Zcp€tƒ¹¯¹‘oUoúýn[õ¶oi0cè å!bÄ0Ïmš0$ȳÈ 0Ž ŠÅoD¸·¹aa~÷XÃÕzóµÔƒÝöV`UÆÖ ß¿×ajkŸÚ: Ü^+w™/a´†Ã.^ p!áõ%«s£9¥i$ñ‡MÙÅKfëé·Q‚ÚÝ0É wöe.’FÓ¬¼ÒO~\–5œtéÈ0Ìη$Ü.^Kl/‰’¸â¡°,Ѧû¡‹;ËE,5¤H ÷Z³êÕr0caž€]è@àBP¥¬¦…BHÅ12E欕Ø)±aÝU50­X ¸«ÄÏT‘“ §z·ñ]7ÀhÁ#ÞXoQîlh;© Þ}¡’lñËöšóû˶~£†Òaýîð Ö÷\¶­@g‡J»Jf‹Í&Ð3iâ•=ûývn:S5=¨¿*Œ™i­)‚0$tÚMäÂkFþ±w–ë´ßÑ`< «ÈÙHLúéYo ßOº7#XŽØ©ß.S„AmäÔ¶ šo›(câL]Ü{vü+·y`ág4 ìàÔ¢ öÞ°ßo!ëœWEúœ'q”$QB’XRƒ±g€í÷G× ŠaK“.­˜—l…L_r?”{*˰¢d¼m [ý›)°*€DoYÁ»=:I#))ð!L.ÏìöoÊô¸zƒAXY@BÓ³?.¶=$ñ4€ÔÍ G×0@gYp3@Ãc¿™a·Û!„¤§7¶´CZ)°'@ ÃU5A¨Ó iYØ‹\ör@£À,p'ei~zsAÓ«%C”1ÁˆþÄcR²Ò@¦jŪŠRn°: f\?$•!EÛË‚Y½Šìð{,Å»ô¨ŽòzTs;)n “.Hé°m£™@± GåˆT2ÃN„ Â@J¨M·A"‡&[SfØ&oW½4³‚ñÓý¥ÕÆ…^ެÅ+Gß »Å æÔšL€¸'Ü úax9 ¾+2Ëmª!0³ãQ_ºZÙ€9X\¡.01Ñ:ŸŒýÁ­¢Ì à,œëv"qóÕmø±çwŒ[¶ —™]i=Å v2‘/C É©%,®7quÉÒ à|ôÀ3U„œÀ "Yå¤Ý’øuFâ % è¨dÆ„ëœhõ¤+I ©T¶$ ÝáÀÎp-â–ÒŠÔ~R)3cßÇ1Q5´›áUÀUÚ ðLN¯ú¼Í[M(TXí”§½« #'2CtAŠÑ&¨Þ@³«Âļ©{µBÉSšÏÑuj8¤b¤•¨ )¨ŠôÝ:}ÔÜ¢OÛµä*ªKÎZ“‡]æ°˜&¸‹"ÕaJµž ™Á7G{õ N±1µÔ¨.mŒÃîõW/Pdpmiœþ‚ÓÛBzÊ|L¾Bž1èC/­³ìf™)8éÂ^Ê[21ºë‡W¦#"´ÝzˆßŒå :9¦×ÊlŸô.¡wWÁwU~'öqv*T+RÔ9• ²ª›l•ÔÑú%\ ¤äLH·t_Nˆ6õ:”åÚ0ŰÞãÁÒk–ýÍo€+ƒ&: X6¹öÑ¿‡ħÝd†kÑv3ÎÊ ,L  þÑŽ ¥5¡DÍ2bψËqV3Æ©1ÐÕ¤7ºêwOM ñiK¦¥ÍhhÏfÉz¨¶w–qí-¾;ÃCÉåA ífÀ µ=N/ˆï1Y] k¸ï©®Æº+´)µP>’-à rä‡ZB¥›Ýñ#Zþ ë¯5&'µ¢”dŽþšn×+LüY([ JD@넸¹‚ódt5Ì ƒªù xȸœ¨Õ¾€(jÁÁ±¸èŽQJE¡öFí*+!jãw,·«ˆ„Ya™Ãh ó¢X¤¹Et± dpÈÑ "°¢Õn“Ú†ì’èuL¢DI cp?LJ¸a9êú`þû† ÀÀŽì:&ýaËkÐ…~&Ð˘÷Å€àtPøˆ‹r9ž xúã7èdo/)à+ZXB(„ YÿŒnwÝEi"××à*»ÿ´ú×­\Äâb4I‘Œ#-ÿÓ™úÌŒéE9 KÃî­)°Œú´Òn…[HŸ$ž¿%5 ã¥ÓÌ &‚L‹LÐÉÒˆÿÃfѳØ/!öKöå^*‘ƒ­ÚÊN [«ˆÉH+KYíÖ}nñ °´‚"fÓ§‚åLºyÕŠ ˆz$6šÆB­g8Ì&®[A¶h‰ÓÖ8,7ä²¶îé”Ù5€Oͳš¸;{¬  Ç̯‰6ŽC€›ƒ‘cXk¨ôó°3ìËú0!Ëfa«Ý#ˆÅ<ð`gc´X%9äu ^oV÷¸°ð|áZ4ûbÆÏ‚âå•tâd‘=K?Jq7”‰(0ï> ®iÁ_;fq‘ûjr¢=¶}*ù CTˆ•Ë}?-ýmK-ÒÄÔöbC,X( Á6ªn¼?å·‘JÍ©ì‚âLÈPên(6„#wafû“®³Ó!ÿdÈ"¥šd̬©hy—Y„Z#ï'J]TŠ˜¶‚H‚Ú¹»Ô8ÆÈàÔéþ"@rª®íÅj%‰ðߪ°ìF¸  òñ£×F…jVM¦ÝY›·qG ±tOSÇd¤¥8HVTÀâòîvºVáÄzË[NšYŠdkÎV­šªZĺ I¸@"!­¶„Ú¬9+Œ¬ŸL“Ì´+ “Ä0m0E‘öj(*xçª5—™MÁZ½šÙ?ÀF)c¥ÒC!'©þ‚D6Ô#AÓ¡ÂÖDíÇ$› £¥CN>S‘V¬²íÿ›¸0ðžÜêO³š#Á@FbX h±à…0ÝadB6ÞCý>lå ^H å^ÀÔGï³$öà%” É|ŒgZ¡·aFul| <¶löœ'h‚}t¬â$Fcl¡gtàRŒcì¡Z<éÑt(Áø”w cænÓK)ÁΖVþÙaôB,\¾“­Ø¶ Ë´)­ŸSŽí’ µ|FÄŒ,zV‚Ð|çF›È a›'[¤©³•Ú©m,0|Iñ»@ƒ–µ•&b[?Xý¸Z®§GÚ \Wöô8ŽËÀNºR×ø•¤iN™™ü©½Á*7•BÐ3£#"z¼¢û-¼Ïéº×¥¡™Q ;íIÚnucèå<ËÅB†.×KÀ¾“ü:ä ¡zǶ‰L;F®†<]ÀxÆ#PPæj™kº>³µDºm·êºêãQlœòí¢‹1ø‘R&e+Ñáx^`Uãû] ¯ù¶HãÄŒ…RÝÚ=\¸¹Ì0E*^hHò×°T›QÄí~Ä5O•(ÄkõP hA#R©±«Æån\±jɺ”‰^ñˆ¢ŠI ÉÆ4\»â¼Œ\2'7ÆIj®£lMŽ®ÅP ‘í€ôs$Œl¿°$ZÈ_Zí²pQ˜ JËPÃŽÑ)U_ˆèV»‡V0ÅŠ9ðÜ3 ôØ‚r»®Æþ®(Ga/€ÎŸ"©"”0¶v ‚(äȽÅ,Z v†½Hó:Ôq¨Ò&iõUW $©öÇHûž”Ï‘— ïÌ´¹g—´Å)…îtAÀÃà¾Ñ»caÝ‘eá9+,.âÓ”±‰@#XÔÝuˆ@( íŽÕ\n.f7ñy¾Ü©º×ÊX¯ÕBë¤ä{$Æ¢6qs•B/PͶ7ë-[J¼åI1´V¤ÎU÷MÞŠbÃ%ÀîŒ>Oæ«1§Šq¥R¤úšßÄά?7tOo—ÖP,n•øµ›}¶ÅŸa¦:”0h`»@7òØ­ÉP&Q‘l¦Ë?mqa&‚(Q>I×y3gÝ©½ùò ªHz âEJäw ÊŽoXBÖdÉ¡:UN?ÜCާLÞI¹“¬SR§1ò!N&£vjqKeyÕ2Œ¾ãÖÓUÄŽä]}á @dÙîDû 丽1©î/ìÞÕ¾Î;%LnRÛ΃Aé«èœ€ä¶/ ñA¡Gçò‹Ezö^ÖêtÛ¦S £Cѿ—YãpãGÞÀíê9õ­Úò¸ŸíÐᇭ=<ç"9;4„Wo92lH¿‡šáÎÙÉiK¨>y/ 0C?Ö‰ŒÆÃÊ'áXPÃi¹ó ùI_7¯¿°'ÈŒëãO9á‹­>诓+ ‘C¾*uOúèÁ–+2§æÝìï?"q÷x;»Äânö0û~Þ­66Û²ÈÙ›­û–vnbŒ9^VËl ³Óoy+P9à _ …®0n¢‹N4T'¤³(‚ÕÄ!û¼ñùÙ€Xë£)œhzo˜¡k9}[š`}ƒv}-ýHzÞà<&ߨ½|q0×hE†`Û]Øõ×ÊS±>vL‰²1І„$tÃGLÙ»µ¥K…” ÷Õ`€qT2ŠÀ hK;ƉÁí<£ÐÞ B9vRö6ìÔ */ÊÚÍÌ‘Ø>(¹~ÈõC=_ #Bq–´²Æ–F¶tÀEÐh!Îɾ­¹@H– OBʳ¥<±CÌíX^GL• ë#MiØûMá¼hj“clÁ¢ õðò&»îãm˜Ø:xg„Û¡;v„†¿li…Bò†8‡‹Ò¯ûOûqô+àFWML4)‹¶‘,ù 0Þ°KVëtbV’µÀ¶-rÑJ¦WŽDbŒ}˜ˆY›/[èåÚ^" ufvkµ¯á.¥È_–—«%‡O©*žRöàQ‚š0# :^9Ó¿ °èô£1RÄŠ®mU6v»¾;.ƒ:ˆ…GÂ`î;Ýþ´¥voØ‚cw ÑÚÈž±mõ¨?˜å3ÚŽ€òìÝ‘evá†Ê]z(èÅHÆP›Ø¯°ßÌ|dƒ2ÅQ¯ð¾ûýqq¿Z¾WcMV¯@{Éãÿ®7Jk0¸ê‘õDŽIIÈGAV"£†8V"*.÷n#**Ãû%½.³rS£Ö¨1ŽÂÈ´¤ê‘(¬ëÕ©žÑ¹¼è#@Nëƒvê©&P±7M4¼&ð-ﱺª1æÜº²FÓ«¡ç¼‹Š„tx}ž;-z-h¶¹¥k¬޼|I7SÃLõ¸âÕZ ‹ïýª(Õ[Uc¯ê4„‰*²¥lØ¡F(gHSjìn_¤À˜¾’`_¤ºñYmæó2:—HªX,•K‹¡Ó!0 ïÕV©ÃlT5Í5•dë´·ª±ÉôÂ4VË4&c”mŠ%2ÅAË¥²e ¬W$Ç0¢2$󏔨¤AA…zh‹XjEÔ;ÇŒ›iª"Ìl#ÄiÊœ–kN0óÓ™xߢð¥÷òÄЇ®¶ɘ¥wïnß©L=úà–§áZ¥^4*ýM± ÿyÕGïi((Ñ3˜ð\>©íárÏlp:¥s„±ˆó'_ò;zË蛩htbXÿ]ÏɃ.ÁÿûUŸw²6fMcook|v5 ½>“W‹1ó!eë4óà”ë¼²nÒ´ÍÒm i#0J Ôb1º©†ÅHÐÑgü‹[ga¯Hû(üµ@ÃGî²PÑ›[N¹i%V ä$ÆChøq”‚œPÒZ”Ö£´¥Í(=ŒÒ£(+ðå‰EŸ%ªÚÐÞ‡I uÚö êîô[6]EÖéî@[(¼`ÈÞ”L¡¦z¾;Èf°§¢€óL©üļørˆ!U•À tѧ(aU(â]:+­Ÿ5ç©•YÓ¿'5=™“@&Õ¢šµc¯M¼®d¸'ûÞ­ã)Þ’b^|ë¾ÂÛÁÒ„^yojø¦ŠoÜçu|^Ë>oàózöyŸ7²Ïñy3ûüŸfŸÇ|q„D®…a¯DLã¬ä–Ø"¢ÙÅdP”ã ÎK)ÅÉÀ7Y´˜3‚É„2N^`,áöž!ah"Žý7.5U€šp¬ÎÍ9‚Îé¬Ly€¤ ’á~\{Œd;15´âà rð_ºk‰@ÍÊ´QN-•yÀW¬X'°ê§‘°Dw¥˜H)¢PE‚Oö$‘•ì'|ùM¸yu•Õ6¦÷lû„L;"G…‘ xY”„ÝŠ5¥( ë >Êßz;'W 3¼Ý“i%V‹™t@rE~™&êKU}©©/uõ¥j¡©Þª/Gê PA$…±º[•2wèŸTê4ò @°%©‡Ý é‰NR©ÏDJA‰”š)íÁm@*‘’ë#-¡GZàŽ´8i95ÐJ¬¦p å™!H-ö§ä¬€ÌEȉʰo,º,=iíñhà‡o‘¸éúù~ åOþ…ŠC­”RÌ1ü†7d0_ùS;ð×Ö¸Ç_.® šÃª–8¡‚õ\ ÚÒr%-£ ~¾H•[‚Ec½³•J’s¥:^U’©¼ÈAå¾¢pDï9mˆ\šüÝö-J§5ÚËèœÍûk¹×{žuß¿GKÜrà<â}°²Ë[2¦¢÷(©¼¹ ËBc4<Ãm ùðŸ6˜¬»ñ-ÇÓ'ùÇcûñùÔ–VM”áö1¹2eòÎÀ:­Œ†íŸ=0oϦZzéîp㌆ØCÄl!2&À‰c¤uÁ’.®#^Ücyœ˜0BfaÂPÝIoä„¢PŒ&æV±cV……ÄoÞ2TóÐ#qכЫîI…‰P缘VTL¦ý´‡ŒõœGZ4Q©ó¸ Š ö“ ö%ÚQ ²7H *ÃxbÔUzéÔˆaÜ>{á‰(L÷{E3Óüɸîò|´ú$œ;/豺æ,NNØHΕN2êmqP*ç‰ß£ÙƳ˜ ¼X‹kìñα‡¸(†#~мÄË)LäÑ’’“ØB“"@mBÚ›¼1R`7GÀR<íÐÊ-Cr‡–Þ)¿@g~3þ+‹Å{™6á}fHxD‹”`&éΘ”RÍž› ~‰d·øiƒDf½•bŸ Pw±NòŸ¸æUh­)œ¡‘½z =´t°Lr È.ÏŒXhDGq#ÌtJÍ#±hÏ_ì¯ç§>!º¦¡ClI٥̗~€¸Ã A¡£þ _õͪA²y†ûVý‰z+´Fÿj›’µaw&D,–Á[’Є²ÈpVLŽRb/ð¢8gÊ%¢BQl8¦ ‡ÝrfÁ áe÷À,…[(¹‰¶V(þœñÞÅ |†ÃcÇWP@)· ž¡}g¼ Ý-é1¨PžADBwZ$ÃheÕÚÐ ŸßnÑ­e·°ŸBñÍXÍ›º·é¹ñ£wcõ#±Ô3Ç.^’<¼´^€/“€ÎxĆFF=ˆŒã,*‰‰ðÐñŽ©F¥h èm°º€æì .uÚÛ€ÉZ'vQå“—àÚBŸà˽xF2¸1ÃLÂ~«eR žG3ƒnÃxeÃÆ»KÃé çxB §/€¸þš†Cg\;‚PN­2O‚àÏV‚‘­Wºé»jžd‰œË¸¬ØñngWîùb“f63ͪê;½ÈU˜VÔ¾<Úº1[Ò§*4 PË1l0O`{ÂÁ).Ì8r õ3qVÝŸµ¢°­4€[†pˆœ¾br³›ZÌÔÃúN´àåbQÓf0}œe€lzÓ ÊŸd3£Zg‘gÇ-åòå8Ù•/;€‘-Uî |ßÛGùXþ>¬—«©@ mØ'œÈÃ9¬¯ƒ’ä)v+J9 ;o0ÛE1» _O™Ø›HìsÏ^ÆÖQùC™•måGÅ‚ã/½xÄ›‡] ^ÄTºsSnòôÔ>aKéWè<%ú‚Õ3áˆPNe„ ¨ÓÔ>è7š±Fo•)dVe7QΕҦì/K@f4Õ ú‹W©’_ õa:”GŽ^1² õ€A¨³‡ ¤Re×H`‹2á­%YuùghR#–¦›Jh³3åŽyw”¿"4 -Æ»lv7¡ A«‡e—›BÅÅc‚æ–ÚÚ4‘tÅ"_ÁÇG8Or˜½‘§ÕÚ66ØØ€MXÚ©û˜lÞîÓ!dbUÞ $=ÏòõØðL&hÇæ=8eï’ÙØDÖÑ4µ^7Ê6´ëlwo×ûÄìŠìC¹ºØo³ûM$?å×zÞ­%ZìÝrBèq™Âo佞dã°u¡äsÉŽÛz¦ôD}8Ñ1‰’cI@ €76#°,cv6 :ÓÉÙè5…œá}Q­d ,²ÉŒu»MŒ‰v>&nBqÍY>²H"™z°/´{£ ^‹é©2CßO))+¾VBÏ>t\èª>t,°Hˆ(î «­BЂ¯sdÑ2~ä,œ˜Lh=®f.\µç±fŸ£7èÊ ŠlA‹’úªa1ÊöO'Š—.ïêÆ2FÍc³ï½xn~ˆòæE2¥d5¨på+õrŽ]Dª×uxpº°¼×™uÚ1uد½r²~?´¢—ý¡2lÕ\ VŸÃ`Káì|Ææ€€²ÙÑCÔ2È‘®Î[ædÑ”¤¾R68­ÕŸ©Ö V«?×ÛPÖ1UÏU±¼‡|_Ýaó¹Ú¼¸ÞãÙSUç0SGgÅÓw?‘°c»cV¦Ø¼&zf3Γžù™­íª¹˜-¬dœðÏM÷ê\%-4¹Õu¥÷R¹žžÃ–墌Äà4’g,¶œËPGÿ-s„¼j+‘$gþ¨Q[]Œ2¿bŠÊ3HxÇôér"¶ ¡­,G²*‹3œ,ëaDØTÈ2•K²#¾P•ÌZ÷Ó!p¢Ä‰òeššþ"=iu$ºÇeò’€Kqz_7è¯C•R/_ÀlÑvžM+³Š±—Íá¦H„û‹4˜¡ñ½9ãñÆyf³V[í®Iv:J"»ê:/;8âœüxÍÙ‹ó(tf%猄w ÇKèËYq@–Ť,¸/Ô剥±(Éf‚‡ÁcÛ\„ÁKqNVP+rƒ NbPõ&û˜˜ÂiÔšÎKI±XL},Ýkh2u›Jü¦èJh÷™é¥I%,kÖøHçGLLÔá|±çW“©º›Ópס *åL²/Ó1¬t~Nò*e]Ø›0ï¡r.ºãéd*“ºÚ7*1ˆD!þRœ€Ö-ø†\”@!ôjxéTt¦³ÃqtKKÌ æ¿Å4>­¶',m“¶N¥ñ‘(“w•©Ë¾ðѬØ;YVqéä?CpàfÉ- /Kq”ÿ2z®ö~üFÖ—ì.ÃËWî+Ý>«Gò¤/ÓËBýûˇÜ*2eP¨ožøÂaT õøÜÖ´ë=‡;ªSòbíͱ!Å Èv¸ƒ¸|Éß þNÌï*þ®•ëžÈ)ßÖðm³\­Vu…:5X)×ý¨Aªåf¸‘&½n–㺮qHPTÊÍÚAèÄ2Áߤ†:Ñp…áN| ëôˆ€Ô`'®0܉ÒÀjåZ#îª g‚\üUñ‘- zÕƒ³NOü5ð‘- zUÁYÔŠ„`SÒÛ-v7a„Ãcì|¾|›0HeJмд“ŽuúOv‚*Õö…S>] ÞŒUÉ%‡†ËKé¦DYD%“;FÅed>»cõŘÙ2\(J*QµÕ*Q½5*žé€úX DLäXE_TdþÅëÞcº³T±ŸsW‡lML›rÃú4;¥J¬[âvªH„æã”P •ëz7«¨×Õ2e¨¤¬ßך‡‰_†®é,Åe»LÃ*ä  jP/×êæ¿z`°¬ì"n‰cð0¶þ ±Ynĵ$®ÆGÍ£ÆQ=8Äj99:ª%ÕÃzó°‘i‡ú:,7’£Z-n$µj£^ƒ2‰]¦Nh8*ÇÕf¥^?¬6ëª]ÈACÍ ØÂ¡M @â<­ñÓÄ-[ç§q"¯F¹Þ¬4:Î_xnkåJ%©¡­zX È´yTm&õ¤qtxä—©s;µêa’Ôšq%ŽëG>r„œz¹Yo6`’8©Æõ<äÔ rjåjó¨ÞH€¬ðo€Fêeýÿ6DRØ £*ÿu‹ÔyˆGÍÊa%9Šù¯[¤ÁÓ߬ÅI­rH«þ%MÅñÈQ܌뙥"Q^;ª¦ªÕz#NÂË)A2ª×’fµÒ¬5*A”•+@Ö&°Y;Œë• H nªQ«%ÍF³Çͦ_¦I3dL nB‡ ÌQÞÌ4왩ÆjÒ¨Nã¤œš£ø°ÚhVk€úÃf-85‡Íf½W`î ½j`j’2o¥Ò€Yi&q¥˜š0Ú¨6ªGÀHš^‘&¯Yÿð°V­W3«Wq¤Z³~ØlTj1LcÎìÖ“z:¬6*^Wfö€‹5ë`:Õ83Ã’âà]µYÅÖ+™n0<0k°taê‡Ipê€ïB@¶ X€‡±_榷 «x`Z‹¹ÓÛ4Ó ¥kGÕ8¦× ­¼F9®U“:ÌÌ!p¼$Lo­œÄ5ÄgµZ”„§÷(±þ Î.Ln\©7`À:ªÙËñ¬`wÀZ*‰ËÆõr½rԨŇHá‡D¯!¨ÁŠK ¹j-HuXš5àïGGÕzrçðKØ ¸ëT3D" ¶íZh2ý"Ã%Ï9jBGµ¤ZϬï&ÏÔQVîQ (»ÒÈpÞCî+©TÑ#P…u˜d鈈$)øê‡$9ªÄf;¬ 5ê¨">5ÊdõëV”Â&¨›È E=“””ÛÊ5{ð3qŒ_ÃKmûÂø±Ku²õ»cÝ|)&Ë}¶;¶,à¶ „hÄÊÚ°èN+øͯn4Þ:A)ß3åò´ëå$âÄ)GnÛ­à“ªL~y¯Ú7îÅŠËÈnŒüw]P0¸R#ˆ“Æ@“>¾&ê¶¡SûŽp/ DE4IV©{æ/püÜOë¢ûU¢¡‘)_Šóc9ÊýXÝN唨<Áûç6LÖÓwØžõ1n=KèQÔ¡tö[}«õ*3ÿMe në08ÓŸn´Ð©ñÇ‘ówÐ6”²ëXóɦ’rè_µ¢9°¥ŠãeÇô¯!l‚÷- «ÂŸu k³.cAihĶÀÕ`¦¶t¶gÉ…h娢”¬‹•ñéÐÈ3Ðl¹®Ì…)Ldétpr¬)ħÌd'âÃqA3.Ñ󒬋c5tg†›ÔðÈMÁh{þg~»e–чž9EûLîuÔ‚ýú{’ÅïKï®âgó}lk,Í6¶5ÅG¨­4°­Y=¶µåÀ%C1«õì e¸äœn¡¼!(TËÝ0-î©,Åâæñ¯Àí£5 š+äíéÌÿ-òuŠªÃqE£é÷:{Ã=_-î9éñ¡ýq…¶¾Nçß©„èçjQ1å]&$Y ¤Ujß ÅšWø)M¶ãî1üß"Ù‡}¸Ñ&çjrÙ3}sq,qz^TÏ‚tLj ±ô¼ÇGÎAv©ðôœkz-zÔµk;²—žìMdì£EƒÔ£ÂÛ#–Ã5ߣ®&ò?µ'H*¾93”©Ÿ å0xœ´ç.sÅØüñ †üH3-–Xêôs H®‰Máð¹âÁØ+£ß»å­ÑúG¡òñaP}F±yJká<æ{.Baûx'Ö1ý EL¸»bܼT¼:/Z0~?zÅ NðÖ´‡Å²ÂÙÜ«ÅH ܸšbØ¿pßj970‰Ù9æ5æJžYÔw⻼·UbŸŠ£¹Ì5=Tz>Ep%¥ÖŠLƒ+RåöÖ÷uÛÈä P‰&èϾKˆzä¼ßfiv3"äËÖŸ+ºà4žçÎ.„•çíü¼óÔe @Ó² oÎÄ«&íÜá£aFú’i;·ùéù³GWë@Ù . ®2£Â vé_ïúfµÈ×ÈàÎÏ÷ÔÝ¥¸X¸¹£Ê­ž®)ò¡™žæy¢$×ËŠŽ,-¦û@MA®'X¾Ä–t‰?ßòcÉ”¤Y–E[/âÂfm},G©gÓ¬¨HÄLzsáâZ‹à@žýPζԮMÅtcß1ÿ1"j@ÐÇû«‡Ð4»{3L{¨o„7Œ^[¥‡¸ %Ä &Àž"{å Š"6+º ™†+d'Ö&t §Ç»ìª‹À÷l£–÷—Ê·![e ÏZµÎºµJ´€ù­Ftæht‚ŽòMìú^tŠ?-´p84¬½ŹÁŒÛeú~…íü •¦Zéq |;ýˆ7ñá™ûzY{¤šk—OÊb€Y3#4øÆé´7µ¤) •ñŸon‡bír«Ü.«4´q]â£fSxQNšÚøþfëjgû,óWÚeÉÈD†ÒWüˆ>\€Ø ¡« ^`ÒÜÅŒé4¯~I‚±mOC?û¿`‘·[—¥±>ÈžRŠ^0:xøÁÚàƒLÊJta —ˆ‹lZZ’.‹|(Ò©Sù{€¿‹ÔXjl™d±쯵Ò¡‘–xçô4Àñ~(ú`-ì”x®õsn…šûà4Ç¿å°VÖ3ìbjkó: ÁŒMn†‰j†1Û›JûS¬ž[RXñÄN~$‡˜y÷ßM3³h=“ø¶–7*¡ö ê—¾ì 3ÀZÈrͮᩀ$/!‘-—œ3Vœ\ ò¤2/ÞÒñj„OH’rØ7˜#س5‚Í ˆ6Ò¹¨·ašÑIÈÅôJÎ ‹ë$ú0ˆ·²Ï|øÖûOî>ææ%<1þf<÷÷ÓÒœºÅÞç\ NaI6ãydhMU9*cLîEG®ÝÌFŒ…Š IWò°Knc°–ñ0ê[›‘ 1“Å:ÀØ(“j%,R"}ÜM$~yC Á,uw;«oÝÎ’—mg´É¸lü¤0°žà/zøÁyøÁcžÛöÂ6õœð8à ³[_0}1·ýp”ÿ‡ë)Â0ˆÝ¡J€ñÏ¿ãQ& w|fߊq±î4šìÞåމ͟pàþ9`‰xÞ‹ŸÿE‰8—plÞûøôi½zÚ,–sq÷åÓçYaöé.º¿›EËèá>zxŠßÛD‹åç•g±ÜÌ¿Ì×Â/„~x,'‹Ý­ž>ÝÏÅ÷õüvñ¸X-4\À:0.uÑ)õ(>Ïn7«õ£˜eë|š-ïÄÃl³^ü!>ýóûÅÃb9ÛÀ«²×ÈâQ<=>Íîïˆ[øw~‡åáÕí*Ÿž6b±çÐà\¾V•Ð×*ý¾Ø|g¿-–_`Œb³x˜‹Åg!Ö·+;X®6b9ŸßÍïLïä|¹YÿÐà# ¢€†/–ESR} ‹Íl±|›¯s5Þ…þ# göe^ôòvuÿô°|Ì6±úlWž­çTð€c1±mg[Á’w‹Ù—Õrvÿ¸µÍõê÷÷û1Ô’úòU${ðóá ž¨NËÙó¹„ïá0ùSt¿ú]|^­ÅÝrÿX¶q (´©0<‚ûùì§ñæpI„$G2[¯g?rAÁ¶ž7H%eÄ8Vòò áÀ±”·Â±ZßÁB‘}¯Ö‹/@¼÷NkÐÍöÖ–OŸ¸)3;Œ(ž ˜õ"0°Š(ßÃp —òý¦ °»?½¢óÙ§Õoóuþduî—yªóÏŸ·  Zv¬¬gÖÚz¾yZ/C‹ ÷Ó÷ï& w¶üòt?[笤|Êxºß,¾ß/æÀ~ÿº¸ý*~Ÿ`O@ò›•X}ÂE ¬$0¬ÎŒlñwbPŠÝü¾^l6s —0÷û=Àõï_¡Ñõ™»XÝ=ÝnÝ0œ‡§ 7‡xZƒIÒ›7ìÛù#ÕOÔ®‡}[P±(âü¿Íqœàâ7à‰‹åvç6 ;Õd¶=4MG]?ÀŠømvÿ4 õX|D O…oÑ·¢(ÏÿsšE!ÂaT’)lóõzè…>ÈÀ„Dä*TÑl{z{¸[Í M8Û ½Ï6¸«<ÞÆqdÿÛânŽ[Íßç뀚í95’ÙQ–¼AT3 íûÅ ö‹í0Hfö¸ài\l~´ÛDnÏ…`$Î$Y›jÄt»º¢B† ´÷e={¦ü™±Ó øq¾R¢Z‹å÷§MÙëG}`õ‹õôûâ¶Vì«И fäV4Œæ™Šè‹4Ü:Xæn…rä¯$¼‰e¢ÅeLJÙÂ᥇§p‡X2QÐ%”ÜÈ{™vÅÐè"É+ŸoX$×_q,[Ê¡<õ-úeÆYa_²eYŒFáb±|  üt^Ó"b}¤}^îëzc·vöœ­h{vÇ‹NÒØb +mtÔâb©ÄÅ`?{ëw~?gàË|9_Ãê»wOk¤RlÏp2ÉoËüPdµÞb6/ê—å7Ó= ÓM¶A¯p…óÉHïçŸ7†{ÚP°°SFJ•3l»ÿW» Y×zþvƒå­-SÞÃÒšÝ~+ó,ü»®ú;BAåð ®45¨·÷sX«ûù: ‚Šo~à€—óßÅÃüÊÀ³5¬òYz¼ÜÏ>™ ;äMãóÓòË=Þr?{þøþ#º{v-îf°Fä[˜»Í¸0®š„®‡ü½$¿&ö†M2†2ÒþÆÓ0²“hQ‰~~}Š~ý{ô+|#Ø ¾ÇÑ}tÿÁÑò!Æe}Ÿ¨VaGB‹–Y6l© ï3p·P…–¯e­_1:‰ÚQÎÖ_c³èŠ–Š <¶/IÊùµR_V(4T+z¸Ä|þµaÐŒÑÔ ™°îÿn^Xlª<Ü;ê&2X÷·Ã6¥¦©W5ëÍægÕýÉ"8¿>9Hú2{z|\àkt/V–¾ÏÖ„9’`Yj”Ád@K3ø”P+ÌÄ6j0}Õ7ɘ,ÔÀ<“@,Ý,0š»åü7qff&×ß]uŸ&éï Þ£(ª ±­¸X@ Êq¨[ã9‰w™’ZfJêþk\Ÿa›¸wjÉjþ‡=¦ûE| x/Kß,ËÖâõR¸Ø#„å¤&Žåz°¦C°ÄWø†`â:ù/2è• < @üȼˆ‘,f÷kа~8LUj‹¤ðÛbÛ^q¥â (È'n¿Bs¤u/ç P>ÎH¯v½çÖTK kf6¸ÊNÍS ¿|ȼ|à—i\Ï™zü"£&Øàa÷¥‡yí%@Aœf'ÚðìÀ,ÑüxýÀF•]nõDöjrÉ2Q ÔŒ639ÃÅhiAñ@äã­©˜ÁJSH=rÉýe&d¹Ð—Cð÷DAién2ÏÍýê:]ì™ÕSÛ¬dÖÖ½”wy~#ú)OfÓ8T<†gÏîhÙáøë×¢“ÌÕ´t”y¢>’ÄcâÇ•œBr7úö zeÚ?«•L 4»KéÒâ±LvKwÅÉžT9©Aó9hÂo™õŽ[µmŠ„ßet7‹î`_ÞþÝýÀ¿? ÑÃÐ73PûÑä† ŠÔ/¿ß?™_JÜ!™ôi¹^‘ï~µúþHú4¹–òâüŸ`[B}}©•Ê_AžR^~A P¤$¬HT⤩r¾Pr÷6ï~п³¬„‚CÂñD M-~ 8ËÄìC‹Ï…%Ú1*Eñ8[w3gž¼÷lßÇeÈÊØý*2y%½]ÝÍ OK½…xê?³¹ ¡c±ûËq¾øaÿ Ϩ©X¡´Ü‹{øÔ–·¨ÜYî‡Sî‡UÎÈ8ˆ7 `|ñ \~ÙZÚƒÉXüa­zê~½bçÖsìnñƒŸÿr‘·„²¦$ÅO+`¸!Šb)ÓT¸ŸÏ–%Ї‘u$mó[ÝÕë〠x̳-«YâOUaàÁÅ€B€5þbXŠÃ.–ÌÉkÂP‰$D=¸H¯s—D®Qm—níØ–*ÅßuIú™)X¥·t’-]µJWÝÒUuëDx>ùÇ3¶g“›¹ÍÚ KÃW6+Mp¦ùÜά앜ŒŠ.>/Øny»Z£—­;ÀÛXžXο€tñÛÜt‰Ãƒ$>8ªlã†8XsêÅçƒìÎÿ ÛÎÑ 6vâT;2§<¦CüIïy^óBž˜Õr“úÏq“?ä²Þ•‹Ô_ÎEêÛº³ßhîâš+8’Lª[ š)Ps ÔþŒÃ['Ê®¢µe–u ø( ¬ ê£GZÂÄWvNBÅ2» €2âØÎÿÁÌE%4 ¹Ã6ìÁb „+ÄÈ"½Xøâ*Ûl…˜Ç?[a(ï`v Ì?Švs¾¤b¸Pbs!èÍnÙ'6¬VI…—…µ¾r`¨‡Àx&ÆœÊëØdA6)w˜ÙQV+/f1‡£ýÅúú†Â1Ÿ¢_WŸž ¢Ø+b±çb( Ô'(*gÈB<Þ ` ¿Í™„Ã)7UŸäß äµÎDÀèrÕVSÖÍv ËYµ¹â•¾Ç UX®–d µ+¨E¢êå9ô!¨……'ö_Áƒï«Ç¹³^²¬W! ëÐŽ+‘ kÔÔ&¸qu;‹ŽüÔ̤§šœ¹«ÛÛ§5š"´;W®o4ól«¥ãŠE?üüöërqK‘Wì“—ÎòGÛIm ˆ½í Œ.¸ˆÛyÿ°ÅÈ è\›­¿w[ «iÅ‚ ø™ÿÙœw+þ ›¯õkb[‘ïÜü}û„ÌYtÿ‰ý˜ìÄäRÊëH~L%^†=kÈ÷ÊË9Оå®òø"²¾Hs”·'ë,uI<Ç5 €äO^­ÎU[„€Ý[âþ‰v± rý퓊IÑ0|£™f—ð·OøñOÀÍdúÍuN㻩ï%(d=üDîU÷!y²\´…P´+1rÿ‰R àÆãçiß•›mäo´ÛÉU{*zd{‡ÎAMyŠp]#6¸ð&¬@KbcO[ãyŠáÑÝ=º .Ê£í‹òÈZ”‡Ñ9þvÉ`l’Û²R-jD#­…ÐzÑ †Öwh57+ÛÖÿ3ë»™Y߇.y¹?ÉY÷0*gö m%"|.ó=g°3=(UocŽn~_I!ññŸÃqç÷ûÎpø&‚aCÞ³LÌÿ—ºÐ$²øï¾´nï±CímœhrŠìùý üj?i  …±²›QÜž¼Â,žß·üE¶ð=Ë­Æ•÷t ʾç8Ó” |Ïvš…«Öt¡€Eœv¤üYåoÎe»Ö&º$j‚éj.£ngu£îÕE„i™»Îðå§B_•ZV'—M':Y¯N;‘0êt·ðhªïÊ$È&Á«  š»h}åaÖ¯^ÊÙí¿Éeo„ÙBí,áÚ4 S ¯ yþ—Š[H“×Û¶&“Q¯éîp2fuñÙèT6…¹¡¼e}¶­ËM'EÎS¾kÓ@CL#LíšÆ®.¸¹*5Wݹ¹^zÆ9‡J†Z7 ôÌ֨њltO^£‘ÔÓ<íñ¿;öì£hz=R(â†sào}fvM_Œ;i{ZÀ›/o¢~”Í™ù’EýŠå”¿Â m"ZÖm©xû(L÷{Eu5\¯ ³9™Žðòw^ZS1}£DÖ{/LÍÜÿéK=½¤É¯Êö‘¼ r¶dîiva}Ôø±›R”O5Ÿ'äZêûUøÃiÔ„J(Ûݲ™8¹û²Ù`kE½†ñ\0]CþîO_He”NÇ­iR¸¼¼Œ¬ë `Ut§­î’­q/jC¹n4¥?Ü ß  ¢ëÑøC·Ã·ópî,Àïb¬Á)_…Àרm8W!`²¬þàÄ¿ÃÀ,ïî‘ÑðdOV0åô%½ä[^Î$Á<¿`Èp,ù¸?:£ÔôŒ‰×-%@2f'âÎrî¤+OìÔæöÐUšs?•9á_¼4§ùëSš¿>‘9ÎgçìdÒÿé朽|wÊzá€é hi·{Õ4Òݘ'ê€Ó¤ø’ f–ñ9NëÏþsv@â'N¾­I yf9·%'‚4¯ýÜÁ¡ÉV}•’¡ÙÏf"eHÞÞÚKs$û­¥;ÃÊ’¼½5/O2¦ß£Ø†l"cbR˜©#“zw“˜Ò8kÛ—w»ìÓ¯ ,ÇêË13ŒÛæ[ògKª¹ $A}'¨ô1[Ýߢ´·‡1RÐf>X&‘{øµÎ‚™¡êú%™z×óǪڛèGô8ûmžD?¾¯óhñ}¶ŽÖøÏb¾^[—ý NËgÝr’¡5•¯qÒ÷ò5rzòH^À»ï?;í".nDýÝš¿Q&‹ÛÁ&wiÊš2”Ç‘'º½›9>¨þJ´©Sp«W5iE+¡¶$¾…Zªépö“L¢îƒ‚ÉÔ­`àÍÅþ²/,i@tÐr¡s,”âšX;%&¢x\@šˆá[i¶þŠÁ7ÿû¿ÿûŽ+yÖ;Aq,ÓÎ{Cá=J•§ã«n9|Û¹G–À.¤¹\Ïáÿû±V˜ÒS¼ªïœŬ˷¯À&;M[}¾ŠzTþP¶×Š`Úqÿ餭£Ž® Ôë.æKÂˈ1½¹Àã|v9„qÿÕË¡þòåð¦×åv'SÒéžøñ%–$²`2?ô´bÖÓÉ4 ’åm>Va••ßÂcO†tƒª–lKbÑ]Þ-×yXf&h !xûKtÁ}ŠòÆø ¡(GátÉ•ò^Çðv:z«W70ƶ²!ì¡ÐÙ#éOÞ%ô‡Û-1I*+6uÑâCI_,R;ZØ3¢¬‹!)Íð¢S`ݲóRöàÈ8ÊИCp ÑxTõ…L<äÄÕw»üeÆ"2ŸzQUÔFtëd<>œö[gZ3õuR÷’¾i4…æ¢)€këʾ¶M–w¾ÜôF}··›!=BE›`ì ëtǾ"Ì&.üÇnÀ¹=•``Ý‹hxÚJûæ:&º›‰UºÐ²}Í<þ™L/¼[ Y÷~í5øyƒùU†ônfŠñÒI॰ûŒèQ÷B1LRïä~˜íÙ$Nq«––º‹ñ€?nÉ…X\Öƒ”wçQÕÀ:3pæ:< '°à‚HÜñô{Zó¼aÃ6@©°û03ûUiRÀ«ƒ€ŠØL§{ä‰gÝŒŸ{™·¼[ý¤ÕþpÝwÌU€Ð)&áF²…cì¨loKôÛ—éàj ÍÁ­@fØ40év3wl–ýêd7Û‡ÖåøbÀs@G,œºiIN½Ð€™®ý!—m€W2_æ»­vÉaÚúÐ*`z¦|9IÿÖ•‰«[SÌa/kãûa÷—)Ê ¤‡}µúÓ¯fd:Žl<ê[9¡;Wc˜)¿¬!)¯Œíµa¦†nJ÷ òE”»tÑ`÷¬EßO®¦~+étBWŠÁHãîhk`Úƒé8ëÁ”8Ý)DK”€À7šV$5Å­Ê@Ù•«D.)…2ÝFëlƒðGò®"Uï´œàm7t‹!eï Ñ»5¼É`X+Ž‘qcæLœ7ÑzhOŬ»¾™•žlL F³JaÍÅ r1 n¤M¾ºòBÝî9úاMÆQ='&1úi:žHŽ»ö3+~pª«K î0¸ªpV0%zwJÉ“ã(I¢„îäLjˆé)’íÐÆ$èG`:]¾z8Æ ÛB`†ó©טtôªW§ž^õ¥K1—Æ«'A,„ R¤|ÕïÐDàâh÷ÒîÇð ”ô!ùö&"¸B•’p_(»ðúW¤X mͬ³€³³ö5µAe¶.«È“ÇcÎÖj ÑÎs{SçýÆws¥¤Å¨àÉT‹7¢QÓRšÐZµ涀´[owžžtÏÒ!±{ë^O·2Í´f`,÷÷$çk@©q;]L å"Úl”E”Ö2-LÍ‹­Éµv·ú}:¥Ô¶þžFƒ%ž’K¬@¨rûæÚ´æ2,—•"æ|1*F*pØåÞ ¡UÕáé’`©5ÍâzÄpýAvI§‘ÂÞÀ²µ§[«Ÿð}eÝqÃl’0Ì ðZ iS&P®{i›W¹Rü:lx÷q&£€‹1¡ ·ÀlȲ/¨ÜÎL,«âr¼q9"LT#¡“‹¬“†ù›»Ã3ø¦1sq­VÛԩ̨-/;Ö´‰ófKë@‡7€À«0¢hN«z³¢„Ëé …:Ѥ Ó—°Á rX Pð¢„ËÊ ÷À°,æ\m}CbËÐÙÞ ¡/ßÅìÎÀ!ÎÍBâoàÔ]ØÅtÜâ¡GeJŸ;¥§(mØ× [Ÿ<˜DáÝr Š“Ìæ(l˜€cék-Záñ9Î'ªþËû ó;Æ«6Xa¾F•î6Û¼„ÃBˆ5²OÓŒþOëkèâZÞ,x„MajÛ0BB¶{+N`+¢°¯\f»ñïÞæ“.<Íßô‡/VVrxöRolüùV˜-]OGÃÒ¸uÑ›Œ¶"ÏúÐq$Í;ÝKjÞe |i´š‰5ÝkXÉBN…1#á¦mî-¿–ߊþàVqÒÔw‘€ˆÏ)©±}5F)‹VÒ?&*±°F~ªü¾¯rñö@¡žü­D±Næaßt»×\»®`­¼+Ÿ° Ë‚w÷5Ûàa’uë´§XyˆÓ¿àök¼q&¥Áéß$JaëÉK±@<š.úð?4«Ø– ø ªdÞ6ÜmavІ"¯ï^_zÎy¼÷~Ò‹FÇi§åºêj¹ †B^º€¹ ÌS2¥/yAo;º½ÙèÒ¾¸ioxò'Q¯Ýƒ§gø#—¡OÙoÆl[2¾sËôtëð—¹Ñùñz÷€ ÞŸw4Eé¤5¸ˆÒk\øÑ9ipÑ9𨫋ˆ·Ö¨ âh({”?ÿÝË6¼†¡Äe<…Æ— þ‘æ-Æ+¼ˆiù-8W”GÃKm7£mA™Ï¦µ‰(YÖˆp ýP¤.Q)`Å/âsÁ„J?"mŠIµ"R¶(T(,ê*)èå`$”.ʵ›}£é20ÂG5ÂÏǸ¢)¶F;xÌZ¬ª²¼Ç ”D,wD,Èõ—âX®.l«*cŽõ¸­PøµuÆ#t18‰ ‘þHr^«¯ˆÄ€5šÈ% ©Éƒêž¼9(³†]·Å~ø†1W*1"Ì]¬ÒÙ7 ‹ï¤ ¨K4÷ìF0œÓû73‹J{kOèî³u“t¢ì±²j ¨Ì•\>1νÊš¸X)Dé“ÊDD" Ên¬º(#–¶²¶ÇÝ–ZtÙZ_Û…TsÚS踸ܩ)ÐúJ†ç(f#FÁB›—ÐX ‡Ñ‹ÈpDòÝQ7Æ×xeln³C‘éð ý³m4&¢@’dÈ4?YBGl™ÒÊdA òhRÖðZ©Fü浘PÜ«¥ iDInz{Ö}¶qÀ·‰[%†8ªŸ¸\ÑÕsЧ×*{øZÑ,¬ÞcuÞ€Öòq¥œ˜«ÂeœÆ´œ@Ïê}lÞ/­(f®èUÖû²1.ø]ÕË fÇZ^„mœBøØ·•S:ÖeÚ®™Ùï±€bnæ?•ÚœéP*“&Š€ øžÛ 3A÷25y¬'qbÿºõ˜ãÂbËc-Å8J‹Ç±]ܼKðÝG AŒQ=ŽšJÚqh–ÕtÝÃŒ—+ôdwíw|Y©þIZ¿i§µð†m7çÍf[þvp+µWRËtžñ^‘qnÒþe3dØKŽw}…¢Ð63œøÎ꺤€^wЀ]²,¸üz0$Fq·1Œ¸ý›¶rçAâ>P1êwO®ÆÞ[ñ'ÃZIycû-ªwÊÒ?‘ ] —ÄDt#<²áieU€= 1(c9šH¦e<|>iÝJQ+>U\YYFÕ„I­‘÷»áe™[°IHÇZHu*¬;ž"Á2@ô™2e>e¡Œv“Ùˆ ª¡6ÖäÉǶO°Ýd3„†‰ù !c®,IÖivöþ~Œ.ÆÝÆpz5&g’¼Ü·ážÜ°Zh"Þ°ÅêͶ+µÒqk&þP¢@Ë2-È¶Ž¯Yé–}æòÒiBˆ³Û{¨yHš“oè;F Yq¡,/c;ˆpÃßЬcñ$6d‹×óÃ&dëH¾²]4Æ’d¼£=Õ'¾ß&€êRmÍcÏ™%ri†m˜Öl.Šß@ƒŽø2ÆChoæc«“Ëvwz{F§SÆ#ï´ÂO@ñüº (2»:Üô íþRÊØ}…¥¹ZÏ8ŽBà¥{Öà0±oHrüRÏ¢¶$gö­IÌíNXÚC ºuè¬M½“Y—©Œ¼ðN²eϱa ‡s\Œ#Ièìš}jÍ´áŸ_ãntPÍ;œ&ci0ªf̧ÍlV“#£nP///A¼Q«a8š²]á¥ãIçì/BQ€ôQŒ—§ðeº|C´hú³MÄF0éŽÿ§Â‚¼k @?ÐU¿5N§7J•ÌkòHžc‹Ž@8–Y¸ Jjá¿Îµ¢,vÕê¹æ/ùL^î‡Rf5 efŽ=[+М±æED×ú±ô޼zчø,2\H\ãóò|B‹¾˜ ò³ÁLZ:HÚ_ÆåAyR¦@ÜÈ騗lç&âÀW\26…ŒML+tV· ¤ã²:Y8ðx º!Ûí«ôÐ0%È–>òp4rËî9 F™ê“b’ˆ4d#ýÑ|£Ž¯Ý¨†4‰g6Fc ÆþÒã7Ìf–I .³Ã=˜¬ /{0#'o” ‹1¢Ï“=^˜<Ê«ànÈ•âüìQ^v2)+¥†ØU‘š°ÿ©ñSùKñ#Ñãl–Ê[Â÷‘Ù 42æ– E­à¡=dÎSï›<µ|¢e´e¤ Ó}{;uŽ.J¶dŽP| ×nMÀÖ!˜YC5áäA¦ÁL³Ci }>ëãèÅÒüaÚ (´éø¦,F(Y_§Òc±£q÷¢Û#¹ÀÓ£lž1M°®­,Z”£L%ƒGWXÏÖŒ&“”Nݦ§Ìá, FÃ’ÍÅñôX‘Ç¿5ÃŽ£{Y.e¬wHи§“ŽÏ g ß‘R. Ÿ€bvÆ3Pg\.d —Ë>_öÇ<8ÕºŸA?V…lÙ‘Yö(Nµ® o6^åg…l!¥Ûic¤B`ÿ|ž-0¹÷l#7óï"Ÿæt½ ¦Î߬¬Aì*±xèÓ²¶ñ"ðïl° 9µœ¸GJÈXP⎲‰½£ ks\Ë;U -*í]^úP]¼£6LÓm-Ût´EשGCâ2qNï¾é朮ó’kïŽC9r$Pç `¨Šæ2>ýÁ?ßQMüXi(•ÔÒÀ¥Nß²\B¾éБ uüï vÿ ìp¢^œÁ®‘üd »F"Ééõ)ìÕüv±•îú HN;h4.Þ‚ä çÁ€’ÑÈŸ20ú‡£?" üøØOTM »ð3[Å!¯˜NYE‹¥_,©Û/tò*Ê]…õ È2=Šÿ¸gݰ›Äœv½œì~¦1KÃF%) óÀ ¡oPº*‚Fß½ƒc ùÞäé2'-nàm¢ø''ñ¦ü‘üÐo<òFNˆ’I”QŽë‹6“$Î÷]ŠŒó2Éñíà½L*>7Ç®ƒAw?0ç í4„½]⾉5Ìäs×Lµ·W –±)樄Œ}êvÏÚ¢ìì’ÐdS²öØaí"Ì®¡}d÷о‰k73\;°óagÈQK ”ÅÄÚ„ßQSY5Q¢ÕJ‚j6#þ3|¾š·‘_#ÌG2{“œ2lÿÇ˺0ɪwñ Åu€aÃÿ-%J†¿Q"Õ8/‘ªÞ¿ŸO£¿*C0à=ù·Võ/¡UáD½T«ª&?©Uaøù­ªjiUo¥TU·(U²çŒNT ˆ~N_Üu<=H~ü{7JÖ.U „/Px¬t±£a×Éc²Øn™¢_åymç@7sU›\o5þ®Çé´+ ”6ä8™½„ó݃ÊÊ'‰*U’]w…|#œgpW;€ü‚Uo ²ò%>uPe¤*ŠJAã¿¥·7¢›¬}|ºŽ çeÙŸ—nipÕŸ¦xT†jëlðuJq¶:&XY¾ñÔå¿Ä#réèjb§'ƒg|vK3攎œU MÁem1£s˜ «oåTÓǶäOÔ ¥Mªsøên©·ÅÛ’'ë:fÛ^g=»ýñ"Ã_Åaúî­ýn]õWë?k¬ÿ¼A°Þxû­«¾Í({&UÏ :ºuüglæËÞ#%kó_cíËÙÚLÓò‹ªq_ŠCfÁÊ«÷:¥yÉŸ^Šœ‹K´ýA§ÊG»’Ý8¼,ý °OÇ[j” èæëë2Ï<”çž' „:µÒ£ O\a²øÄóè‡,ú)GÑ"¤ü˜Ê—´aCväšÊúbûh<4Š%YGÀi_C°KU-fÔ|ëg¢ÁWîX›°'5¥ÃÉ•Îú†Q*°GÞhè/Ô ¸sPà<{]$+š“"à §—W¹Ñ)5Tµ¾©†ôlªv†XùÔ®¼—›¦ªMîʾèC«Ðh¡À$o5*[s³¶Ëtq§»à^¸9쉡-J¨ùE˜ïpŒÁeÌP´+À œ(³9a2±c¤cë<0)ÆpÒ{Çx0F¶»gQv‰&¸hï„ ]^ڦᗓÇÞÚÇÁóðB‰?îº»Ê N~»š.Òöà yvÇ* Œºmƒµ±.Ô7)Sy¶9¢u¢ÎÌãù;X_”b™Ž\)‘mu”÷Ç›âÀ6wÔ‘û8‰ÌûAu³1`æɘTJæ§adL«ÒHô8¨Mg %_wKêìA¤…ØL§2›"¨ L¡©Lá- CâŠi…„Uª€Ñ' [«£°ÓÑ&”nêúg­¡½ae6>.%o}V­UlƈM'ƒy¶Ÿ#ÚÏ-Ó@˜wòzæ½ã™`û| ûêxÞbÖ ¨Ûb2'{­CÆö ㈣ÇÍ”Ó ÒIc™RM½öŽ£’Š7Z7~Žu”ãç³Ý|æ‡iYèUn#(œ`—¡&bã×H$ÓÁ;ZWÎü„䨳• ‰$®È“À¶óiñFž¡m!6uÏ™sBÝIuʼn­´Ú•Ùh"øaRÙiéÁç¯M–²ÓÆ "¯¯ÅùÕ€BÑ8­šš4pco7«Ñó'˜¬:•*–Ãíð,¦Ô^9£ŽòÆ(R±âs¤1àMù–]#×¶þ"ƒ ¼‡ÛC „d@­¼äaÕ¬ NåðŸý‚2Û[þ_£¨†UC¬UÍaî}Û™caØ£ï”Ë3píYV Ë61ø4¿»ƒgóõzµóÇÍâa¶±âܶZ¾|×c`?4·úÚáVÉ >>O¼p«j(nC}ͯŒ·J¼€«„"®ªoO5àí¹[ÁãL ¸°"ù%¬a‚ÿÞ(ÂËÙ¼hªò³r&ƉHHfÐ?Œ_1«s#£'MÈ©¾+sþã-,rË‹8h]z[¿ì׊2ߤðV¢ÓdÒè„[¬¥}êˆ"yõ5vxÖ\Z ù …væÖ´»·´yäÓ¦Z+F)U¨ä¿%ã²#¢Ž xõU -¥^qϲð{º^ØE†?8íîgðµÐÒÕO%„³L'9y…I ÈÝðlçît«Ù€^?å Nøü ñåÆí.’ðQ؉#ø'‘fìj¤³±šº=yÏÉ ¨~9.àQ¹òJЀúÎkùc?~g™N&xæc·ÃUXÇûqˆL(Y_³RX*ÏMË$¦nšäÒ!ªSð“ÂÞ”7ÒË“\˜ ²×V;E^(d yô¼ÀRo~:F E¥Š‹¯ÜÙO, €é §îi/r±èKrU™C§Luãœø°-Vuñ¬a>¥¢“1¬‹ÝlëVÏØÖ †ÌëÛ|ÂÐBõÌ·° Ù·7èChñ-þh#ôGÎ…UËÄï©ÏVC¼`º¨‘˜g›â¥ùÛÙr øöxjDäá `«a^Ôh˜Øï“m<¶5žZÙ¨Ü=Rð6‰±F¨틚 /íµl÷f•ŒmWÁµ #vÕÞêËÑØNî@DÀJ¯šYêÕð/«a[ù¾Ï˜ ØènhÕ…ÊŽ°š}6®É´{ر§ù‡D zòŒ^d|üB:?޽g€åPÎja¹ÿ%~s\$"àUÑ3áOD¼e¡é)Øæ'!|&ÒTÞe\&Å=äÚžuœJÆ]@Ú Ø›’]=!,‰ ßIc°Þ¬…"g·!g 65TÉ©UнzܳÈAª¶ ÍVòŽŸó1Ù8Vô<МĚ¾cMßMgrÄ6/„†¢ò¼ßÊ¥"A‘°(`*»Ãð@䬮xhð$PŽû@±š¾ØˆÅ/Û6 aë„éaÖɼ§â%gLõ»lÞBóM½y輆ztÕ1©+|ë1–A,¬N\^­ø|*û\ÂŽÝáÏÐÂEWC‚°AîT…ÕÐDkO†¥ØèSåÕЂÿ:#<ä6¾½áÄŸ³íYEä¸ªì¥ KöÛwº}V·tj+€ \ìdÈŒ“®›ß§…q¸ U&º}|E!uÅãØ%ô¶]²c}|/šÈ¨“"³0ÜÄ›v®Mñ¬±\Èp1΂ԘÈ,9ò-°ZÊœQŽ¡ê,,WtéÝH­³È°mÐ 4OY„ðôí )ÂÇŠäQz{U•U¹±ÊÞÎæ¼qU™ÃÑ6>¼ŒD_)‰VºO“YOGãÙ㘠XÒÕ…u³ÕœãÍ2kúŸrÐx»|¬y)›v䎥uDú“÷ã\2%ùJaû°†9€Å¥ x™’Z»í¦ã–@u\†¾‰^Š69‰ªŒ\¤: ?RTSÆ%ÛІ¦Zµb [ŸPôí¸×…CÝö@mË3 ÒKí"ßÙ"AÈu¹ð'äx±`ÙÍ/À[†­…3{…žK:¬ÀÊÉ;,rR‹€Rd<Œ"ƒXÐzñ³Û, […YmE^xM­æzcÞÈz«»œÂÕJ§ÀQ2Ó­‡²*§"TüiÏ\‹¤/1rdzQ­1ŧª{på¨ã‹Q_'¦Ptì.ª5‚ï;Â=¶uÑ·»ó©¨÷ÐÎ'#Bì­Í4eK0uÝáÁÁ« ¼w"¹ø—ƧRàéoM€³ Z2êÆ$õ˜ŽF¢ßÂä*Î-<îqhIÇ2ÓhÛùžBìs¬I‹"óŽÍÑG*„à Tú&•[;Ǩúìi§Í×¹åü}½ØÌ {Q|Ô¬Ó±ã&åþ`F! ÑAôžn® |RÃòHz»…Ç¿ùÚÖ´ßïžá…—òŠGn|×è{“hÖtùõX|=H/`®IŒåsq%ìÞò´C¡Å ¤Ì£X›Àµ›ìú#v¼÷ŒÙ—l©én±^XŽ'/MÈV!õii L°¸×Ãsb왌0#X“Þý‚WÓìÐäAŽ=¨a‰¨ç”á£ÁÄ©a¿”÷Æöèü•¾©¸cìÜ #A…8˜×¹BFý%ÈÈ^lòëãf¶Þx«_V›•Ðzø|y·øüîÖ°tâËEþŒ^…éð.¯!Ÿ:tÔŸ„ÕáÎHô6w»úoë±|¹ì+Qùñ{åÙ© öõ'\t€»oÊv‹<²@P8ûÜ‚9ÕR‹Û÷娥›”5Íj2£H6zlŠrÇtqNúŠ4‘£¾¡\_>ßJ6!HBz†Ê]¿è{ºÄ<ÖL;2´ìYÒf˜·}dƒølPGx!i/çÛ7#úfÄfxX-Br„ ÈS¨@Þmä.ÇÜev^ñ³æãq-á3SÿjV ÉÍ")-]œšœ}QÓÜÁD!ì5÷¬¿¥Pl_@ô9©g¾ü?1‹©Ñ8ö5Gz}KwÔížšÖíî‘Ý·ß{³—àŠÃØö$¶CÂV-y’™þžR*HP-ië)jÖh=x_Ž‚ÖèšÅrd͵{²çT+t·=Ö1åKít¾K¶¹×‰Ä°”sá–ð“mŸÙ[‚R+äBÊù¹CÙRn ycf;vˆù ZÖ†sØÝÛ&FBÊý[îQ˜ücÅÒæ«î‘%"í=0èû¶Èƒ€Ùëáô¤gËÖV@Ríûo^2›žœ ¯‹Fϧò‚U ŸcßÚqŽmÑöü}ù±mÏÑ8÷%Î#¹–_ëµ|tú2ÒÛHªÛúr‰ñø¸¶¾½Ýë[àú{³mOxDÉX_»µÐ>¢ú¶º‘{{Á¿T=.=s¸ß°¢¶â‚ zjé²NñéS¹PƒßÍNwýÕss²ÜüÐ¦Žž–uµ›µ%+æšÏOŸðiéE«G¥ãÀÒ¥¶âïê­sŒUØw ^–w{õþf4öf‚›éÁα®Þ¾žÈ÷¼±ÆñšÿK™½’ÎÐ0=ùÔŸ…–ªtµ¯9ÆþÃéàV¿÷_h !4ØÏ$'N9¢ÊÛSpÖìúô£z/Ÿžèó^½$hJâÊoßàé›.¶ØJÁ;gæäP\¿ÇHÐõ/Nûcå®Jð¶D¤v œâvq>þúøCñŽþ; ÛÓ'­®~ Ey sË E–/®ð<(fñ¦2ÊAE:XŒLbçÜXÅ/RÕ#”Ò`FÿÁ¹÷ÚnŽqâŸM $ ®7iž™¿éŽ·…oµð©ÏÀƒbYùäòv|3“/T»ÂÓ y¸~½°GWãú0¡5µR|xVxñ˜y©Çoø¾u ® ÛãÁvަ8´«IŠÄcöœ¹y]Fc!J5NßFoÉ¼Šš’_ba-+èùîÌpø®Ü¹6¡g´Ô†¬»M/Éc¤ñï«çÐzÂ]L:Á Χƒ‹Ñ»†]àÛ7šjý©…/¢ÓÁ5ÞÓl¾Fq¤Ï"M6y´ Eã)L³(Y¡£Û„–Ø>ïWÑSOƒÍÂÕ*̲0ô̳QG÷IºŠ1 ÓÏa>{ârð«˜æ‹Ž'!Œ’M¬r1Yey”or0H€&.ƒF b1 î<¬â&š‡‘ÉŸ%ˆ«yžÜ…©8êz¢{zêól«ùy)‚Ü,ÃUž% Âz3áþ¡?D Ý—Á\oÒu’…øçø9SP,Âc®aDꘇ$YþušÒŠãɵùr'F(´H¾BêÖ‘S~±…”Ô4ñ=²úš%¹ ÆÇŠÙo7)A>sºÄÛ([¬áJ¹PzèçÜz*ìï7èT€ _ñ<‰—Jz£Õ<å‘‚u™Kþ€þ!ÙþÐôWä\¼­ï‹ Mž€¬°nCPà.:bÐîé‰ßƒø!!ºÒð)EÁò’#Õ’{C0D´>Úë$UcñÔÏr™uÔZŒ.@…ô=qÚ;mŸöÕ’ƒÁó·"bc+n;bȈõšâ‚¼]˜FÐ B¡ˆ*\5Š¢ˆ"8â C£ ãOø'Çmÿä¤óÇxÈ/÷R¥.Ýî­NYÛM·¸¨ðóÐàŽ¾õØÿßÙG<›Á³*Çm›£Y1©iUî_ç }QFNÔü´ÊYþpôd°Ãv%ä¼ôœçŽNÖy´Œþiáü‘7C\H×j-qXðL™lÄ1†ÐÀ¢ˆ(ò !|=½¤€¦‹|Á»ö1'8ŽÖÀ=ú6RÌSE¿½›LÑ Æ²zämßöí9ÿ_yc…Õºì„ã€~kà<ÃQ™# \ÝÔ³¶,òö ð»|›lùV+=üNs±©s Þè\‘þ®Šô[(oÀè˜r å¼S ý°Lz‹C¶Á«ø00\ðwqaPŃ!ñ`¸ÃJ û¿‡Â¹43;(済ær„ÑÃãý¸Ù¹»žÄÖe&”]C|Hau¢]wÖJ«•{ò†ÈQÞÉa£²Ä^!a”‚e ‹2 Ws6_ X ²¥^wü>ê…9þ¢õ«…ÈÂMA¡¸´ÉðÊ­'¼•@4¦Ã^¥Ã2Ö}?´{Á£jy(É<éTlE’kï¢l*&©1‰ÙüõÈB xöVþá.ÊA)ƒ Gje%hJxè 'Èß—hÛ~bTˆúÔ Ü½µ¦7ÄXLÿï¾~mOÔ¦ì÷¶ ùÔö ,±-ò¦ŽWP\ªFøðÕÑI:d/¬ Ȇ¿Tbeotàƒ#à ,*eŽ@»¦e÷[KÊuÄxƒøØ‹F¦‚¨ã`®]ÄüKR^›‹D¹£< ”$#w剨C=E!2€`_L<;%‹ºddE,AÀ"ƒ½>æÕ¬¬=Øn†)»q‰¬-ª³n™Ïg~¹ƒ¶×';tWžu‡Ëgtë‹ì#Ÿ’U\XÅ÷ZIÄDå;{¶1ÉÕ4ÐÝÓê·¥5š³=8yPn*ùø¤—ðÅ:°ì“/À”T §3J”¦ØÖh;Ë :b íQKã1ñ +ÂèœíØ,ïm»ˆÜ䊭RjqËTÿÞLgîÁo6^޾Ñx!àŸâm ˆÈ UÐpâ»6G{n»n ùUî6r< ù…í•IÑeV­êc–ݳô‹1KJ YÙ4+iö3’)4›ë½n&§õÝâµMø%qO¸g&Ã\ä[q°Œç‡Pbð´~F=ÿ¿D=1ç£(mEj—wIÿ:ŒwÜOÚ4l·ÓYxåFÐ( ¥Sz. Ž(¤/øm»$ãÕ6’#JDŠìM•PôÒ?cIÑf§³ 6 •­¦™‘ËâIzpÎ\ Ÿ¸I"Š .M'оûÐ)øZI'/.§uqTõQQb^@šI VRWIÑ>ô±ápu¢KŸ—„ÚYiË À-;îèÍ K.¯&9“ß:âozgRË™ ¦°‹w«6²—ˆ1Úðïoƒ›Ë}ÃÇ¿^Òîõë5ב˜ *ïmSÞܦ³-7—Æ%©ßuúÝ`úAã¶8å}qÊfE§o¨è§~¼ÛɇÙ;S#…ï#÷ð½z²°Ì쮊Ž?*’ýmESÀ"ú¿'ÿïW@ž¬rp#²h^5àêÍäòvbú†@Z"¬ko(x/¹+¡ª”‹“õl“,þîqz!‘ÃGrËþQ±{Ušhž¨h¾~r‰±ÃŠðþT—sIúÑn‚†í”74çwÂü¶§ZcÓ³q üàp¿Œöÿá~YÀuê¼wXp‘¢LDð×¼05uÝFœBþÜÒ•:@òÛ»‚tè8Ñ4hO:¶)—¹ Šq©,YéÓhþ™B§à2i¶[¤1+RæUš©D%&(i‡Âºp Õ¬Z•y¤?í ¸ÑÔÖŽÙ VµÚ(K]›îÀªÔÄ%XCw¬Jx™BÇå8DÛsjÕ°ýbëÏZ% W…3ô#†`¾ºUЋhý6÷KÑ8@tdÊSþÕµ! ¤U‹×9ØjõE³ÅcµZ¾‰rÖôr M†¥&Å@ŠÙ‹Ý@^–ArH9¨akÕ"¹†¶@Û.†•ïhþCØ`aN«ñ.$°Ñ9f ¿5ºð—´âÛGͦUð·0\ƒGú±ß8PEà|äQ¼Í!—ÐIw ïNš*8AÑ΃®iXÄh¬³Kç(Ùdbà¬èY¼Ë«· e¬bUó1ó OQxY²l¯W3Uˆ²Å›1¢.76°º’…ÆðŠw‰²Óv 4æ8ïRß”MÇöm%ÔÁÎvdUC íÛIG.êÏd̬•®Q?Tßl ?ªš !7û3e3;8ÀÀ /IÖfQ'Mº]°‰®!…ÐÈà_ N¼;ØoÿîV„ «^SN©})ñ¤·¸ì….¹¼¯L@´D^Î ½hR”@ÞwZ4˜‹«E.À1cŒ³g3­›ükU ˆð@<€i“ý þy•|1§óJw°Zù©BpÙå@YX”¸±üí!o´s^*V^jVvw°_¤) :I0ûB8úˆ×°ù¯ûßÊŸ:ì~ƒ¶Q—XUáïì¯+Ô¬œí~+²’fîÉÏ}—ßBÿ[s´`˜Õb¥ƒ!e÷G°;…&k¨ ¹ }²è^Ü·÷ÿ 1ŸûÙݤr…Ph/µL¾¹U{âí69…X½Ö)‚jVb8Ë6K®Šµ+*AÌfRŠÀFÅКÎmw`âXµh`wA' ø:²ð” 8ÙwJ°Ãö¥“Ö™Üh¥}Wöå:À•<ÝH$?d¾÷#w H7’xõ`W’BŸ¿¨”®'îƒMœ?›%±}êµ2öI§E]»·<ú¯suÓQIÀªZ5rëÁœÌ6ó9ÆVÑФL<æX§ø ý<ʸs?ˆúYeø™Ò÷äMê uA¥B¶ ‚e…HÝœñ /Â;Sµi‰1Ý‚èX½”HßË&f!lQém´ âøÙ9Že$ŒªŠ$õ(¦Ì‘Á‡·¾ïºõâ ¨ŠX9gä~oâ—ý•©uЉ`“GÒej¹áciSë¼mŸÐÓøè2/X¾,·îÀ¡8¨&—¿v(ÜÍárÅö.ä‚q ü¹}àå4š@œ7ž8,ü<¹_é>Ç2‡æù°tî'Éç¬9Á^s ¶ÌÎ-Wh޳ Šû·Rë¸èÒÏè/E¸XeÌcÊ£r¸æKCršÔƒ¹ <}œÙz?3²á×uÍ£<~îP;ã±Óòþ–ßq·†SÆøß–€¯É¼$W~D>7VЬ\aÓ a{àòé¡ç=d¤rF ×ï$R=¢À©/åLWP±˜ÃP)H&Ìwpž[&p‰p–ÑÃc.è>í”J➎‡î‘–'yæf]¹;庲á”–ÚI—Ü8<^! ‹ê¬¼ôŽ~tEL¯!Ë6øwNKþW.¼ªây¥Á¶dÑí%«³‡‹3«/}BìAqM±öÝc9”* Æ…àÉ Ë¼C5j’ßÝ|¡ôµ¸c×BÚ¸ƒ²Ñ ŸQ—ëò‹o}9·9ç_8„¯³'#U+s'°V­ï<{i@·õ¹jÍ ìdKù½6öh¦âÛ/>9wÛX&¡=^ÑI–dSn“ù²vÖw— ’Š*¢êÌV·UÝŠ\zY® Õ»÷X¨甡 œêÂòC'Û³5’>^nQƒ§_…çÑ< ²xú¾nWÈ9i¢rŽh‹•ÐûÑVB_ÕÍ^«*Ov&VËVžûßbQ”Êe¯ëµ/ûÀ‹=,‹¾rPµë<³ã\W”[m*2äeF0³â$:ªª’ŽO˸ðaÌoî²N¤ ~&l Öë4ù-Aéƒ×‰w¾pñ?2Óg8_¢8÷X æ¢?å>ä2uHc­ÉýÐ׬¡áÑŽ~ 0tËÓ=#ã0¬ÞŠ­í¿9öûž®%<þ›“5|g ãÝ[.=9ËXAŒ£ÈÈ#ùCIÂäk¡à÷yœdTÒŠ‡Oš–ÚÞÁŽ÷Ì1dMºÔïg¨ÔŸ@—2"ûìgê 0!Q]€L‡>ìî *k\·þÛÔºçÀ\Qý<À#+ÚX÷qð ËÁŸÑ*,•’„Ù{÷Ò%—™`•P®V’b«ºAgÝ^X³ºûJ;ìñ͵ªÕK[òú2k÷¼ñwTÏÊÔWÞxgÝq÷Ú³î¿{Ñ}wzwüa8™aY؇ëÛ÷\ß ÃVÂÏg7aÓ'õø_MÏ?âk|G°ÿ{þüô/´¯_Zyquþ2+Û¼R*µ“ Åh }O¤Ë¿$¬ÚéÒ Ÿ·…;ÌaÈÞzo X>¾ùOn!5;)f¹ˆŠ„N¢ñaD:R›± Îh‘šÁd÷Á—cz¹IG/ ÿ±‰RSíN¯„5Þ ÍÔø,=êAÙ^Ç·!žQ§–*/CãýÉ ÑWg?å0ÐβÍÕXòçÀXµiFD€hô«ÝáÞópS#æn:ðý‹ÐÑ9&7ÖµiB@r:ûKG{q³þæRElð µßˆiìUµ•=:jhÉJkß½j†ä”0(¤éðÜ­lmÐ4_1°h­Áh×ñˆ5{¯%œÕÆÒ!ÉFELñ4©WvEÕ%¹Í{+O73’æ„ýªèÒ]*¹ÅŸ¯ ÏlºæIcÖQWÒU™µû‹MhP: ¥`ýB¹€<ô„Ï|Cu+ 6ŽhÇÎÉ*Ä YÅ¢ˆà&«B¯COž9Š0)GàN€7GùÂ;>idåÑV ¤A”†ë4Ì€kº O&y-¸|±alg|Xþ!Fi`’ªM¡‘H 6ã%ghèÊÄ";¯ cž‡sñ÷à«j£¦Áš8k ,ЕkHÛ¬ñÒÔ È¦Òùœ î]§'Ò˜§ø¢!BJMö’vá#¿ÔyXì¾^|wûèè¨=:F•agGýÎ_ãhGWðÿÁ°{ÞOð\uûÇq4_ôâ¨ÿþBýìÀÝ£îüôùbxG½ágxXÞwáýpÐÂ7ôïiû0Ž#§õ:ïã?§Ã‹‹qõ—Ã_wvvÞw>tûÑ`xÑ»øð±É$é§açSwÔ½èG'ÝÑøbxE•««óó£#h ýÕõ·IZ´Çèó°;wúðµü–$uøíðK:¿Ë&ÑÓÃrzŸEéòîñ>›¯¢é<:žt΢›t6Ë£ÕBv¡¦š7’7Qt¾˜Lo§Ð>[.Ëh™­—óèÞþO¶\äðîq>Æ‹èf¶È3xw—­¾dK·—öd],‹U4™.³›Õt1«‡Gšm‡´8¨7“·fÚé俈iÆ0ÃË>>z¼^Ö`öÐh$­šÐ* ξNsh•¯Óù]´ÊòUŽmà"¬2ì&šûõh/j¾­ÿ',g±œdËlB½¨ „íh•«y”ÂÐØatÁ7ý†}ËÏiŽs{\ÉT^×ß$ €Øßóö åF–r?OïÚ_k0¥›e¦–•¼ê|Wþ»*=}¨ïstBqt;ý:¸~¼Ã¥Œ;çƒFT99ïö‡U˜Ôlñ-‚³®Ä(|@Hé_Ô ‹¤Ý ¯Ï:W§G#€•îñqgØé»íÞ«vïCçý°ÝP}ß><ûÜÙŒôŽ/†ç{íQììô}>èu»c§Çht5‚ùÁí:^õ.>EÝqgHýÐ4ÚÇ'C˜EÔ›Î'i4¬Eƒlõ?‹Ù$Æ7Ù2ê×¢÷ËÅ·9ܼY:kÑ Î–Ó›/ÞC‡ð#tçu¸¸˜KþÒY4º™fó›,þQg~7gÙ.óÞ~òÚ뤗~[âÃ0ó¯Ùò¡¯úé¥×‹eºZ,Ÿ¼FƒœÂûÅïÑÛú[ÿt?qtØŽÞ4› íÑàãpp1<0þ2Í£›Å$‹òÅì+Ì2ò§ 8ZÜÂ5º½À¯¦éìU:»Ë®—éô&rÇÈþþHS̱ÜJÜ‚û¨°…*«ø)~úK5jEõ(öxÌØS˜ÉýõtžòÕ½Þ§7¿}K—“èÈÌ„~;†1g©×KåýÑq5ºܰ˜äxØã—Å6çömÍ`ÿXÉaÉÔÛtð« ‘¨2Éæ€uàd¯¡Ï*¾9[>Í_£ÊÎfô5“k¥_´¯°÷9v?%ì0Y<^ϲèúœâ×Ðbïe^t¤GÑá°;àk“Ùù˜§wÙ¯ôÞ»Kß÷:@ :‡„ø+í½“øbïoUýdîwgÈÄ U¥_…,="BDD©o«LŽ*;º¢€ð»"‚ô–é R@úØ–7Ö>y„!Ž0tGè\Âï. *kè ôgV|Øîõ¾›s;eu ÒQô×Ç|5…[øí `˜è>}Ô¥³’èú‰îÝÖt‰GË Ø[:´¤ê¬Èž=?$ïpt˜¶í\€vìWØg¨áÿ8‹yzO· ðPJ Lp³dÚO‹ÇÈ_|N|G“ìŸÇŽ–Y><‚»}œ3ÍÖ¨"Ø‹`™ÜÄ7»Æ €ÓþµÛ÷o¥ZÀãý5 lèÎ`0¸¥ø“Óɺ wró¸Ä9D_ÓÙc¦¦´${Èàø!¸„¯érš.ðôœAh„t¹„ã¿YÌWétΣi|hòa1'F‡±êG¿ê¬¬àF¦_ mi¤­ºÎ7<^g{ÕÆ¤ÑÃb k‡g R3søyà,½z1é^àa ©wpF¼„Gdºv`6÷óé ðZЬj–—ÊàZœ› wêúDŽ7ÆI†{Y-f€â‘&¯ž€ŒÞ§«åôw`õ–7À¦Â7+Q>ýŸ¬ŒuˆˆÿÚs?]åÄ`XTi¾˜»D‰©O-ØQ?ºF•pÂ*šeÈ6겑tÓñ¢›ÓÏ€jýÝ •e´'G¨L¯iGhÆðõŒ`!8"säzò¨»8[;Ï?UÍW÷»dñZð¡á©¤7puàÃ쉚~›æ_Ü“Z-‚ýàÑ{MIÂv’\ðöÏ÷z±úå0M`êJÎ$Êf¹iþj߆OLAƒ+-$˜‡Å’ø÷o_ÒUxC&ŸLõJðlïó)v±"BEÙnL€•˜d4ì¨ì[´Jô–ý¾RxþWüGšµa‚p€ßËßän™•U“#•ã$”Q‚pì)4¦vÂÑxŽ(Horƒþ|œ©±`Tš›ÚêÂ< ϵëÏuýVoœkwí\»Ö\‰Ö#©W ø¶Yæ½Å‹¥æþ.X®"êœ*Þ…%Àu$J\‡g{­¾eÙŸZ’D VFiï`Lš®Þ0Iø ²%ÕÎeIðû÷’{¼„kI~eñÀbÌì©Ê Kg7ˆwàŽœ¦7‹ëiê³áòªÀö!‘·¨V5š¤«ö÷+Ê,½áöŸ4Øãל ÄÊÚqKœÅ?gípm×.×ëÎOÏezK]äÙ ,L=R%me`°|x\> ΃yÑy2e#!.Ø‹?0í ]³ìvEbÉ@ê@S&‚CˆÛl ®—¼2ð„€~¦È#(¦/†ÓÊ8¾Š¯þREš‚KCUò/á M—¤„!Æ .Ü’îJMfJS¢Y†Bƒ£îéw¸¡(h (÷KÝ%ÅRÇÛ°”¡‡7«FM må’ÑÈ|aõlCž…¡ f„"5_í‡4GþkòxÿÄkÇÃ*–¤ ›š•2[)„þ±Ö¤•qZ¤v›ÎQöçýg‘âDà¯Óì›pŒ°G"Ž .ÏñÖ]+IâIú·,é#qÆ.ˆ•‚$<ÀÝ}‰n#hØwôZá(°\Ð|¸¢]{ú ¾eÅd}B^>§Gøgþ…TtdSBGØÍjj +ºƒyÌ„'`£jŠ$|åîò/‹ÇÙ„ù—y>…)ÎW¬Y!õžÂ 2°ES‚‘;Vãr1þò¤zÅnrدüö ÖJE]Ðza&'‹ol7 a†WSch££ýmNj1¸÷éøªÏG¥/éW÷üX;‰L×ÞÀg¦ùù$p®°ø¸@¨Ž˜ðppyo–ÓkÆÝÌij’±&²p ¨Eì ˜[âYt»\ÜGcˆë'­DH¶%A¢ÑÝ,²ÛÛé ¡.™åÀ’/YÁ–#ÛÈŽ¢¿ ])þ‚ƒ*‰îQj"& €â1$ßátVKEÜïQË1ÀS\.~ŸÞ#ñµà¹˜óžõzJùôz=Ô6ˢ߲ìAdëf øZ üžÃ\Ãé/Á¢|öûP‡ÕÄÝùÀCX¿è+4ÉýÿL£i©D¤~ø¨ÝÑà$@MǘœÝ|aþû`!H˜YnºA|’§7 Ç ¨kèõfEF¾éÝ\ŒÆŒ!Ÿ¬àHVpȳ™˜â6Hq:Zå=f&™¶ƒ¥bk.ÈbóÔÿWœL¾®y8"„3!ÉàZɃԂÍf´‘ɦ]–E ƼE†„L8œ2ü=~IîF¶xÌe÷H¨¢.®³›u+°>Bž€$Öå{ Ò€ÐÖêëÇE®9"þõ5æÊŽ×í@²Ú‹>Ÿ´ÇÀHFGš¾N:Ñqw8³9 ¿§uíxæ‚ÞN—@®‘X©) ‹ôç·ÄJ²¢“&·J—+²d ß8Gq‹´ò÷5tXH'ZZbý¿ž=ÂX±)Âé¯z,ÝÃÚGu&Ò>Â@ÅDT\Z\¥ëù°ˆamf®ù39âéÿh‹/~+3צx„g[¶ŒÒkÄ*¨F&Ýq:_Ùídc˜Í­©=6¥”,ÇÑåX¢¢1ÊËrìÁ.*®ñððno§7n¿EJ6c¬:]¹6¯M&v«¡1NñÝÒ YšÂ-ÜÀ"Õ~ÁB¥%ÈÆ¾¾FP«¹ëjζun³›·u´1€‰Ôz†'ƒ‘ NGo,N<$ðÁ.ל™…à—Ëœ’c÷¸Ð9ãÔ6­hµ»¨ŒZ ûbÖQ™š­fÚÑ!m¬Ò¸©cg'­` Œ(mN®Öäð„dKæÅeŽéºà6hïªÃSâ¼àò-ˆuY,µ™ Næa+½,ºQbòŒ* ž¸­E8z¨EÉAÓñlè/VÙ¯,lLÑ „¬ðJ°)#ÔÉâÛu9{ÈÑÃ0×Î,ˆyÀ£^,G¬Q1¡aQXÛët·¢Kgã(­ngé—tö Õì¸H¢èEf_‰¹VÑxÀ#7A‡†ùú µ&S[ó\Éâi!Ïä‰;¥˜A¾"iüÃ"ÍïÙ»8ŠC½·¢½{OeFÒ 1Ü©@…8ëY{ˆ›X!áElü''j9ˆéÐCÙs:¬¾oè´h>)O–‹™’+Šæòt h&£1÷¬²hDo¾Ë<ò|£ˆš›Õ Ž˜;§]Ã;õ¤´_¶—…Ü\Ü#×—a² G‘;æ•ÁlØßƒo îLR¯('Ÿ XñjÏêEÁ{dMYS«@=WcâHʨ¹ …cÛ7Æ êjÞÙh¾Oâ àQ›B3ò5ÞH1X§+½&¢ ¶«}èÔ,ÍJ±Åܱ§€vœ°ô÷•qAoŸωªqP¯Œ·ƒ7ˆè9eœ_ÈÝÁõr(L²Æß¢3S/!_Ú<ƒ•œïµñ=ë .± “_ãßQ¾ÊB/ëV­5â…UºñWï‘ȇ fz·ôÑ#\1~À›I¦oêºE7+¥”N€AÈЀN®V'jUû­ÆUöõ‹>¨~îñÏ0óï§«•öc‘×X;hM¾²|î쾫³·GTôÚÌÅô´"'œÈøâF>iS!ô‰7ÕA7_¦“?ˆyŒ-nžØ¼¤|-bÛ(np>¡r·=uÂ$«Ü.<"G$Ä…äQÅíÀ~bO89T£WqÄ.)ün\\T‡úR¬pªŠJ*¡]D6íæf±œèù£¾Äá¢Óé½-FÏŒ!ÏU¹ÛBWÄDg@Zs‘#íe®xfË숤“[òÚvnIܹr}r‘¤Àù óã_\㊤y¡‡%:†p[á‹òÇ銼JD siºïŸ:é^B|V.Øà•£1Ñ?ÝÞQ9–•³³¬È¹A‹,Qó îGž.Ÿbä$WË) bOÇø^ØhûrOð˜ôT÷šÊé8âQ)‰—•u@ì‰ÄXÁY9ž•ò\ƃXk P/—‹1iºzŠx§D­`Ó¥{بÕv/ŒbE‚¿ÎžŽË[CÅ34ÌkuÊÈ$‰W'ó-Uá=Š"¿›^V‹ÙÄ!Kk' '(3Q¬>+¿çO„ —Ù;¦éDØj˱©Zô‘ÙúÆ6’êõ|È×]¼ùX¾w ‰#ì+‰‹¸]%ÛÞŽÚØêv¢ êRµ¨Ù¨#6$到Ád•&ÎççöÁàœ4AX¡‚Ó&(-(R%²ô6Z«âvÄ*ÏÃÔ9%þþˆñÀ\ "ÈɨC¸H'"Nf P&À® ÂýãLd®äÄ„‡?×í$îÂpV´‰†#Ïæ¬°ÑçD0#JT£:º-ø*ê É5u×ή¾J4~ß ®? ¹ú˜xEÝœAÒ Ò’Ý*§9µZÀ‰ò ³GóRkï5_Àc$ •à†Ð%^÷°_f„2]&a2,¢²`ƃÔ5ž½¿é Üôhźj!ÖÔ· íeþ¹SÏ>ˆ›RðÙD×P èZX_¢b"VŠd r‘¥{TûXˆ‰›/2Àß§×Ó KqT Eös‰œãq¨ ŒÅœ3bCÀÐ^Š gpÑìù®f½D4Ê ‚^,N! £úÄÓ¦ 8›\Qî0ÉæaD$„¨}¾ÆŠuQ>W¼§e+Dî–èµ?tQ@~Îg]Ó€`öÈõ»pxG—o$Y2û=À@úT|˃ËDÂÆp!RD(8¢'(aŠšåñÝ-À121œqƒB½øã±J¹às‹—¸Ë (ê”õ•Žq@o0’ݲ-a¿“èuìk#×ø†™'"ñäå3-,Åð_²ñ|Mµ9nöº)G W[nØ$t/ešGnΚò3«nuµö9ö«¢¼iœƒÜxc¨u"­ƒ º tpóÉ,ÛbûýyˆÌM›aíp,lá+€¡RFÊl¥Òy-3¢„gŠ¢ýqFÛL.H~#€i4„g+l¤2kºŒœðfȈªan5oKãïŠR‰xBœ-ó¯Qþl–ØfÅ,ŽÛJà¿ÊĬ„›ä÷A»WÐ:OÔ²k­òÂ7´ºN±DJ°`S—êËÄçhûq„}>(%.´¬Åï‚=H…;'v©Æ—âíŸÚjºM¼ì?€ xH¶ VTq¼žr?”‘¨;k™×ZríÏ‚„¢pà¬ÆL•Pgѱ[©« X3h/3*HƒxYCà&‹V?˜‘ò|@8OçéìIIÊñf´_–ÕTK¤8+t¥°|†£Ù4_z¬f¶ËñlóÇ{ØÛ-Œl°sƒc GÒž‰ÈÂèx7¸µKŽ0%çVØ‹ÕÂTBwPQÐ9”NË0vi Q¥kç–ÙŒï–PUÿW<`{è¯Ó÷ÎôcþH§Q¶AÌDÃÞ,ò‰n€:ä€ñi{L€Ñ×E‡æ<€h÷ø k±%Ð Þò©­ž4Ä‘ÁÆÞø½âm—Ùº  ðÉd%Ü­¿š[â¯æfüe<ô•«€Ðgm{à°Íq­E!\öú‡q™mé_-Çkzk»¹Ê}|f‡Ýæ¢ QÑÃÓ ¦*r =r–÷¬ R†'D9¹r¢2`>)½À$¨‘Vgš{ºšXÑòÔw.¹~7ÄÁn.3r‹ËܲmE~Ÿö˜ðô}Í9þÞŒ@Ä\,Wx!´3fD‘ÙïÙ â%êu¨Ù²¦ÜsØŸdàt:)èþóœ]£ó^TA=[²ÓæùǨòøð€áð “Ñ£(÷aQ =ÎÉY>®*ÿ]%&¤Îå´ö`àÚ,«Eÿͦ»0t@Q—‰ddÚJâF —:Æ`D\ÿ#@ÚyOÍYy+^û%Š–I-h…½Ú&„[+#>2pµ~7Z¡~|ªöN¼Î²ßofÒ½­¥¾–îG¤½©ì& ^@먄QD/–¿åÊO2­~`çvv…#I’– Þã¤J>‡s ÁÝ\­\…=¦1í¹#Fê#ä˜RáÌM¨¡v-ȦzÖäÐyEóAÇ dV>ÙbÅU…B½Áë1«½ëú§›92ÆÃeø¾(&[Ri]®9Ùyô§ \`:!†uj C+ã3ŠoéL;‡àO´ÇÉ!³±I`ùY¨¤%t|œ$¤(ÆÊ4­§*£7|;!úõZ¥ûØvôÛà‰Êy¯j!‹ÊùÇp |Yø5E,”>­" Zç!Õ˜ýLžù¤ýoö‹­WEÏ<ñ;ÅTtP»rÄ)LžôYÓÿÁ˜ýEÈœ•~]L9†vº$TPûÝݾCîöŽDä”r¯M.È߿ɴ‚âÆ¨ùnÇÚ½Ù’µ{³µhzrÞ¾Üæt‡rrÐ/5 Þ[<=;-@j³J‹``$X¸p‚ØžU™9*û2í)¼ÁôæqF©–í1Ÿ"ך  ù&]³ÏºézQd l 6rÙìA¬?Ù×)“<Ë"R±š(0cÑ5R&|ѹ)à*óìx&`½Ý°ÞnXõíÁŠô~Ð Tï¨Ð¥\H,)v0 §“#3Ò‹©Ö×OF_$–o…]$l„ãÆ/†GA,0E=žväkÖ˜¥#×lnXc ”½¨©éH—†Sb_Ÿ q4æÞ n„ãTDüŸþ†™4ôm6„Ük^Ð…£¥.´L˜Cå…òûfm3ªÝÒáj;àms;`{·%°½ÛØd[S)8iº¤ŽPg£vú•82ËÁªD™¥uЫããJGv`öÒ˜/æóì.˜™¥ð??,ò)¥Cˆ•»ƒÂ?¹¸‘„W6ì²<%‘æ«/Ë,Óño´˜@Ð}ú5ÎPókXj«Ø9nGùƒ xó¡Oò¡Uº×”E°IáºÍÒ«Áƒep“Y»„ŽStÈ+Iàw´ïvDÖ5µ ÁOÃ×ìDâ™Á>8´4f"¨+ßå6"â¡ð¡ë,g×¶"a1¬½VÎ6èîÎñb1šNßô9“â"µâZ(e Ø¿Ìð,:RêS\<7Šcòõ¤Y[¸vè{Gbå0ØàõÚ ðƒ‰óeu¤E…@Y ¿†îcÛ.öý.öÍ4<$b½‰P;cÆwVÇ€¥$¦P'̺bìù%£1MÈ£× ”È?=Ô‹{?בbWÕyfáU¶ïGSК ÉÔw»¸À]ò¿ªÀ% ’ïp½@m¶ãíÚîÆíѸ#¸EÑ{ãçôÕu¢hv7×#;äŸ#ê˹¤G0‚}xôÈF&ŠII¦A5$k­×…ܾ§ôØ× !¿ å’ÕPœÑç>ã%*\•Q, Jë0]ÐöQsQTÁÑ"š 4"À%\ýß¾)%¬‘XL3j‘ê b4CYx¬d ^Ø›’~‚„õ…uÁœb}3ºþò'&‚y5Lÿ8í W¶k‚ñ%×q5¿µƒËBzåJ¾”è¤XÌÜÜËô‘›XVD…jwD '‰3”†bb’ª„ˆX[D.ô ¨[ÙNˆúlAJ}Â|ÄŸhúcvX!"¿fADDŽ¢^/`/ï$‰d}øNªaù$–XoœD‰ìQ(ŽQ¿¬Ã®˜ûäÁ’ùœ;¥‡²5š‚lÒ‡t¬Î~GŸ½éª¨?À—òK6éF-¤V­mMÂzÝ£ #ù3ˆ_¶ègÏíg@7A€úFœ|°ˆ¦ØŒB[ƒI=± Òdpcn‹¬G‘ ‘Çbç÷›ìa¥ÑƒJa t³^ø^aÖ‚+u*ͫŰA'X° ~'˜3¾º%ÙšU:hÍÖ†0QI¢Ì•ÞÚȧAøÔa¬]«dTð»§W!«M (£<‡q—Í‹|«æDZö󤵯©S[+åG˜}üÅí¹[¥2^nBŽb³¤{F‘¤Pñ’Õt,Ök÷æ²t,ôÒˆÙ7!'QÅ™XŽ¡~ ö”#èyû²GgçÌì ˜v½n ΂AF´ñÅ»õ@±f͘rä!U$ù’¨·Tú|G ¥Ndb`Uc?xQØžéRõ' Š\|Ú‹ nl¢ÒYÆ_•ÑI‡ ržŒfrõT¨?R?é´3™3­e çÝ~¥S«Â=;Àfn|NJcÆ)‹%œ„ƒª“½ Ó¯[r†a•1ìi;³óEö53žû_•t ¸È¤–*žˆ;aòB@û$ºÉàH8 ÕZÀEž„Š” yàŽl¥W$Zº‚x[l~0)>fÖW¼‹Íçp†…9y€Uù~~¾i ]úýD¶Ç²*qœ‚IˆZè$ÍóÅÍ”<^<Á†§6(N‰ã#Œ± ѯŽâÂÈ+R²ºÍÉ÷þ) ã¦f¤,ôb9Õ·2$N{À\vƒèžÒ¢´X·Ã8™KÎÛ”vÒóžj+¶“ò­ÛJR |ÜÃHpò™»å2´•–I³è0+f$'™£ä#d°“·ÏI;:(bS“.øqŽ–e;%fU%A’ÄGÅEØõBrTÆ»—° ÷‹ÀžÓýª gX K´ÁÛa®¦8)C,ù •má]¦Ú弬< ¾p-ˆÙsN)f…‰IHK·µˆ4ezä—-ZK¢»äÔ—#٠ˆDpf¦·†Q©…fU¨YÎ5IfOQ°„Jp§g±Ýq•úÚù§³ÁÛn®öõºZzý–^?¢µ¥× ¨ny)?¤¿¥×+qùdž¡É-çþÝ—ÖÕ®½p[ë~Ÿ=ìšmßNO\ÚnºË¶Ò¿ 1TJ¸1œÓÓ@µœci|«æ“€¦_²Çå4_¬2Ò°ª"1$=kT&\O*;‡-ov´@i£b ÔÇáÂ’©½ 9•ý°Rî²ßGA%Ÿo=tgQ©£É“=›–«XŸ€‹Mä+]B‘g׊¢¦šÎ)Güdñá+¥úHš›]è#)î04>ÂÛS¾ìÀ$ÌFä…WÉÂOaÔ×?´ìS2~×¢OJífCƒ>[ÚÔd 9Qyi)>W£6In¡–£‰…Ô€ðê_Œ;¿:Ž†ÏƒdR ‹X€69Îîã ìÍ7„K)üF7óZ4 Óî.Žq£)E%J͘5aq{«íNœñÙè.C¦î±ô}–Îs»z€ÍãH=‘ðAM1áYÖèÀêt=L1þ|<À„-]`˜’‹N8ûìgh![œ¶î¶ú ¹Öù§¶³Si¼Ú/‚mgÐ回Sšb–FøŠGX*ãZ"ËUž-Ò'û)á­ä·l!A`…{ô9çv‚×íœä…T„&Šì‹¯­p’­87<Ð5 ÑÉN!b Š˜³ò|D®C!1M¼ %oEÎ×I)ÃýäÕBbFóâÈ' tâ£Úé /ú±ðôN¯¶¿/å#‚,-î0{·«æ'Böb«8=É6š~šZ=)Ó¶‡uTÆA‰ìA-­¼Çl©‚/ßðÅ7?U¿O4ƒIj…ê6*Í÷÷¥ÍéöM¤ÉÉöMH f$UQˆê¾Àr¿`6I’D©nêkmpôIê€WÈ.«.þã\Ó™¸Ü|™R~q$8T×4prh š|÷€—EîdšÿÆ]-^BÙð½¤ìGÜP!¥YÓ-§È‹Ì0Ã|ñàTšUh^*¤~yí·  áÚ阷¬ ò¼ˆKž®ðÂÆ=NûPè]í0d–bü³¥rƯ u)t®m7õ8;þÑ:˜«-°èŤå#ˆíU`©‘i$‘%ÒÕÛRªú§Èìì1·ëÔªõ´x(ÆX®ü ‹üç­íØRÇD8!¯ñ*,3"åÑûö¨ƒjl´ ðT8rz÷ ~SÝA+Ñn´¿Ó3Z†Ãå%îº5­üæïÈ¢÷¹Xœ@-˜;á¶±3£wëçÐ.î¨â¾a³&Ð:‰@,™Ì%m“¯nQGR»¨cx­r—‘C>»f Ò2‘ F!ÑÔiœD'‰ŸÜ¼D×£tÖìŽÀU$åÐnBǶ­Õ‘^»Qcçÿ…F¯*ª ø*ùÿÖ@$Á¬[³¶šËm†qßy«Ú!ºd’‚ë¦Õk¶i7úÉsžÄ›1W’¿Ø;;Tÿ¿ {Ìãõv÷«;ÒIÿõ:ýÏß-t0&7)Ågç6â&ËV\- ½_Kï_¬]ûa!ìW<]ãÑY$ûÎ Î7ZMÕÖ'u\wòÖÞóòx¡Ûº¿ë®oñù®MM±²íi¢?h°ÄÓ&’èw‚PÑ SÄîö±»"¾=Ü‚æTGkÑ{!’gžrƒðÁ°{ àÐ(B`¡¹k¤ cÈ’õ­]òw`@{¥p废U8ÃlÓ-^ÌnÀOäÛâKuQNoö¨P7žÈÒ=á̹®«É}]==¼jœ¬‰v•%ÅÎÂ[Ìܱ¡Œ]±š¤×^U åÕ.4iù*ê{ÛÃÑëÁ<žW•¢¶¼^ñH¬ÿ åð¼¶âxë ╎(W,ŠçµÞT"¯"ïÿR¯]Fomñ<ìaoOWÀà²¢ØÆ8gªêW¦¼5X…‘$jmªü(‰Ú™Õ´iÙd{tÙcŠ% ´ÝœwìG1±Jꢗpš;þß­pòbR;”$aþ¨P+Ùª”L€â–Þqµä²ÉŲôë+厱?§]ld T P×SÉ ÇI—uq4®±²ZH P«—PUeUK9.S¶¦íôBKpR~Õ$e[I6Óv\ôÀÔ)P]Ó!´\ìjp$ü½Ý'lB¸ÛQ¶RÕpU”'"÷è§DW™1)¤^fëô¢GQnÈÆ²W+œ'–WáÒ}…4®@]ÑTq!‚BœN„_4Yiýì¯*µ+ЇÝYhì „l2@ëb Û[#÷…ThvRfÊÀ"§¶ µÙ‡\èHzIÁ+ÅteZËjøÝ4€…¶Âz¨ÛÍ*Îÿ¤r놶™\>E¸ s$e9æ.$Ž*Óø¿«„4`‚€,0÷µ þ‚«™þ÷ÚSðµ¼®bšn:?®WD8œ?džÈ0ßq¤Zÿ`lÑúºÎ›O¶h|tŽzË£-ôR8ë­Î6èâÝ^|†Ã™F{0ä.fßÅ<ã»áÀl°=4Øø4» ›xâ¶E³)M–rÅ8*vÀ×-×Bw;’ˆ/Ånh¢] ×.v;ÙŠ`[Tº$ìvÉ–ÍNXŽJßîv$(Ñgˉ{Îñ¨²ž(òâù J·¾x×]‰û¾®5à ÌpÛ1 “Æ‘Ë, ;Ÿá‹QûÓp“¾>ŸÅÑ 3Ÿ1Á?á˰Ö,p —QQ†›‡U,H‘zL ö­¤ 4<«bP7Ë%W'*hê¸8žªÜ„þ~Ƈ±æ>Ÿá±‘**Ho2« &“6§Ý‰bt…ÙW;ÜTÌ0æ©tÚ:”šKÑÕA‹äÕ tˆ®Â_h’~âeÏñ>h+¿x¤ÆÐ;‘Ä®K‰7~Ø¿¤ò8'·UbTÅXÀŸÇ#§—év!EŠà%2RMÛŒ+"8¡šS’œNä–ç¶+Æß+,/æúJÐå UDðsêóþñÎIr<˜ð;”NÏÈ:Þbn–‘‰¹Ñ¿ ¢œ¤è…±ûµ .ç Iš0G´âöP*?;PÒW¸¢ü‰ó*d`â°LžŽB=2§ -†»Úѽºå2‡)LD*Q²gÚz!£ÇUåºCxácƒºÇÞm©ºànÑ,y¢¦b‚ÌOL¥_®pºñúýÛD© Y1"£òÇÀ" ÷unñ³•F•ì+*ŒoÚú"WCäJž›´Eö8Z-ä^WEd)p8 \ˆÒÿ”ø‡ª¬.ôˆ­½%ÏXTÕ9uù b4c\:-m>^Ì%ó)r9dzò‰ è |VXÁ F(Š!x–s‹<4áÓZ|¾†W·Ÿœ›’i#ñ(ù•Sll>Ìp—Îô€tÀ®<¢H®ä9œ=ðñžøPA°ž :é)¤œ})î¨æŠNjápX ùÉv )× n(I½¨þE£Ù£_ï¦jBå ˆ<»*%FjŸT Êxœb•#¬ ¹ºb˜oEÅö|y¬ô6s9!ò¥$cV‘ÒÚ¯ÕB¼Ž[¡‡®-±_•ªb,è­ðUpNHWà+{/´²˜‡Œ†SÊáaÈå¸3ì·{˜“n¥D«U·Að,]¯½½hƒ&Õ׃Mû¸5 R BC7ùœ§}$YÌËßa/¾»9`OgË0…­Úõµ~”¼«´SŒè£ D²… «‹r9·Á<´Q˜'ù±ê×#¼érJ©”]fÍp"t5O+oQô»ƒ³”`uQ.0XÒ‹kAóåÌý8¯•4‹þ>¦¤Lý ‚ÂØŸ¾¥-÷¶L»|,ƒßa'®…!(àe·«°hU+L`‰ÜíÞ çÁã—ækÖ]Rwï«<·ðÚ³À5Huz]°Yû¨I‡TF齯öÿQaå;DÀŒh¢³Qo!¤ùÌš³áÐæÐcdDöÅQ^q@‚Ê2aÀæÂU¼9S,;3±Mw*¹€[K ŽŸªÄ6ý9µÕÄaŒž‚w6Â¥> k»ÜsB>Ëiuc$7{>ÚßAßLX yKp²ÏÚ÷ĵc–2’ϸ»–å`<ýø ¼.Òø­4îz­vš;(0A,­¦ËíÊ«ZñÓ¹”Š© ?"§†eS«–R£ F'+®£Z“|a@Ž€%ÓüðÔAǫ̈QÌ¢ªË‡e“Yé4›qúw¹’GÀêr°ї¶°K€}ùYKÈLY8ˆR²9Q ‚ã£êçže%×{Ä=ô“0Ö7ƒÛ’ÁÏîUaû„¤&ÁT1]j–øå«üïËU…²tÚ,[×`aZ´MÂã©Bwé« ÝÔâD•Ü\ÑEYLeZÐÁ±ÅÀX])]í™oU}9!$®ˆíGCÍÀ3a÷‰Öè¯ÃqT}<*QeX™VwpÃP_±³Óˆª‘ ©á%1½ÇBº5¦›šfrG.Yuú‡b»¸­“6ˆ»Ç4‰ô89ºGß ›L™‡\Ü­ãÇðœp%¿¡ƒÿÒÜJˆªlÿ*£ZqwzшJ¢¢´ªLMös©%ú5½·´i.®¥´9Þ:c #‡‡ Úæîd â ½ó#z-G9¬r§ Õ&ýŒÊV,0³q)®$ÆšÁ²Õ6²×VSñ9àú¡À”8h3ŒLÅ.P˜BQ¼øŽÛòç%úK[[Å‘Tâ"@ƒSõH,‰‘0ÍÆ¦¸X‰*)nT®†wÎÒ⫇c—©Æ¬Õð~ø©Ýó˜b§‡#À!ýQ÷¢ò"¢ml*o°i:©Ú È̯‰è úp⚺ýÁDZa ™da/®ÚöK?D62c3§Y+Ý&u'Ó@R”¢uEòàqZ©ø ×S5¹S’i¨/î…Ÿ¹r ÊclÓ¦}˜V½ 0ogãÞêK2,À°gcׯÀ›0´h¶sŠï“I¤²RÜLWŽSÑJz¡Øa¬Íe|b ·E -8BÖ+¢úò)ÁÀu´r¯óaàð¢_«Eï%â&n OKV7/ÍÞ«É#‰´*kˆá”E×LÄv½&kV·éLÝ8ц! a ¡@±o ü™ Ì£ðçxj±Ï­å$ÂlaÎõ«õhüH ·ëéŠÃQ2(¡´TµÙ ¹âµY²…iàb²¥åÁ([g²qO¢{+A¯ø¥Ü(bF~2K·b䚊ÞÜ5K-[-˘cþ-ø¨%ß±YMë 8=åÁ?Ÿ!;†l8²ÈìösC]3hº²+îPUDF´’ë'êù°Ðy u_P;ÊZ&sЬ¬Y|®–gMb¾°«›ÅÄáTiÎ…dµQ¤„\ —ÓáÅÅØö´<Ä)¶ÊÞc7‚]®-UðËh—J Ð2~ïIêS7|M³â`lò´å¼tf7¤v´ºc_Ý`µ'. XQÞ@QrÆæ ’o*iS'u£"¶K‘“æÔÍ–%`mÁÒ¯QÙj$utÁý‰*@EØ“•M**ÖÂ}ì%%}¨v؇±ý\ ѵ{½+ Bƒ^û°Ó~ßëX´øW~ÎÎ\—±$ŽÙaçØ^‡ž§Ë)fÏaœ!è¢aiÐÊ¥#’/v@è)[õ‡ 0ϯ sXœ[HEG×E“¹ÉWAÄšÌè–Âpø8iœÛê ë¢YëÌMéM—ßðÚj`E‡2IL–,S= &Zd+z¯ö¢èô„wu>‚öûQ3*{1>£)Ó-ñ+´ðs6Bù¸:…w…~ám kFÅž“*ÉÞl0—l,Cw¥vš*ÆH5Ôt‰ÐëÌqZ¶¼ÔåcÄ%ca.ÇÈŽHõÛrºâ¢ç,ç`2#‰ùWìäê*-È9k:«¦jV²g‚C¡ØÎ’ƒkÎã|òR(>ÂÔÒ˜‚ Á±N˜¶àÖóŸnMûâ(ó GTE¥«Û»ÄAóbºD‘·Á¸dªœ²$£`kìžÒ=âä¯RÙ¿ÇÍGìF¸gggkPüµIïKHfõ%óróÝ e>Q.„$Ô-—+P)Å)º!œêÂÀ±Êfé ‹QVÊJ«ª€¨c+e¸˜—J«9‘²l9½›¢ë—vRí+J¥ñ¿ªS`ö-UµNIM‡3Ù“âþWáîÑ pÑ^Ô>¦4j÷ùŽ?ûÑñðâ\¦»·æÅHfü…ÓÝLP;½W[ÏÉUrÞ%"¹iQ3$I(ðºŽ/HÔ™1àJ.>‚CM5|®I«N¬‹”RÀT:€t¶®üG]-XÀ4ëªì”žF’gäÍÅÂäÝ"%¿W]ÊÀ°_ÊÅŒì³8öò‘.ž8 Ó-Ióßi¦¹.J3± N¦±¦Ð°¢‚‘ð:™p.FY:VB63€° Œ«mC…¡Ç® ûû騀+ ¸EVÚy#ÈÊè” ´¼+}y?´ÐÀè-ßœà‹Ét[¾«NúÖ;:MŽn£Ã‹óA¯3î}\£! ÈšbÍ™ÌóòÆV0Áé¶EÛãïó*ý-›kó"Èaöä€ïa™5+mÒg×öl^OÚ§daÖxU|™`CÁ'<ŒGã‹AaÊÁ‘4ð‚ Ž÷[V©88¬úi[ö{Šy]t¿%sÙ/› Lÿû¦ Ý™$ÛhOn¾érý„DøøBPa!f!ë µöÎÃNéûZ^«ÛAiX¬¸äì#XXZ!¸3Z¢"F°^V]äXïKJ"¶Ÿ`A+?~HïC3t …k -8ØrÄÈ\õܾ°€”–£lLQxé›Þíõ~lu×µ$³Í]ß“ËNÙo¢ôdBR…M³ÒÙï’Q²X´³\#íiÖë|b¾Ÿ·L_Ü¢"xîk¨râ4]Á.ô}³³ô1ÏY©&ÊÏp/©?=e¥ Ö.Hc vå dÜák±“½¤l΄t©BŠH]`ĽD­Dœ%féçÜPj³•M aÝ6³jr²²¸m2.ëŠr;l©õ6%‚\Î"Wþ%w<®bo;xá¥àô]Û¡yñ ‘¹Ýe¡4e6ãZ-ÞÛ§cèsÅ“Ùô&õø$˜ï“^š5Æà¼Ûo—rK‹3ЪÅ´öö÷Ëè¬&´šk·OƒR%JB ë…×T“eÎE‡ë;œ%)o°1ÖIQU éƒ:ò`%`àÅepI4y…/%•›Îaë¦1r`©:`ÿì8t7=/k…¬#Ëì…úǹRg\<;/Œª¦BéÆUˆ{Ò¼m¡#ŠëHýt°«…bß ^JUÌmPâLça3´Òè:b_I;G KZhár(Ú¨TèÇð¶N¾B}èkKv#¿K®;áéUø·äÄ$Ë#ËËPjA%2ÐQöLíxÛ‘?^sñ%¨±„Vs*Ýãu¦G³ÂíØÍŠ‚ƒy­gÓ|¥ܨ( Ð(ÿ‰*Ü)û ºÑN‰´‚ š êãîçÙýb>½!sUî;ºâ„ûÕ½=­'9‰ÇY$¾»¢³$é`oª|¢üfÈ{Èq?.:¢³ax/Ó(Þœ[¨’M”KÖ-—èJZ©uÝîq¥h1jâ?BvzñZS@ŠéUsŽÀnä0lMq&mÐÛ/p€fßÞ8GuÑ;2§E‡%6Fí£X>šBGåçé±Eþ5ŽÙBð,%›>½»âúXW©)”®¸g±¨¥'l^Yqeõäe›)6C/ýÖ[ŸBhxÜ‚†ŸõC¡XcöGcÒXÁYã‚ÂÚ¸ºf¹¬Ìx?ÖŒÖpFvüÑØë eÂUÍåµqõôOËÇ¡À©`N~Ý¢µU•«¢’Úƒ—úñ±¤|^¬BÍ´Öä±µf$²ÐÆMh:ƒ÷Ãj1©|L[êñÆGÐ ³*^×ÇuÕýš”ljŠñ(w©,ïÁÝüµ;þÚ[|¯k‹ù›­ü³½ ö›ÁÁ'äåÎ1~î’1ïfELGù\T´C͸ J~¼ïì¦.Ìo¾1±‹3z•pÈ0°b® %>Ô/XÃã-VýBâ(©׬ý­·öaaíÃÙµ¿sà¯ßýî»g*—éGÙyP'+þ¹ˆËÊËhᕤÖ5w¦QwæÜ+™ó¦ KÊŽjqæk†vÉÐ`äßV‰›Ã•n@×}ïWÙæ-+}ÝQû®“Îkë4Y–u âò%í;(´‘š­åcB. Êô=¯ÙYvÉ´çHë¤Fc¬#¾ ‚¹õ ¥+¿ÏÔìžql”ß'¥;CÉ_=3±Æ“§o´}>i£ñEttÿ^ôÇÝþÇN4>é¥ãð®RQ°Æ»#U°~ÎHŒûØü¸;MŸ)~€þò.w;ÑÉ­|™M~±ÖÎàdóö’nÅj5Ë(÷G:ªÚ9¹³[(JÃoð£v×.G‹âÕJBI\S9ž³€äŒƒ.&Ÿ©F*2˜†™Òqèä~Æa­ö]AúñC/+J¬Gk.™q«±–•$¸ü&ÁÏ4wÊsC•-ÙU0¯Í<ûÆ>> ,g”ÛþÎÕ\[ïÑ ]ÁÈhUÕ…}S¬ ˆîá$,q¸;·v|zCÜÇdŠ…ñUÚúXÝ‹®þBÊ©l’:Ím/(àdQdz–^8‡ÂÌ®F˜Ãb(éoŽ];EySSŠ#ç±ùñ‹¦V’'¬åžÞbͳd¢u—I:YåB¦ÜÔ˜ûTš4ôœjÞ´R]¾¨ÊžOOܯU†I;²0„Xy‰H3ý’bO†TQŒY´ò ËoÊÁXí(>¥£m@…^޲`/‡Ø•Ó“tñ%U÷ɪA‚ñ¢ìiÆéœP?ñu1ØÆaÙM,5¹BGnåƒe{çØ¾.†wîlè~µª¶[6šjD<‘™É ‚ôted£nBÕ%¬Y 4»»…Q“ï€rªfÿžÄ†øÏ÷·Œ xÞOÉjÃ(Dúp«]˜-¤ªO|·¯³§…2ˆò¥Öü±°²º(ÃÊy2zy¢0q|‰ÌØ*nŠT[•–#bSWÖ” “¥hÁM­ýÖmç Œ¦ã‡Ô¦‘âõylFvf’™=Ÿ&ú°rzºXEËïa5GF3Ó%–t[Ü `MlTôŠÙgrr'VÅJX„J?:wvEcÄ ðýZ+ÂñE¯wñ¹Ûÿµ-"²-Ø»·:¬O¹ Å‘¦]Íî(Ü¥¡›ìˆ)ìÎ7rêviƒöâìÑ,ØD†¬ÅiÝM¨Ž_QÈ]è`-€HC¬óB'†iA'Op—rKà›¥5«—-ƒõPwÅ”ðj&®¾"°³ Õ»·ÈåÝŒ¢Z'Ä¥à tÃDÇ‹ÅC-X>SY,r sâ5¤Ub*ˆÑxІzp]w®%’r5R* 7ˆ¢_S©ƒD5žY;(9ªXG>Y̳Âi4Ÿ Ôk4“E8. (Z— 7 õ¡ƒpÈ‚ƒ!sämB<ˆ%ד¿ØJ46„ºôU,ªÊzÁp(“_IU ­&P¸€ÔE\•‹Cè$Á¦ ý}ˆ«ïxHp×xAQ&–ýKeÜÆ”ÈÂ(ßւ﫹&º ×»4a‘,T‹:†²”ßôb.ºG'ŠøuO²§hû++]=_+ë,x^%×I¦êÿ,tÙžÛbŽžŽ–ºêÆ×ײc–.-›©ñÏÓ6Ë jÓ¥ž±²\æ¾rÍV &6—Q©)0JÂ[žÍnƒ[ae"¥à›\¨\®³ô÷ðRüÝØât‚TÌÛòýØ®BæTA#?7É0§ÂùÃ{å»¶q€BèÈ$‹´ÁàìÖm‘uu¹“œT†zD`Á ‹¥xÙ¢?NÀ·™fê†éFW[…VÝeªÔØëI ÂMX4iÍHki±ÌÁSÎ9aÆD'’0QIJ«¨‡R45Gq–£)Ý/¾¢\/î : í¦ÂU^…©êÙxRÄܯ½öpÒ*ÕiؤÚöd‘q„@öû4 ï\"yœOÿþÀ¸ïbÛ[YÒ,dåW-&8ÏJœè¯ŸLœ?ï~–Ÿ³a¿R¼»ÐHù zή¼²Š¢‹€WDP½Ô©i±ìK®ø®Õ“›òs;¾õ}ßÈoyß³NêÏ<ìà{îæDÛ^â,/x¾kWì./¸Ï]1ËK·,ÉÃTÑÌUÖ™Àǰ NÑ««rmÒ능£”¥ÝS±ˆË'Ò‡9¨¤oéÜlÅM…Æ´£æ§ ©}A¯ü¹>O»¿àÑù)©­¹´»xÅ`ž¢»¦4 ‹-j4I6ÁdßÎUÞ¡œñZXåeЪ,‡*fGLÅM4õ(z Ê<;$£ïâ@Ùç´¿”ýǃÛLÈÍKíÀ” @A¤µ¨b›ºˆJp‘vÀD)‰l ÛÖìRåVV¶ûÛ!×B„Î-E«){)›Ö°Ò–éËtFãäšIXfœjñ:[•ˆs&J]¶ÓJöíÕ 50]{MBº”ÍLðfSÖóM²|Š´y-i]1Ý<¬PµÖÉ7{‰°u>‡™Õ-%é@ÅÐ4Hº­±avÃÆ¡ÐŠØ‹·‘‹/_íoÅôþVH@¬l€ãd¢šå"\DŽýÌ»•)’LÍô ÊÚ¬Dâ÷Hå__>ÎË£=øj§Æ]ÿÖ/ì –?ìw†þ!4¤Ï½ZÒÎ [ýÏbXº ÈÅ©yI ~Åï-#ñ«öì.»^¦Óµ‚ƒÄˆðeÝ‘‹Z´£C"Q°ÞÇÁÿ™ƒM̘¡nDof¥òA5Žú‹åêËÞÉb6ÐIÝCçËI œ¼{ §õ¬àëæÞë·Ôƒ ÎjQ§½‡ù¥ó‡€u¦÷×ÙL‚t½…öu..½먻Løö>‘±lÀgNj{ö ÛÐQ·*Ž:³<û:ÅÍègߢ«Åò7šý;šò>LyP‹ú8eÌDB~þ0ãZt2OîÓeþö ® pÖ°[\ì÷œ¥²s–£Ô¸Õ°Í·ÑÅQGŽF?­Emñðf_2$Ó7œ„Oƒíª“ý$ªà䪼·õ½wÉ|àN4.Î2°·‰f ¨FY+Ìdovct÷FpÙ†íÕËuÛç¸F²-¨›ÛÄ5¼;5$¯ßq{Ë0Òüžuj¦& p¿ˆ7ö¡ñÆ&Ów/€ƒæÿVÏÿ]ÐbÒ¨©»Ë ‰F:uTÜ32>&•I¢žË¯)ñbmUä!RtÄÎ Åðð¤sÆò#PÏÙ1Ðò‰Ó^PºtÌÇYäÈÁ [R§—^—- ·)r‘“GGuµ¿ÅüS£šLßSˆ¹I&~¿ÈW:[JIx£­1Òú3ñ)8Ы&WWƒþgÉ6š;,3)òA9pBèAf¯{Ù®G>Mg4à÷Köy\B:¦E!A‰´9ìÆÃWô§n%Q¾DšI’²t}J8…Ù LWD© ìGV(<Øí¼Š;å’[óÙå–NZ2ðèŽÇÃI—ìÆrÁ»Cd>W P´ˆ+?÷‡çæ€pŒ 9À踙ó—>’óöá‰JþÏ}<¹˜ ‰ÚŒF‘f—áðóÑ«ËQg|Lÿ~ì¿ê^ŽÚŸ˜™{Ho~CìJ©æÍŸK0Ÿ x";²=íÕ3YN¿*/±Æf.8‘zÁшym£&ëªÛD^m@EV.Ø©´BV¡a+ú8W~  ¨”Q|þÛ©§¬u«ÒÜ/&ìå°Xs\ap%j <—^·?:rî‚u·ÒÙÝØù/÷ϺeÁ5€é§0ÐK}í “ºzê!±57áÝîEk@ƒòè–¹³ÛkOÝ‘Ÿ·ÇG¨ê̳{L×£J`å(r`ÅxéÁzö´¿6F½OGÙàT³¡[š8Þȳº‘"®™LìáY{ÇØ5_éл”g/t)ƒû½åÌ­<{é[X4ÞʳŸ+CÛÝ9:Ût+Ëöê»/fðØÏžu¿ºsJÑ@øîqW¸eÕ;wMqánmw·“ ºÎÕ¤Ó¨0Iú}hEšµÇŸ"«øc=) „ƒeD¿ùÅp|¥ÀÅzeè"|Z±rƒ§™Üü¤óסÕü¯C¿„ßmt‚áÃóëlygªÒpÓÞ(Îs–¥ùJX‘ÜÑX­‘N>tŽÛ1þõðÏ{þô~Ô#¾nÜ }xfxq‰Ïøê—NóêïT¸”3êp,¬ªª¿«¿\Á`‡ôçèb ÿÂÍC…ñäšÀ{ز›¨Ç½‹hUÞ÷Ú#ÇÛÍ*_£|¡g,™/y èWü¤¹úeö«acÙ®lh,\f,Ì[,Œ`,{,\eÌ,slI#±0n"ýtúGÑ`xÑ»øð±#ƒ’ý’çÜ=ôº‡Ý1€ÐGÌ>;v»˜}µÒÞ;‰/öþV•¡}÷ãè¢ß‰£^ûœTb {\×ûÜöåY;üN5Ö.õ…ŸÙ¼^ü}j{ÃÏÕ˜î{Ýϧ$/¬ä‡-þ. bU~Xþýð¤=lb€ÈùèÃÎÛº|­³”GT}–J’p%tKc.VþžÅ̺ðŸ3­eÎ16€6hò5½Û0öy‡®ôÎ{­¶íüc«ÆWƒNë ŽøÉÜñîçs~äòbxÔÚ‡w§p‡­&¼œ´GÖkxwÖzƒÿ^ôŽZoc«uÔzãŸ1 Tçw-4ãôúÃN -"½þ)¼Á~Áo¡:»‡Q¢­¤éôÇÛJp¤~÷s+yCÀ›·ø¦ßm%4J¯ÛjÐ ƒQ«‘8íG­-± o5p¼óöe¯ÕÀÏέF“ú„/[ §×ÿ _¾±;Á±á;³wqˆoqÔÞ!~»ãžŒõ[û¼]ý߰ÛÃw§­ý}þõ¤µC÷FÇÇ­}|ÜiíãØÝáqöWÙ… Ù:HªkŽwX~¼ä$NÇwB Ã7t†ã>sïð”Îñðîµ=W ¿§3µp½Ã‹0#\.Vdá£ýuØç³ò[g±ÁáEŸ&Ñã“í ºt²puxð¢Þûθííô‡öùy›·j0ê¶öqà¬vëë­&¶ò·š¸¬£NzyMÅû"böï(¦€WøÓ¿è÷;àïáà¤ËÞ€™(6 î]çðã˜S_Ûã\Þq@TÇRJ¯ZëwjõjôbÅ’zÝ„“½€²—Æ"ÌõlqóJºÙïÙ epS>Æš£B…&!Ö~š+“²@k€È¨ºR8~ǵœºL☢I ‚;fÂ|Èæ@®w8q»",>'ÍÛËLÕ¯ ` ¢%ºA’ÿ¸ø´Me…°-È.ce”nT¹ªk6„³CQFVMܶZ5lp>ˆ¾,fJe%Q ÒÌVÀNb#ЭÌHƒÇ‚BH(~÷‹&È÷ñò 'Í@O§øËëm;¢§nN¼ùð$IUÌ©a¢Û—k’„¯ ÍÄYGÙÍ’gO·ôdÝ£4{ƒ x1NGüßyù7.)ÞÙž.Fàq}1öƒtà=¦2PÕnŒ39– AÆAS WêK/”|H•ûÅT<ì¼ëªÅ¼‚åÆÙ¯0â>šTÍ-À-hx—5æ/©«–†B’ì+ õõÚðª¥é+H„èæYžQ‡Â3’B&Ž²ÕŠŒ‰Î9­äùG³Tx‘L‹´¦}) à.FÊT]TËáÝ8ª ™ßy™íÎNÃÿiøoozÞßÁ°Ì]àqØÝýê2ûÜÞi¦mÚƒ:ODæfãb«-w\—š…žOÜç-V6ÔÜ¢‡=–ðଯ€t ”ëaË1nëÅÖ ­ß··Uiì˜ávMÛÝ„¶ñG·¯éÿ|þ$«VÉ…‡QkÒBÙæµ <²=$À߯õøÌ#=øþ1׃ ¸Â5“ŒhT;<–*àÎõb¬ÕŠê<à‡]¹Û6'ÍORñº×˜¿S]â0 ï„õ½VwÚÁ*åÕ49OÕÀŒôÏ]ÿwÂìuw8Øz%‰ñÓžÅïU-€Mtã]Õ¬ºCS„Ióf}8#”V¹Û,9е‡ÀK2lK´J!ðÚÈÔj|þËgèŠîV¹{ɧ2ÓÖ|QF®¶hÞ÷Cþ~è?8¢ïõâÔ1 RYêÐŽöhËv-Loøqõ¨PLýÀú½«~ïšß›Þººä¨D,$%Ÿ›®ž\Ö‘wV±c8]àæw) ƒ‹ªº‡]N8Ä^²Û­Ú °ÃCÄ¿ì5„\â8ÑÈ4€=<U—¯êño0/èJ«Èeìæ‰]Ž­â—[¯]óÄF¯bkEÕ¸;ìŒ]ž ¿Ñ0¥VðfítZí½‡<ŒÓ=ê\r1ê%GàË0ò}â-á(¼Ý}hÍ’C;²í(xdðë│N}±g±œ¡VˆÐð!kÍg¬¨áÈ“r¢`¾E/J¬k6×P5\·ºBhcM‘ß™Ÿ©•+nî×½9 ÙÄé“0É›2å>ŽEyKñ“§œ£lxbÑ w/ºÆFgΣ ’ê€uîó"B«­ºŒ:ÅïN‹ßuÆÇÅçûÅ/ûÝâw½ÀwƒQ¨Ã^`†ãâtȤVm)õ­ú«¨¡ty?Ýcå—O•Ü?ËãË ±,•¤Ê@*¥ít:d?©‡òæÖ­UѼ@æŽo*ïÆ‹šn’º#ø¹0Ÿ šÈEJŒ½Aéß‹ÏÃÙKªFÕno|£rÅåÄë·EB¿È«SÚÅ !ö= =.Ù*Œ QÛeΕüGoÓ•ª×déäG¨:ý‹”a$ž,BSc4wv\Êçîø$j÷ð¡=î~êüEÚaÿ‡ ô¼‹õ$®›ÿáåç«%c~j÷>v¢J« (ô☣]Gý‹¨= /à^ÀJG|#6`¤$Æ–ÛÚ>¤—ŸÛ£hÜ>ëôkÑðcßZpép ·'ëÈ.I_»ðb¸²ÚlŠzŠÙLŠ $¯1*æI¿ŠnŒ‰| ‰´ŠÊæ¡ä1vÈeC`û¤4W—ÜèG´1_*¿è•nÔÃ:ýô«ENÈo1ÏxÙ!b¸Ú¢,ò3àúºaó«úÎ}óû¤*æ§žû“–fúèÓ#Ò“Mš 6äñ¿¹ö|¬« 'ªÓ¶|Ûv¾V©A\SÒ’š%¨9ýv«ëv†ó枘›3úì¶÷mU¯, 6ï}ž¢þÚ}°]úà›_eæe4tÒ¸®"ùMýmØ#Eù£X…<òìŽlüêêjQâÅè¬1Ô¹ ržs ¼lH6Òâž’žzÔ¸%> A5›’úœ'ѽ×Áïéwçk*g ?:_~¢/? cÙ¦zÈmÊâ<þè|=Dà§wYæß±LðÇ!нÀ#éo/éÛÄýöó9}{é|k&h@’Ïñàô\YÌ×;®Œ²•ó€®ÒbZÄs”±*ìVMãsVèø^ØVZ%•ËF|ÉcäËаBþ!/Š{÷CìÔ¸ßÒ§ÂL¬}œ52yøÌÉ#)æM²û46÷„eWrÛ$ÙUPPL¹Ì¨ÚýU,rÉç1|ï¹à½ÝžÜ=©½õl·3t¬Ÿ¡/ó&û뮂¯Cد7Eå+€Ÿé%c~ïv÷(eª³µ|J_Æõꎞ}S&CjP°CŒp+O€™1ûö‘\Û`,vq­h•¹òþÄŸq.ü üˆ} * ñèÿ‚Ú®Jûý¨;F°b®ã·÷ŠùzàV‘t©ÇAýbPZ¶8)Ùb`Á£õÆ=½:tÍ3‹ÛÙ©½~ýúÍ‘F=]­eQâ–ë-J¾6|Bä)ÈzŽSႨ$ɉlŸ7S¬æ™Îb»ìŒ¤^(7†jûŒÁ6Àý}½¾ãÞE{ÜwÌ3juÒãðw\áÇ|¸P$µ£ú+»™Ç§¨³Õ9—m½;QåôlŽÅ×ÍÌÇGÝ’KÑ^46×°Â?5G`4WñÀWaÔc–=§ª†:ìzÀZü¶`,¶•'uK¯á10fz;ðX€ÁH<–Ÿ¢¡Ü§,ËÌ QÿeŸ9“ « Ç¦B,(qˆ •åäJÒˆ#;1ÎWü×¹ÊWD6îmzshÕ0K´ïÐŽËWƒþÅI-;ij¶ ŠFb$‹‹—¦ÂÙ5xN`d$üÈØ3u?ôaÓbÜç=«@{òßèÞÏÀ;™¸ï3 &¤lºFÔ‡+Ú'÷}Üo ǽo÷ðD æöª\±ù™’&{·áÄ÷ƒÃä%¹¤ú&p4¥%inûÎÜ s;°æÆ™¯54r‘t"é™u Ùô[ÇîIÑujf1ß´4ÝkÙhˆòK¬¿Ãu8ë 8h¼©(33.JdÁ³ôaAµUë¼Aî(Š;^f’€Ð k“.Ĺkõô©8¾WR\£òÓ\š¥÷:ç¾*† âb¬8)É0¸w4©±åƒkûHGÄöä$-ÄÑ^à«þ;ø|ƃ¤˜*ümAaQŽè’.GX-¸Ž=EPǘ’>‡6%Æ®Ó[£O·$L’ ´/R–®Gò´ØqÈŽBèªs ýâšP;søe¿é‚Œï5 !±wE^ "D4\[r÷ ½"«™ûî^‹1ß*~ÐdeÜǺð bš·Òæ‘£ªå½Ï±+ºB‘+ˆ·hI6K {ÈŠ)"ÝøIXJUµÂ3×H~ÓŸ:ÕPcX©~—¦?ÀrõûÏçVlÏy°fõXè_Ûä>v§†Øæ˜dïšð†^ ø(€)·‡OþóùóÜÿíßÇþ:ö3}ìÊ‹^bO;òLU¯ë†PHúŒo\ŽE _²Ã¡æú¼Lõ’êp§ôÁ‰4è1”8I¼¬è$½*‹ÅUÕäÖUç$3SEä¨<›…¤5©eÄ©¸¼?‡ÀªæhvSW Uå$¥=Ð`'­ý1.Ífdc烒;ªÚè/NˆS²D¶‚GYU³Û=@­ÇyÖÍwudxݳ„û4V¤™ÜS®Âé}›4ÌX©âêŸS›Ð|ãjà³}‘_D›0Ìò€vŠê ¹âIŠÝø¬FXür¼|1‚žY=xXq:¨óaöeÄÍì½·coKMÔ%‚œ\„×C´ÕðÏ( Áœ#O° ®ü‡ešÂÆ:E\tU®ÿ¸ b×q­Ü<ƒ‘¼¶SºZÁ¨[m¾ÛMª­éOÜ~0²Ú¸º¡Ž}ÙQ“ƾ‡_IgÇ}ü®å˜®áêl†Ëp0Öc{|ÍòÔÍÛ$ÐpxYO1XÜ…q]~JJùxœ0’èütòSWפðOáýÑúáq‰©ª¥˜!CÊšV3.8ý ÆIulêCét¶Àù/¶Ú_B¦ Ö ·œóÓ7´jí¸ "J͘6ÔUÐ2ïIË'5ÊñÊ݃‚›bðòû>‡Â0Þp`¼ÿ¯äÞïNP,îY×èvRIEUêþIއõAJÿ€Nê^Œ”>ðñ êËÉmÚ"¦FZ{„}‘”CjÚ4¿h9õUÆFçE_­ÖpP0‰ZARª2¢ÿˆ8 #ö÷d@Ž0qgF¦d)•€ƒmƒ †›€@Œ«5J-ìÈ=íýíV”Á<{Ú«‚%ÇÌžŸs6¶ð°6ûøKõ•ßµP¤¬}w¡É– u ø§žNò{ÏCàe§õßœÑ#ĪÖa¿DåMOâcep‹Êê­à»ùƒðýœ4íë˜io¿‚È¢tÇJ4ünÏ%תùœkuð'½Vÿתt;tükÅUr}ØÙH¹2¬MöÈ«€!&©×mÙòhu—)`U]™×eW†äÆß.ò*i˜ûƒÝn·W$FûKhÕß=q€¾4˜E^¨ÜIK.‰‚)ÍüµP\”hæDcéaõ«Lÿ~§m£=™þ)þ(nÁÔ?‡ÃS'ò®gmr­1®ÑóÔnøï®fÅß±x8 ŠÄ‚Ñ= žçSÿ9]Ω¬ç`ádm0IZ-H„æÑYNöyÙGÍ×ð_=‰£º÷ò¶w{=þ€1T¸—I-jc%›;4ô×ȉe^çÍñ±ÆwL¤õˆù¤}=ºµc}ìŽ5~pÇúÚÁ®­¥*¼P="ܳgODvlh9Â7ì±ýܰÞ»õ=1Ö+³Ôh:)†/c}÷r×J¸Ÿu M^Î Z†çÓ¯²‡…Lˆ{&•¡ve°³ÑxÖL\WÜfò\qÇT„n#·¢ÈkI*Ùt³Ã;. n­~ÈÒWb T͆Ñ4žsż'7”„ šKmY4üÃ?N(\ÓN.©8“ÖpÇ8¡W[ÿØ.þÈüŒ“þœ0Œ"'Ö8(¨D* E Û­þ‚ÚÃvõ°]GVÐ#ÚƒS#­á$•pÞí·ÂNÑý W4ÆIô5¯‘jÛÝ-lyÏ1å)å¢~È3ä©Ú+©@qÓýÅJ\w7úÏÙžsª¹v—Ó¹œÖùÆ=>ØÞqëó˜¹û#È ™‚ƒ®\—ç‚Ó­ c]ã¹óc®;ªõ:·õ %ŽÕaîXý²Æ†[‘š_Í Ɇ;<ÕOQN\ýéÄþ0ªÆèÁÄà¤#ÛÖoÈuV7MY–õÆPöeËŸÈé@i LcVhé¤KŽE?n{J(ý\÷³ÃÚ¿aí;a­àóÔ'bB çOêæªãïöb Õ2ðrñ ¥ âÊשåN§¼ÀLĹÎôGa\伬øÏÞû¢šÙüµYH_ñ,+êþ?»5`$mcÞI¥¯·¸½ÍÒPNâÉkäø—k}¡‚­ùB´Ë%"]F&²Œ0Ž• ì{ 1Þkƒ’Ùì•¥™wöÓቭ ´¿õÒsé=Ý/èÞêvø‡m´0ZzÀÀh¿¸ûUÏ÷°‰Ž ëÎ-J\XUEtíIÍ´{*>î!…^Ûf‘J\ÃAÁ\&²Ï¶<óTÇÊs¬°¿…à¬Àþšo °/­[.ÊÓ"nXY@ðÐõÎZ¨g Øû ðžØnyjlÿ¥¦g®¢¶Äûl‚yOÀøo@üÄ É@çíÏM«ÒѸÀ åŠRE„¢ût’ñ—+Í[½¬¡é]À/ȸµ|Ó#•©ê˜{Û )ßpZš?‚·DõÂã<°‹œËkÏáõV¡Í3ʧãu_lz¿¼æyoÜ=*7U^'õøuþÛ¯Çoêð·ÙŒ_à_øï5ü÷ÿ³êõ¼~ _¼…‡ÞÁßwM”°w£õùbÊ!Ìï¯añÀ‘:ɤ¶/Ii²Ì°v=bÁG/¿&¥m!Ñ8:ü8bõ–qTi 1£&Ú&1qÓ¨,{ÑþÛ—¨þ— ó»6MåFŠ.ú€Dº\Ð7zß9¾vÐhvx‚åËñš—fKÂñ6¤ƒR>%†£”§H îi[2*Ã7Í9[À/¯%{£öd|q<<‰Ú‡°]íÃ+˜ø_?vFëR<½!Ø~“`?L!/LauN[˜Ô­˜Ë µ‘ë6©ñÌœYŸ;pÝþ!œÂ¨s½¿ŠÚÑqûp 3òÊKGÛ‡ÑÖꆛŽãó˜ê”hr/õàUÐÎb>³t¹â"‰T‚œtx¯÷7Îèâ¼uz\ƒöís) !àî?çL@ºH=ÄÔÌËún`ßÏÒgÃ"A8Ψ äC†ïgOwÏñ-•‚ãõÁ†½@(¡»Ûý[ø'ø’+f”žãa&õ¿îȉµ-3 4¸ÐŒt{/ÃΠƒYÎzWü¹݈ ¢ù– Ù„‹qðìêûZbÙ.ô«Ù†­kþ„­k®Ù:gœþE¿w¹=hí}ê ÕÂ*®ÀÖóÌ‹ý}'C$ªñÜ“ÉÜ¢¦K àŽ•!3çChþ„Chnyˆ]/€7Äœ}p'mÂt‡Q­t¤×0ÒëïÙœBYb`t0‰ÿã,¥Ä+¯_ÿÈ|½-dbÙ–6e1<n¾{…EþØkK7éë¹ÃRXlÜ>ÙDò¸QCΛŸ°Io¾ûú^|ìanɱ¾¾å€|Ý›gÞßvotSÍuX·|Èw0ä³/réñH‘¸¨ ÷9Þ„ä'\å7Û^eë@®`óòs)%C ·á>û£+ÐþˆFÜLú L`»‡ûXõã8Üõ«{óÜ#9.9ÀÛï¸ëpÿÛ­qFÉúÇŸ/J1 Ï·ÏÆÇ‘wÓ±ª·á'âÛæó £Ûª[QQJªŠï;ðá|ðq´7öíkoûŸû”¹D`òe"Zi‹Þ½0¤¼ÛR`^(*ãot<¼8§y• ŠÁß(ö~PŠäPõï›ÅãlB~MŠmä ú 0ôn[d¶%&+E÷¨Vx÷Öä—7A›’‹ð¸¨ hŒþqº!f”RhòH¹5Î?g«ì†òcs0):³¡~Äñ^ç2¬&{æ=†“Þé’àT@•Š‚[ÙB¾¤Ù\ÕkZ}›Þp¾$V[ÛKJOä1Ê5[ÞOçéKgÏ|SO ¥Äb“K³~ꌜÂ×§v¯[Š˜šuÊRĤ‘Ú]Í:L£˜f&ä)°]*D7cNÂ*ä-ÆÚŒuÞ¦Ú/<®¯Û†í~9'°ïÓ TNe›a"_8¦»€Yv?€¨ÕïtŽ:\Gyø™'GËCøzõø«rü œû¡ .øÏožP³0¡nÉ„ºÅ u7O¨©&Ô… u·˜ÐëÀÁøzRk rhXééeÜ µÑ¸o¶·½í¸Û±k4îÛù€eØ‘^¯RãEía'ú[gXÊ0¼‰ßn?þ»"ˆµ½%4(Û¿˜š½ïœtñ+LC¹–Ö7âw6#ãY‡Šsê—)þžU€khŽR›–.8 !;µk…ë—7ßÒ+7ØŒ\[©IÞ˜ïïI½µnqïâd;Œ† áµ¢š“x½ZÀ8Ùß~àf³ö•à‡½ àñ±â.°ü±ì¸ó5 kIr°Õv‡¸1èV98oà[a'šE` Â"ç=!)Ý^˜Ù^-êtª “@OUIØÒÛ„u¾™˜JÜO¯ºÍ¡„ËùÇ™Î[g:·šÎ»õW¯k‰…›îÉ»m"hŸ¡ª5›lµ q£îlÆbm± :;l7#eÝ”{JX;n›9¯!c`gN(ßë¸Ñ°ÇçRÃÛÌ „·ÐÍRFaSy ¸RìÁQ鼚oã†Ae‰›j°tJ!TÂó&‰+Ç ùÍK¾ÆÔÖ0xl㦄FG _·?ø ‚ÿ m¸G2Ïu€²AOaÏ#Ä“­ß‰ú6;±=wÖáULFEí ¾ Ÿ+]Ø>ˆ¨£.ˆÅýrãð»¸!HÄÏ-f4'¢s¨PÖÝ Øï}+!ÇÏ- a‰¡œíÞQçDÿ#&‘äí±Uö˜²ì£¢\§Ö|ï'ë‹9gðúÔ^)".ãŬçÀ@´5xÚHß¼q]ß;?›vF®~eÕ¼Óaü-Õì+—Ñ!¼ E´¹æâÝ;ÄZk0Æ»´Æ‚󦼪ԛ Á-çTÃRRmò–€e Î¯õ..kxx‡·*ó¤u&8vXÑèãû!PCqÚ^Àývth—Ýy.G•Ó‹÷1È#1Â=E²qœ Ñßè ³Dǘ€þu-ï-ôæ‚vuø/ÿ.ãˆ}‘#tF–üW1ð¾xÙ‹¯»í“Œ~È;"âwvvÞw>€ˆ>^ô.>ú‘IÒOÃÎ1 €ÞþŽ×Â~$ â†C;˜ €óÑáÅà —']|bŠÀ#ýQåêêüÃ6PcSoÔß"» }t¢ÏÃîp |-¿% äÛ´M­”·:–[È£›f-û†e­`7ÑÏŽ•Hõfâ#¿¤ó;h6ަó›e†}¢ôëdaE¯|!u;çƒäÕ‰ªÆ˜H L¡nãxú;*¬ïP¡„6°"b·?¬F×T]ºªÑ*jŠ;¨nØ9ÈÃî8:ºøø¾×1^&•öÞI|±÷7ÜŠªHD©éÔ~•öpVíó\ƒˆ‹¶€CˆØ3¼µÿ¦ØxXÖx\o5pØÉ¬Õlèh†Kø½÷×TàAiÚ  e´N€­+À£IþZ׸ÑÅ`Œ‡VÏ]Á¨Ë«ÊNû¢?˜°'IïG]üÎ@½¼†uzt˜ðŸKúCËá/¯î葪·0×Óã^ûCÙOJ@¨õÃ" äøO#Ž`ÅH{¤ÀO®Uô7œ’ê«=nÓ3¯o½zIå.)v•rUêÇ)‡]¼‘ÈUãdX!GÓ¿T%g=tòuzƒV¡'•ÿ^%_ÆPæ94½Oçè”à¤Ðç–¤#¦š h¨Ó]I–ÂüñZM‹Ñ ª–)Ñ>Ûp:ÐAš‹#Y6{¢‹<ŸJÓè £O¤> 4ÿš.§˜ÝuÑùÍrz wZ.óÙS¬o-æÆ•Ô‹Ürœ2'S¼¿œO9Ù 1"xµ8îRʛ‡ =„ÎX„¶…"·•’›^Œ³’è>K%Í‹ø\¡Šú:ÃÆSíSÊñé ^œ¼q¶X¨:.Àoðák¶|â@;ð80›†Ì&-æTùgÄŠƒ¦.Lûš ºÔÚóu2KZ³ÓðSx-³Yö5zw4ã[©ê¦SÓšþ¾ž¾å²ªKtR–Jjú}›9ëü9TÇšÎeÈÙ\[’ŠóTPAnFž’χ@AûÇŒ#œ$ Á¸î,z›æêzÀpD¢\Kw‰×üT•‰”¢€E‡—z¹;êŠ*9æ)gêÁ$š6Úwg) vŸÂJϲÁóo në­äöôÞS ­Dsh—n]/`f¸uü=tÔŽ@˜ >œŒ·W2^Ã÷‰Ü‡Sk@hþkIòL‚ºsÚfì$Ò7Ëîà~Ý#šr·ˆx>šœÁ~µúb.Ö´˜òûæÕé# »Wò[°“› ßüaÁ¸.,[¾¸Ž;V ªÁpL¨p- hNE[ ¤Ó{à¿0ø|ödËÜØuöJVbœ5^‘ª(vÒ)ªŠ•£VÎŒ-ˆŸÞZÏ+.w¯9à¼æt½cS”Xn±:8…ùÂÂöËîf‹kX<_gb h館.ìa"kùÜxl QVxE˜t·úüÁP©‡ƒÝNçx¼˜_i]>,"vi‘&ªù¦^jÜÂÅ쵎¾‚¼9dS•¹Ocº‚ó"„8Óün¿q]µâÑ<zTñlÞ½oƒ/a¡0 YB¿BšæÖ0›•¤ïFXŠ‹2cصÓaŸUÔQ´+ŽU$áIšù—˜ø:Æšßq´_¯#Óyñžx”¢ "š:G;¬³j§ø&J—×ôr#ª…½>¿¡¥5@/²ñCŒ8FþØÍPC,¿ÕJ1îDÒˆg·dIÙX¥±wì Uâg#?óÕåv6žw^E~»Þ­Jp%rÀUÓ‘PçÌ⟕Î#ÁD}°¯_Ò\QDë5Ì´Þ ùÝ*ù=•Q’bÕsU$rr…W|ú˜h(1:bñ‘ªãã;^Útλn}Øåç­Mi8ÉäÒ¾³¢Ž“ÇK“ÆÊ<ݸñNÎù¸»ÅAÿóRꌂ(oÈ3FµZs\Sœ³t€Öi%ŽU ×Ò¿6·Œ‚s`m7¸ñgßÕB”a ¤À¬’A+ÇQ¦1U¾¨Àùé/6Æ †ÚÈëJÐþ€Iýï‘ÙÂ0˜xñâA¨Á—…ªM`µÄ!6¼ŠŒkaO«kõÖ¨ïÛ¾yxØ€+"@»¢m‡(p×úÃquR_Þ(™½N 0•(¸¬p‘Kckä"#`fçÞîïÂÆAýB\lspûàïbSn,ˆ.l€GŸ³«k-¥sÁ8$;𠪡4œ˜F‹2ÖW‚{4Hc}ÛðÜ`Zqt ¬”sYÓŒ20œ›söN)UÌ;T˜añŠ5Ë®XWeýÀúP‚G³áÜAޤɛB‘X q¹¸ÉÄç˜x­ P‰¹^ 4Rà(ã>âÑV-Ľï îC*^=NˆdôQ`Åä·/S“ÝTﳜÓî±r—©‹Ÿ˜=´×Iü-¯”“zê*³}ÊÂiã$róCª›:Õ‰”ÍAý dñ‹*°ß†j*·²¼¹¨–|.Ù°ÛÐMM¾ç¦r3£ã˳tyóEëùQ¨Ð‚(ùTÊÅSK\M†#å,³ëT‡2ÈóXšµèe©ƒ…py€|HŸBZZº!vâC‹óµé=eËæ=¾|þ_z;|ùý¸Ð”‹¨ü”Cw.9£õFvGä}ˆ’ÀÒØlð:ŒŽ‰±yÆ£{XÌHm»ˆ.í4…%‰dÖ±9[í¡‹¢,°†³ O# ØY¥¹~Þ%¦?ì4Ô]Âö^&®Ä\™{ólt–ÝγÑÑ¡¾¬9Žn§ÑòLç”°UÞóäŘvPo¢ •2Èq•8²Ç!H4©&oéV§_SÔ#Ü¢œQó°yïghgeOOmJÈS²íP@è—mFòæRÞ0ÍÄ÷/n’±Ì°VβÛÕý"_i[La‘?|³¼ž®–és·óN‹<œV~Gý ñ?µZ y 2Û¤N­À6\R60Tì±6zN“ItÏG§7hÞ©LkY ˆ2#i–óé.z<œVIµ§õÁ'Ù’M5ê©K¤Q—¤§Óóe†½úa«TòÔ§ÑÝôk6‡NÄ&Â$xOmÀžÒC­@£0‚éeÇJ·ž«i±Ös¾œè½©êwD|•á4ަ­6‡‘–`ì<Ȇ|Y¢²Ò$âÏ1¬˜ÅÕÈ÷ÙêËb•EwÛŇ®X¥6fËÔ0»…í^~…géêË4‹zµèdš]gË­¶—~£'¢ãZ4ú’Þ?0æ¿çÓ€£:Ê42Tš_°bŒÀl¿‰Ö—öá⨓ÇÑ:¦0òÃFµûGoë{u¸x£qœ]/±nWòîm§xD6²¥Â}H—),0[æüs_L> òâH”æg§¸­>4ç…ΉȈRòàxip8~¥ßÖD¿tEÑgôÐ:`{tؘŠVæbaœP++©±¡Ù{XMC8ú秇)‚ÇãvÔ¼bœÓuFWùš‚‡±~ÂþLh_ :|ÔäíˆÞ¨„´—uNH —µÛ¾}Á›&Ï Üø®œÊ +Ê`B©7x ™„û¦K„ÖÇZJw×î©C¾èu1—@Z§f¿Ô"­ ¾Æ¬ÌWÚî›eh—iÃÙ ¥xœ¯¦3ß6*†ªÌ5:UÆ%O‰‡3,lA[(+K­ZX /YÈ )®X° Nq|•ÿêX"e”õK¨þ$”ÕŒáÅá%©y.«AkŽ©_,]©ÆÅ1ù–À'esAÉ ¸ÖP+ä;¨Àæ¥)A‚S«a}˜›Õ#Cøå:óS´œÞ}a:•>À‰ü>®@,ÈL¼"îG(šÿÉø^Ͳ<¯UC  À¸¡@m¦j+¤l0¸0¦ £Ï4Õ Íx%Y_²õÄU¢eŠPÔ3Ü‹¿‹Æn<¿×¼šG•®Fß²eÉγVÑL óº8S®òí¥_ñî+sŸ&—š-`¤ÂÓ(±ØÉ®EÇkX— ±@ÅÜÿjì[xïÑ Ílu鯳Õ7„Zö ¡»ƒ "š N(Ñ]O¦9ðP7+f3rŸ>Áv/°úlJ2Ü-èêdñ-£ÜÖœ [Aâ@ö˜!0ßsñn©*­L0ƉGY2‹‚ÐwIºód›¿f¦'½>—ãªØZî·BTÞ¦¦ÂtN¶¨EaFEÜRßmˆ4Ár‹ä‘9¨ÜY›³¢Dò"†‰4ÞætOå~Ö+ÀWbŠ7rûsÓ7—}I­ƒÞöª÷ /µqÚ£¼fq‰c¨.P¹y‘Œ L=¹C¡¤ü’˜: 'Î}Ø•ZýÅ"…Yr¥0²{JW¬Ê"2c÷À®-°‘ÊÈ0aÚ»H'dÀEçÁËò1ÙyÁ®`6v?°ŸÄëâøØø%1áKHÃoñ%Úv‹Ôt¹OqËÄÍ{cøQp ^"ÊJèbÁ³A 8N"L¥7ËEn¹plpŒpI‰–S”Qدµ½¾GÐ$ò_¨EWÌþø÷vïšt?)íã Ô‡þá êk,ݨý©£æ€#óì {Þ›2Ú+²¿âA_qíxûUS¼<Ãú6­jl`*…b€«§Ðš!‹ü=Œ”ó±4¶þMû)·/-_‡1´ÜSË­¿µv–$lgI¬ê»%–=y°km¯8àŸ0H,´As¹d,|v“ÎÑ¢†§)¶œ ¦Þdûkš{Õšˆž1Øô_ñŸ=îÈ1š …¢ÁyÜÔpozÃÇNIu*]í²²à3\rLw®  ·…c“ÇОGÖzó˜ArP·Í¤ü»©æ\gµÇ2cFÛâ3kè9/Ø’9ÀeÁŒr„.¾þXÊÇD)˜åÌbËÇÓ¾_lÊ}`8¢YÀZÖ\ú¥mÞ¯Û¥Ôdb0doR;ªm„pªääì:¦ËM”õÊôcNïß áˆúè ƒgÔ“ J,O¢f•¬-f_Û»Ä:H ÁŒ-Áäw°ƒð‹_Ø R<4ˆ}®FÿEHkå1Ϙ%xWn…ðÛÕWø¤ƒÔs\^^Á*ˆ› Ø€.]ûgè1£үo”ïZì¥ÙŽžâ/Åçõ²é‘Æ?û²qRÛ,[³—êRbãÝ42%‹¯²·(-5|R[áÔ!“³¶3Á"»+fÍô•j…³RF‚s˜o@\i•fúnšÖXOÓ.K|¶¥i ¢i}‘;™˜ÍBCÇð(˜"_H® Ý îöqÙ’¦]Ú4­QBÓ.™¦á4.oMÕϤjçPµÆ&ª¦ú%P(ñ\)P:rHÑ„ÆâÞʨ{ˆŒ ÉÓj %¥/˜/0 ç,½Éðú‘ ê²Q`ýZÜÌ8ù¾ú;øý,³Ô1†6ÁuŒ¡/ìã9¶¼;¨¿n`~ãc;ÉÐÓðHÞ¯7=Ô¬sú¦uÕë2ït”.ï92}jj/š¹Û»Ì ¥-%D³'Ë):;ZL¼JóŠþÓ|•ÍML ›>¥Ä_W‹Æ‰`#ÑâÛ"šLoÉÅa-ȇ ÿU¼ˆ`ZTĦ Ÿã!T„.ÜÚ+ ¥ºú¿ÛÆ-ÒÙ]v½L§7Öãôd;†" "O 汕õdп„;Iææ‘Ô ^Ñÿüýäù†yþ/ÎãJWÚ+둪ndo˜6|“OKQë¬í ¬aVï inµ~œÿ6_| ´'[EA\ÓÚ¿NQ•ŸæºË/&zö;à@ %S'NW‰wJ•yÍÑØ‡3÷²Q»=wSNV?pE˜»Cð~ÀÒ¡vîÊ~päF*IÓÉÃG€-gÙEÛp1À_˜ñR÷Ý×N(Ùߥh<ÛA”Þ¸ª¤wÐüØ‹çV§£“í tµYä^P²yq—h²ÁÓ”@­¯_³ Mv“†žã‘zæØV’ÉP &±v9Gl Ø%³ÂÕÿ 2 Ýäx}{NóÀÝ®)¬)¯õv ?æÙr/|x˜Mê-·98^HSfžO'Âd4ÀÄrzÄtoÕlRíPÏS±æÁQ©Ñiz³¸ž¦óÐTàŒ½ž?e¨Ps¨£ Ÿb:«Êªp®Û­ Ù½¬Í«¢9„¦z‰Ÿ±Ö‚³<ø¬Õyá„iå³<±0F4lLÙRÂè+îÕz¨i‚‡Vÿ0LAZ*„\ _;g(ÖÖg·Z6ï†ÒµÑ&%#YåaëH·Ø¨ê߃ZM‘®H™‡x ‰1%R-}R´iúñ?n{é°Ì¦w4܇§dÁDz‹êÚÛ]ú™´ž:R[ÿ)ÐÙO{é‰NµœÅ5:CJƒRµPQ!ô¢ Ê»@gDH²réÕ>&m'€,¢TĨ„PObQo~[*G‚‘Xs¬1©6Y³Ù‰߃¨>f^$><ý…¬Õde¦è4E“ÉØ&}&©3E›)ÊLÓG¨@oäA}§]C~VyŠ}‹aðEMJZa—&ü Òjö iÌoÙ«äîî% ƒXG¦±»¬ þUÓ?ÎQd„Á]=‚ÖŒü "“²…“-3¥¤ | K'8q̯ãö=Q@­Ë@”4"ãYQ£ˆyWeÒ®ÆF¶$ßQ —NðF÷V5ø?{…c%3>ŸÂ´–Ó;Ê%™^ê…“¬*4Lïwض DMºa 4.AŒ¡'àHègÂüÖ‡6Ú‡Ìlð‹“¢í ØH´lŠ®ûd¶·(‰Rº`‹Hx)3x( H7W¢`¹âã§înÚk-DWWƒþgCüÔ„§EC`9Ž¥Èà&ÿuÕï|ÆÖð§ÔCCùv˜^—Dè æ-¥ 2Ö“?Ä¢•ú¬ŸgßìBr)Ü?ŒàÒ9Τ5¢HÉW ã;£ë4Ïtj• u–Þ_ORíàßµb°¸ž‰^´;œœßb^ò÷vo|ðw"Ʊź‰ÿ`X@8dGAÒ,ÌΑgD/KêÊÕî›T©¢…ôt+Î ÖZ ;¶ÆÚ’b)³Ž–²ö® Ö›¨²LË’„“.TO ƒZºté±Dé9 Ë§èu¤’ ^y–ºÏî„Χ°VÝó‹]„`z€Jÿ¢ß©†ep¹Ç›eðhƒ)œ6Z¾SïX20—sW]H˜#‰ûZ’ËÈzaü#.xÎîcžÃ‰>n+ïÑÀÎΠҭž˜y Þ3ž{f ómºä žç ..’–Ðg„ÝÑÑsÑÉðô܇WÛ(Ûr¾ùWF¿%°ªG¹:Kí³ý·lþ'‹Ó?¥2I1‰²†ÝGw‡å._7ãØŽ÷øñCûü¼‰ãQ÷¼ªyxŠ*'øÏ(¦´¾lP"Àè'íQ'>…xÈ‚Á'cìb¸ýÎÓG|5€%²tvz€íØÚçáí»zBŠ׺3Ôëï}êï¨ô™z(ª 3Tep²Mfv€=Bøëwõ?Šªãv(«vªDZ²å+ck4FaxÆ"g¨;Ž>X`Vå F2ÉÎ%%ueŽfÔžRÕ%…zîžT] ,ç†ÈÙžBËý&ƒ&µÉ”-öjn7 @ufYJ”\ËÞÙííôfš±.úžf{Þüö-]Nì¥qgxX̨eMm8’€%×é«)ºšb)%yó°`Õ¦DRŸÒÑÆÉ2ÅÝÂ'€~þGzMKTÂKþîÏ3Q‡æåY­7˜¨ÃµZ‡†P†ì­¬Ö¡|íÂ&Su¨?¡õºB©{àÿ¾ þ9&í6r·p)V‚_èv"Æ™g¿¯8u^}……°Î.¸5†.2Ü~·ñYòÙ̯fmÕÛ'»·ó4Çø»¯…NUܱݞi²gãÒáÐHHê8¥Ðz9΋3¦&làB•Ì6Fl“«›0vȵ"¸ÅÚò½?Åÿ6+¸å‡äS4ë§Ð8\š(ˆÈIp…ïTÎv‘‡ T¡†g*¶\iðˆ~mi^G¡Üº e غKþ‘¶êM¶]«/yT«r£Èp­¾¸¬•*ˆ å?¤Š='‹_šÿ’ü[È0 œn Súܞǒÿ¯Ä>Llï š‡Ù0üi²ëZõ…‚æáµ†àbo\¡ˆÄ |ƒþ%!ߌjéY”}3rÈŽ´"SµíÖ~ÈŒÜ[ ŠêŒ[ɼQ&ç?Ð`J™0ü4R”gÊbÑ5ËšØÞ³ÐXžáÛ¡…_V“âÎW˜CJ`7csl®7£ÑZÊ,Ý?QWOÔUZÜ “]7k¥±°U‡§ÆÎv*ñ’ðúa<²Ú¹ýQ@©)D8±û7upì2Ïw볨•ò‚ªöØ\ñð,6Ï›Œ€œ®–X\ /øQ³¥š¶/ÖÀØßÚ'ð¦¶ðgö(ÞV+®)U?%Ý?8?­î¨/~¡XRgûoƒ0ô‘ýͱN¡?­ËÝ“"Íé§Y™ò¸ùÉX[ùlï„¢*±vÇDM´úç'˜Å•›‰¯m6ÖÑ¢Þ¹\×Ì:dÓ6¤{Öze¾/ñÈz^ÔËeZe[ @gÚ‰J™5ÉEWm#g¾ƒAù5Ù­* (·Û\ЦQî:¼ã9W/œ Ds•„ÿOÉ­^;æjä@N*9æTýYœ¢¥ÇÔ]GlmE•„“L¥,e)$ù‡’2kï——g#õ¬[½6‰\ ÏኲˆDBImÔ´P0<ãpf T(LïPKMÐéWùa¨hD‡0´ù|Öï|nYXOÙâ$yßÂwõ¦eyÂOôIIdPbd&ˆÙ2£ÁpÀ ÉD«;ÖÂøn„7¦FÐéÙ½s¦ˆI#jЉÅ`K+ ¯ך;ø¹jªù9³4 >öI9>;+MÊVŠëTßZkm¨µ"“±ýjt^{|^ç ³*¤c¼²˜¬âØkÛ7"DoñM œ ¾?h„8W:xÍžj¸8O,½ÿ¡‡ôï˜ \´o‘ºŽ’îÅ*ì‰W×Ü/œÒ~`J!Ö^¼±Ù €¿p)ªíý‡!°¦¥‰ à‰èHic-LtaŒ¨H¥B_¶t’Yê^MЭpÙ'×}þÅóóax}½Ð3¬?W[ú‘nŽºÇÇ2g&õ I— {tybAOí[¨y7‰ç˜ì×(ËÒÙ8LÔq߆‘Ê´"0SÇÉO?Ë |GEè>¨÷õœŠ¨¸ðŒ–éT4ÙNÅ.‡%„;ƒè. ‰pø^\š/†GÕ*KË- W‰ü,.ž ·Íƒf°Ký³…Ï Còj6í‡ ú$öG4)<%l¥ûkZ(¼i9T4•(£0X QöžBâ £Q+NÜð (J£ú ´Æm±Ñ}²WZO³á¡q^çE³PYŽ|Ô×$§Ñ?b éSÌ]UC›kFÁwÁ5c* "˜hÔÚ$¤ó2.ëÝKµr&æòd©š*}Ñ„M‰Ì9½ô“¹ ZQ/A «o šR9äÌÔOÐ× =1–ìI¾™ðƒ;µ7Mëæk¼È!ýžÑk-:Ä™P¹)B+$X`4-ÃV…óàÁVí‚`\¯c}ÆÊ)=oRÜU†,ÊQs‡šî2¹Çw]¯¼šMÝ‹!BÖ2'‘8 dcÀzíf:Ñ9nôvi’PxÒ¢4'´À¶­8ÆAÜ78›B>­ ââð}Û´0À[ ¼ `î<áø!06~g5~ç5Nª-óv×bÿ¨a3:M|%Ñ)Þ›½Ódמña3JÓåTqZÝ•7»Z½$ÚÕ@u ŸÊ$¼0 ®3ýP|¿/ɽæ]¸&®ˆXl”[¡•W˜*qd¿OW䢮$(»^0b0<%=&‡÷ûtnpÀtö¸|QÝùkÒ”jì”ÜiökÒÚ ÃUIƒ KªáölIäu#¬yaÝ £Ñ°8õÚ"¯“€Z†:0J™×¿¸×!Æ{>0JÐ×êf(e'½©îÙÌEŸ*yT!gòH %Ù´Ÿ…Ú¢„o3¯5jÒÿ-ýn׿óƒA!8)©Ô'á}µ z¿I1ÎT²e«•à¥ZmÈNoìTØ&@‚½8)¿‹.ÞabèH Û¥Iƒºz K s^ŒkèfØ_ܸ2©ý èÙ™_¿Q8³xŒÑ ð¯ O’:¶Ñti" uÓ+6 .ÌjíþQM*Isø'ˆ“¡Þ9ѨŸSþ°*žàÝqç˜ûØ7¿¿shÖk‡ÏVèÄ$>§cT`ææ7ÉKÇ —¹Ú—ìw,è¨PÓj1Ë–)ÇKÿòšˆÚÀà¿]GÆèŒñeÐP ´ëî½~ÝÔ˹˜‹UÌÜ5Ï8ú-ô«DR)Á³d!B_ ÃÕŒ*c ¤¼ò‹â˜Ê /ZJHÄ7¦HÕg=+Í;ä1žÁ¼®¢QÊV€o PÈy‚µÑ9¹Ew¯¼-ÿ«ÍÖ:Û;J)ê L+ª &!÷ú÷Çt¹b‡€:æªÖǼ¥CÍà½àF­¾L—œ›<¼æb!Í¹Ž’mÙ6Ë¥z⦠*l Îl¶ç§¯ÛB/ÇÊ ¾Ló/¸Bè”cºÊ£æÆ @ L2uëFb <åñ‰É4ðùKµD ôhï³[웿œG€Æ¦·O`B!èùÄågW–72„¤ˆ çÙ»“êˆ[·-ž¶0­@;M¯ÔÓâ9º÷&¶’J):eh|!Ýmá pßVµÌÊeìY+ÎCUÊ Òò}|ÒbóÊ|à r?ÖVÛÔ%ÌQ{¯ñYXZîârž9%×Qžã´Üwu™ Š0:Ö é.Ïé¯ézg«i_+Q!R¥­ ì½:×nPƒ·&Do4<ƒÿÕÃÈ;»+NûaBô(ÔN¥>·?b¤ÖÈ€ã€?¡*¥$(€Úƒíšõ·õIá!/ŸáÏöÝ÷K_RI‡\u~“¯ÓÅL|mä¦ ÿ¸@Š } ¶#Ä´¹7Ë.ÖÍ uÇÌ_.—6Õ_\ýxó¡Ì‰:^PsGð%'bÕÙ8ÝŸÔY‘‚Æûùf­>O7ÎZ!¡°¾_墧zDóD„÷ð‡áàÄi‘*†¢;¢ç†(àÆ†= ¯^ñ0Ð’ÉÕ³$°Î^Óî@GÖ•wào€nlRá¨R1¡<8Vo(ÕæE«^ì ‰;9•T“´jNÂsèðŠ F”'O¸•#Ë \Td•J·ÇYËýìw~뤴ugsë½ÒÖdÛ¢€3ßqÖï£tþ"pmч–ËTÓܨÒõ.R€¹l0Ê9@ùåô†,rÔAìA•œ`²ÞLà¸OCï]U_]EX§á¸}ØØÁ±¦V¾Ì¨«f÷XfáØ§¬d]{m_1WXßEf2‚½rŒ Æ{”t;uÑ·0)¡'pEû¸«b¥ºfÆLeý1±H2'¥HI_8 .¼)g†Ê¿ øP«.5 ‰»¢k”üsŠl"¦Ï¤º ¶O íR{zŠÄ?$ä3Ù0žcDj­Í2–iûO{"¡Ì.N&á! >²Nâ+;õ=~Í€U -¿•û„Ûy“ŽÑSoLlWÅ ÌIðmôª^{͵©àüôŽ>Y­^RgÊÉÑíüh·d$ô]“d"‰Q–gS‡à&ŸÐ;Ÿ"´VÑ*¥7ä¥VknåÚ9<º:÷ ”4š'(æÙð=Ô|«ÄK+´ÌD$ƒˆ£cNÔüs¬ãŸÚ?üÉ ]å°³nY¶RZ>“¹Ý‚³}îd¢³)çQ·`PÂtÖÕz†îû§äÌœÆÏeœêßWiêóxD|S÷ó8brÓ6þs…Ñ/–“gYº"l^ž¯ÈOO¦ó“½h.£B®qšÓKáåZáò^€¥lG Î! ‘Æâ[éÍÍbÉ. ø]oq“áï 2$¾ì¶ùÕŽ0 >oå$ÀFÄAµé p$¬s¥8:r µeTªýkò3>ŸRÑâ1\^þ^é`ýâ£Áðxì Ë{¨½Ûô]ÛùN²§ùå|æ4t&,Û©sjà&T³†,Ÿ2s®]ïT»¿4Ìc¥µy[ƒ C”·?—u½ÛÿÓÄ‹`Ùí”_izðE®4ˆHI£¹á¶Êp/šÁÛ*’RÉ}¥‹êÈ ê&I¦©ÓÅ#Þ½–3ªK/ª “I%†ÇùµúÚûák¶DA@B1XÔ´&“”º²9åZɹ•¤ÏWì'%$×nIC‚ ²»˜²L%š²³ÒÃs–ÉŒV,І'ò-Sóù}lüaÌû]ž·â… mçéy±³û~´ÂäÐÜý$Ĩáå—upÔ˜åÜ>§‰í`Ó›\éÏRê’=¹è“Z@ÇÖóê{‘C)ÿØÝ Ð Ù~×!íS_ÄWôÏÿ%Ç TÕ¾%¥ôžR¸üÉ轤•y18b’ýÁm³° çØA·¸‡Åìi¾¸ùF]V.þ¢5Ž¡š«ýDnÄì€hr§2áÐT:|zŸExrè%¦î¼Ö_ b;¯8Ð[Ì^â&Q‹ÉW4Én•î… •«~›<¤y.¹¶”w0î–•ݤsø¯%ÓU:[[d!·`ŠÙ ä§Ký€2ãÒr­ª»â¯&MA èÁt%š'²|Ji§K£®¹ÒÍ,Ï´‰u"’<ùŠrÂîÁd†#=E­ªºÅk´M·Úõmû¾YH_¨š“/UtAn†âÏJžu%i1‚•å‹Ü´Bé+)åŠ.Ž hÁnõæVÀ6ÌéÓŸƒàs6(~SxÀJÅÙ›ä ÜHÌò‚‰X”N›ã ð´ö.]òâ³–4lWE­$‡Èéº9VÝä*Îo”\€#^±?œzã!1ÏÕŒ~Ô:Ú¡f»‡ÔäÔÉÅrØ:ä_ÍWžW>¡@>$®º³Ó¨þ)ðØ'DbÃçǼÔ?z¼¿ÒHÂä·aÒ-kÍ3A!/XâYu!èÇtñ#ªÍdžU¡šÈÌú•®O‹-i’à ¯CÇ`åÀÞœð:Ž*vIÆkd&&ñdux3QϵËä"ÍòˆŸ~£‰ƒ¼æÐƒ­[éòÌ^«\5[W›™)þÚµHfk~dCfk©»È@í¼òG,š¥ÒwS Ò, ÀÎ)÷]Wèõ-ïW©……Y_…˜Óò¨Ü·z j%‡§Öþžó¸&;àÀW'ªâP íß°}uó…AÄÌ I?áZ"Í'‹Û[k[;z¤(ÚüÈpÃ#Ä©GÆ*¸š|ü)°tab¹ñ{‘«˜È)‚ªÃ«a’kùª=ûáz¾4HyM_úXÈõëff³°nå¾­¾•CØ= Oœ8—ý ͉ã·R¬—lRÌ;rÍÏ6TöLÔ©ÜÌË:x„òȵ²à¯P7ߦ³¹•¡yŠñ¤ñ§S‰®ì¸uè@mr.” w¿•Ÿ\IÑš±=aÛžÁñ`“œ„²ÛlSž!Bö½Õú?kr„Û%qƒÚYåFUARÑ-‡×ÝEÔFÄrivÑLëz‹k[7¼ÖºîçÚæ{‰¬Ý)šªWZtÓžÍLztòÿ»÷ÿ„båCÀŽR3œ.¢~ZéÖ+²iÂ'Ðh…½˜TZ6€ß·ÇG1©@9²¬¨<ýô#Žs¥Eåýò.0,Ö’/Z&בWª‚BÊÿ£_B‰þûÃN+iÄQ¯ oöáÍùe¿;ní7øíik¿jI’Î#ïçŽ7©,¨ydŠuwÇUóË©õéeÚäb¿:…9ĶÓHÎHÀŒ&91m™•‹Ÿ°Ê}SÂaG¬¥÷»n¹ohbëMOEç}ÓÑ‘ŸŽâæzÙc§f[óü°˜r!.ŽYTtFKÐ`ßMȾŒË6‰˜(tXq³q§ö’½€m¥„aVö¹Ì) ¥©°pY&ðøäëÉìÔáé½å½Ñ÷mü%€™ücvšwÿ´£’B»]çèV’nÜ: >8‘Ô ’똕²Øz‰: ŒÎMtQ¤óÚt†¦íã}t«*؇ÈA­Ô’êˆÎχ_ï«u-x¥ýMš:Û4 pö²µ2ÎháD£çhÛh"Jçf·/*ܸL¯µ= 7ìå)ɇà@iÃèÚ²×â¡!5þªÙdBYIHôòùAÀþÿ”Õô Sf[׳wÀH´¨ô¬ˆªc›Ã{«^6âÙTƒî%åV=ç+ütG£Þ§#«Hª Ä# Waµgÿ¥µž¬ï ª:Y!ªž‘(;£ÑvZ½{*VGÖ€ö»­ä]õz£‹ããÖ~ÓVwz<K«L•›ñ8—¬ª< u¨ôôA«BíX„h ùP_õØ»îàÈÒB‰Œn¥(nˆ™( ©˜psSÈ®¦+/Á0«Ÿ.$£˱ÈáôLe»ÙJûX²†Õ† ,?Sÿå}…~’Ø"¨f(ø©ïW âÑ>j|˳ÔÈÒ,{ÂÙ ØÓOÅ?;ü3EÆžÚÊ=zˆ44­ª¥V g §6[,¨lD´›emP¿k´Èø~×JJOj§Ï‚ˆön¶¸ÆLv+$øwOœá7c$¨·ë¢wtÜWëò΋.®IAr©¬<1p±ppú(y ctè éI þb!ô*E(éËH¥ !¦yÄ,@;ê*Òµå;+m†N‘B9Hé¤#åâì€:ŽðŠ×mö‘"ý-qGK¤¹²ì™Ã¦PiO-öÎ\™ñ~N€)˜Ó} Â^$7¼ ‰Ty 0í0½T°‚V‹:ê•u;„Yæ'œ&|„é˜r³@G¤†¤4NÄFà™2á‡âkã‰ÎCÔu?+e¨4Á Î{WéIK†ÚÛ0Öþº±Ö©MûÏÑš®W›ziÆHøÿ:\įm1Ry6ªÒx‰r"_ TjdÄLå=¬AYA],­ßWÆjmA›Ô‹Áʾ.ö i$z]Oïà¡sò?FlS˜¸€CAÉn&9"ÿ)i§t@¢DŨE1ÓÙÝb9]}¹å<®h­tóN—œ-¦3®²†Â¨õX·ÎĤT’h/jpÍæa¯ºskºŽbÝ’í›õè¿p{ÿ‹õ‡'–,¸­<ÅO©rüßí”# LýŒ…Î^5ª;Ä%E•Ó=Š×γê·Ê*æg0´ 0&5¨*æR…ÁDÃó‘âóð‰8ú 9j+í´èÂ56W6Œ¶OUyÌ·úJ'e'«Ö,îAdª" W®¶ZmC6Ë3ZC Ó&Je°¶(™@ý1Ó@î+j˜,Á^©Õ‘Vº ä‚ã"ÑX7ƒ#{g§;P2ËN£¼œY|ÃyÙ„:»…üºQ$ ¾¿  ²DMü»±U¯Øzéü·ÄšÜÞºi†é¥·y­ÍZÍ356–z¦8жú¥W)ö°µ¶¦´‡mT5/¢¨Y§¦‘œ×Z)]šôšæ¡ú\e»£vWFÉSªx(ü²˜Mtõ+‹a½دGJÒ™>w(/öãYãɃ%ãÒó9­eµ¸…»R5óf£×3úáÅ9«îvÿǵØGhõKc ›àT•ñ1-âhQ'²a;„s äöTïJ€ÒØ_¢Äf‡Öô#LµsÃIó-á {¼rÂQ¡{Q(x*)ñˆ<Ù‹E6žRˆ”õã$'!FIÜZ[Haˆ(UíY#RG\bøövz3…ÝŸ=E“i½Þ¬ìÔÅ—#Þê™”²ù7dþ, ¼Rg>×þí B,ª¯?Ç‚QD=G—áðs‰w¤p¿ÏÕº:8þÎÓFFuȉW€§I[X¢ŒX)Xì3çr§êÝ€Ÿ/õ¿TjÅÈÕ+ž´‡íCÔž>ìÍ?](Ï` ýÖ~b”*‘xÎYu>;Yé§W¨úÛ;xE¼âÔ/ô䫆äP—&Ü?é¡è­ç8aAšt;øOõ• DÜî ––€º`M/“Âbáñ¿0(Eð”+ÿÁwo˜Tÿ¢›‘ØÏðS.b·ãè]$ÿºõÿD ‡¬ÖÓ­m±–ÓŒJP ;/Ÿ\1±eJõS9“ÍÙ =½'Ž/w—¿µÚZéštuáÝœ:‰q¯áX ‹e"N|È8?ýã|5Å”LÁa^]°~OiÕ.¿eäœ9£V ØrÃ^<0^U–Læ·ÿŽºF«jE`Y^J‚¬# =½²€Ê½ôw‡Ÿu(KÖ$˜€36 (~¼ØX.ŠŸ›{à ]CØ{ØŽv·?Ž>u/zµØ¾„°+ý£Z *]ûJ–\Ìf/fCÝGÜѵw“_á<å­ea$¸”’„㜖¡aB)õ yJ·ÉQo€VÙ,¸CØQNGã“sØû–LÙ \K\JL<`·Æ–Ðo-êµÏßµ7#ÈFÏa¿€ iz%ò1àûìnjÛBïØ–eAI\Œ¬c:{ø’îiµ¼`@xò:»Y`ˆñٓ̕DÈ¥^Q‚¡ð/‰ºBùN-qcb“N"„4ÖzÅ¡«ïüì(.ƒÚFìm`YYl]£Þ†ˆ£GvP³{Áú ïjÙ·Åq](5iÖ]Žb ºù±‹¥x±õm ¬t¸ …«àпžWŽ©Â#Cõ éÿðËÆºqöqܦ;. GÚù33*dâ­"S%3b»ÌØí Be£](ð¥Àþ0j»7¸œ {[Šª)k‘0‘‡¡’ È€>%‹U*1ÛhòRóEè*ä¿.Šø¢%Ä$ÕÒ§ú1±>$ž=™à5 «>‡àÁj§ý©Ûÿàq¼±êw4’^ øxû!?ó³w»TÐTGÍriu)ûÙ˜‚ÆÃôb•8/\¡Ýž@ú×*õ§#FCŽ$­Ì)×ýSÈB#dÏGÓ_2ˆJ…±h²‹js6 %Þ…£R_cQ åÚfÛHÙJÔÐ{œC¤{\‘D•[Á,£Û1bÎ:Ub£9ø61c(û£ØŸÃbÅ›«MR¹­>{Xfún¡ÛJK¦ƒ”5J%<³«‡™HÃ~¡(º¸Vº:ò÷rn2ÿ¢²›)Ü·t6{ŠK¦ü9Õ\àØ„N¸€Ú¶¤ÃµØ¦´½ }ä¯Í¶xžçŽí0ö[Ã%L>„®—4}¢ -Z%J—\Ô| Tþ¨ã4Ý5íí*# : ¡3•ƒÿ AÓ¶7aX/(`³f0*U >W3X¢,s|áD†:$ù'Ö ù| Ž]AfÛHå—g/ðÕ~x/—Æ Ú„›.ˆdøâ[c\‰ì %ïgœ±¸"èë÷âka à¸Jƒn’¥.‘Âî^" h“‘µS/ «ìÂ*ý „+Â¥æc¬'oÅ[bÎpJ&ëæ"´ÒF&îòðÓŒžöÚ£q¬ã¿½\„¦!ÇgžÂVJ6³A÷/úý·¸?¾t6å!ÄEnùö]ÒHÞmàw)Yÿ˜èJXÉÚèJåëWcIÑ•ÊwxcŒåÕ¿ã%ùF>'þ0d}ôB7Ä#ŠþJ<‰.Vb¬!' Ê)”ý¾rò›ÿHÖ;DpÒ²Ë%á¶q!ŒJ-±ðâváVuÔÒåðV’ÛÝÔÕÀî‚цº´†TŸ/ôŽ æÕ+à ƒYÆç2KïbU;ïú‰qEpnxªÔËv¯ÿ q«œî^‘‰Y.¦»§…\?…æe¤¦ ¿çˆBv*g»I5&+ÔðLºrxZ`ž~57¢›îÜ”s`K€¿•áîL $ôð¼}y1<ÚMDêãk'ñÓ’ vô¼t`[zsóxÿ8³ci×÷ñœ`àÐŽãƒÁÀÁk޶ƒC;?>x ƒÓÙ7\¹Ü-ñyöúO^˜†TóŽÒ¯¢<`CÌ:N5Õ§Z {QŸ jhßPP””ý’Îï¸*ô5ϲ Ae¨Ý$»ÁÜjÚë’ 3º1çºÌ¾Ò¾XÓÕ>’6È´•ƒÖ$cÓ]ONõn—ƒ…JN°48ØÒäàIá¥+!¼~RxêÌp?Î oÈ;ZþÈìoŠ9sò/ÃɃ἖òó« R¢@#6›w^U’ÚQ}ßVK¦KeXf ÀÁzÚ0Ýz•:·N™½üàÆéíåhh þOÞÎBßI­lâQŽ®áÝvû.YÌÅÜ"º…ÈÐ §ðø°‡®›A pÔ‡KÚȤŽåScØ©ÇÜÎ6jNËôNhÁ ˜'%³Qádœ—_¥ö޾Êô7_XÞu%Aƒ5O±?¾€ÄH‰A n1|Émç9›×úß”?•ÄP5yœ+Z­ñbAÒ¨°'pÙNúXR½Â´`ÀÙ¯a@àÁ1ðˆ‚—už3B³6‹_>òY72°ô8²×á.ÏjA÷g}IY0{¾~A,à½04WMþâRo /eûG׆-záI¡6[á¶í%ú˜7§¬=$Ò ‚šu5ˆ¯ªœ‘+ÀFrè¶¾\ä9ÍQË•¬Ä>š@ù~žÝ!Ó1]=…ÜÑIû¡æÐE¹™L Ý<É êÖŠ+*{€'Ïÿ'°Æ€øV^ú±Éîú]É]£Èíº.ö¿¼¥Bý£2¬Ò³Ç Œí@ iÊAX9;•Û‚rÌTÇš!]܃8Q@*\¸Å5£ýÇ<½Ël ;ç¹À4î¾Ìž,N„@?ƒØÌûRÏØ(=\Ï­žøüþ€“4ÓÃѤŽ\šë{•ª¤órÖŒ³ßfºöÏÝ=0‰ML§Š£ØýjÀ`|Ñ#W„ó=gb޹%fÓÁ:C¸YúRJU¹ n®Xä–%6d‰—¥tŒ4ågwÏ ŽONçág_VÁô5—^öÝ÷rÖ«¾tŠÂC:õDçÇÓMXeßHîþ἞ï¸W—”:@&äüû«ä•<ò ÝÈé©WõZ£ÉÞã Ÿ@â¼}RœQaC®•Tö8ª0•z¡™–§§ @×_)? ¹·]r„± *O7ŒÍûkú3b±¶Íw†ñ)›’òŽA–H‰Úe30鋵^Ý_Ñ›Fs(¨}#Ñ×ô@õò(r­:Ñ`õ•|Þ•ÏÖÓ ]#–ÚZ{qxúŠWL¦?î™’Üš>Èjq#¸ >\°«–pò¢6 ÞuLð¶)]²“*]ª2‘/h’ûGddˆFne—e’lê´Î'Ú1 Ÿ[)9Dù—¬Qª€ë,/éÔ–ÜW^þ‚@½â’؉*éT‰„ïËÊ9RF¬Ïhª3ZÎófýn—±èiÕªç¸ï 78†SåⲜ2…Ö ¼ÃÒåu èõ×ç_“*ø“Š[é:=NÅn0)d7ö£¹ÕbJ3yG•@ïV]Ç3bò F ¨Ÿ!Žj®Æ'@SäPF’ÉÂ!ÇnNèx¯±.x$¨¹e‘Šg¥ï¨t¼A_·íR„GB3Z6Űp YIùÑYÏTììâþ.ÑÙ”h¼H18@LÓ …ã]ÇYà·B&y²Æ»ùÆÙÖ^Ø;e|7&wcW'3;›ÔG1M«Ø^˜dáe˜•¡sèâÖå ÿ06U6Ûai5Æ!ÿKŠq ¶X†A¢ËLëè3K=çß%N}µ|2Ñ\ÜŒzáa;ŸxˆÁðS}?#Ï·- ™;‰†8(ßôµß®a½ÌþþˆE¬cVVØäÇ´Î(öÍÈmÀ­—×:Xd²{b~O¤qŽ’^MºLþ¬ŽU²Ø,¥4~:[ÀT$Aņ.–n< É™Ú[£\äM¾U¢êP<ŸIþ$ ‹yÖßXA[øÉ3Çlar¤N±ã˜ý¼üe0i@À*¢ü{NÓyëLç­@O…þìéÉ9îÄ’ÒJqL›Ó¹÷ŽVÒ)4ãgU•Ð_ø®Ç™ã‘êªðü4ñÀ—Ý×Ïÿ6@°ý,HºòŠ ¥²„—N>ÐO•§€ùO¹}ëÇšêþ²,Ñ`á±Æn>° H¨‡Þz÷œ’AiÌPÈ¿&}ˆÅÏ`cõ¨!÷¬mIÁážÖ`Úbø|Ü>Œ·Åþ%¹ï¾§U0?~²ñ¡z½þ&q3íÿAñÿí²õo—­g{>ý/O4ÿƒþ(të~Ô!¥¼“{¤Ø)QE{ãUíÔ|·=w ÃñO7ù:î(ÕµÑYO§~œWwÔ÷üjûc3IJàõ“5ÚWúRæüIÈ1¤pJtNÅgÇÖMWö 2Ø8~ìºS2ÖH• È̆ìjÛä2ÊíTìÜÀ}uÜotTkŠü“_¨€- ˆ¸Â®Ž¡I6š3õÑmÜÓU3k¤çN¶OѸعçÈJ†e@.«éÃ,ìU¨²AóŽDÌ¥ ¾QT(mÑ6íÄÒ¹¶ÁV"èjJëƒ9ìIù«`²¤¡›$£ZYàIUV»b*¿¿ö“ðµ”ÿËïÑonüº›éö?:“©Q{«Á·0Ž—û»¡qܘs·1Žë§Ÿc1Ž}Û„üœ‰>˜†ÿgdáÿq{çIôµ“²é›|ò–õDcAJ“÷â•@Žvò[[ZÌõ¡, ”÷#bŠX*L†k¥LRé¼=ãŽìðÆôî¨L˰’^ŠuˆÞ5äåúÉÁêLg\ë¤`÷pv"e<1Š!c±4UúíŽ`ÛBDº«¡±U”YT)0Ú~˜nfëß”ï¾kôðbîD¦âî”ßgª¼Š£Nl;˜;f¨ƒ23þ³çé¿Ôávœß°nW´ížî蟕ޚ&éóZ[ªÿ(ÝzÈò|Ñ; Ô6à&Jeˆ:,f;ŠÕ!ý”dΗ†¯¸ïêΫþ_Wî9»9Ç ¾; Z&l×]6:Û6f?ÅÉhÇž±«ä|ãê§7œ ßF+/¾ÖMnȈ/Ïóâ·ÎwQ»rŽ©?JâwBr¹âœq‚w Ê“!:ï5™ïÿ8ë ?N”_føEóܪ‚mT°Sꛨ©çè{¿;í}€É-ÓÞ?Cmº^oºÙüwÈü´;F[).Ñ‚âðÏPƒÖ“Í@·RƒêZ¢HMzÝÁ§qXúºþ&iàs‡dñ™èLÓG5@ŒÓùÍ2£ì$ÞÃFUþ»Zý#t¥c‹ÑS.yÐlî[ÍwŸe3?ѹ0dŒ)1^‚¤N †OR¨%–«dqŽnU”ž d8¥©€Ï,˜-1ч_ýýq±š{\襽4K±dX[9¾•˜þjQLkf[Ûée[nf¥±ˆ³lÂÒ½îZëi×¶±TR-]ËÔ‹TjUà&W‘}D‘¤B—ÅØ;ÖâíÒM´Òª¥’s˜"‹oC^´%†2eÚŠ­•Ûôz®ÐDµø$Û+ œ$[–p¾%`k½Ð+võ:8Y–õÚïUâoKü’(×ZxÐs«%©÷Ê Ùâ JÑýza–Ȭ²=ÃîqèßÊ™3¥D$,©=,ûËÏúÃè¯q©`cJkE1O Äê ¶ÜÁ7˜|U9ÕßìhÍ |#.ëgðÙídÔýÐGíFl7q¡~wá]uïÊÑ]a¨DËûnÀ_ª¾Ú&ìÊnPtFfe’ï»–î£R¿S"ÜG剺ìuwûŸZ¼qÎ\ðDõ¨''Ò0zC˜žên¯Úªt*=¥9ëU«;Ü}ôô¤9RÞÜ:õ…µ-ôu#ñ½ä b¨!ÀâžpŸ7CCÚ)Aá¾Ê&OZEÄxx¯ñ?ÚZ’5Q®V›eKü<)üsQY"¹°˜ù}£ò-4|´!›”Þëƒõ÷úÀ¹×/s¯Ï;ïÛ}Ày;Œ°zÕ]~óÑ€_ýf³^\Évw¿Ië:§Ê‡ß[çÝ~½Bâyxs¤ïwUæ·¤ÅhmÎ9Ý?Tš¼¢6fŒ.ýâïm—/,ý¸{nA ƒ®Ï´Âø©y€Á I £é[‚_Ãõ[§Z4s…¢³VTéïVy†V®mu¤4™3Dýª÷Ó@ÿÆ7Äy -ö}´Ø÷Ðb¿ê÷RÄ‹Åg¨g…yàÇ–úõ—¦‚ÖHwefï#»õ¸îÙ¨n¦Ã“ÚÿΓ¢UZÇeÿ¤jŸÙŸð¤Ê}ÔMp†õJèëtÝmð¥Å«ôõítŸé¶ú;xQ÷¼Ë«vŸbxºIÜmüÒ´‚tpߺ»]¢%–FÓ’¦`sMšå¢Hn£kÆbDšÍz¤é°ÀF,†6ÀÔcµ ©³Þí0òÑÏàݹ߰v¸Ü.RæâÓÿPe®<äjuÿ`eì}:OïTFe嘩r Ùɓŭ5]NsÒ¸…]Ÿ´ŠbgÀŽ3Ú£’Ci©È›¦ §ì^gs%µÏº\³Ò:/iÛ5Ñ)¹#ípOê«nÐ><Ó»¼Ð¨GjdÊ<á6—{²¾ý{¯ýKjгgóÀay•ÁýGäU- ÚBè³$ËçÈ‘k%GKpuV`p*b_u¡cøgðùÌŠŒãžé§0#×Ú‡ü‡£%¸öx,°OÝRÕR»Zª`mHIK ÝšªPþ-‚ ÞÖßn|(aÝÆ0»§ÒÔèè•Þ|!U¤Æð¥ÚŸ1Ç¿ºY´uk#*ÖÄRH'”+`s,ºZSK;£ñµa­$ÿÆnîúÊRÿW¢fåïïQ§NßK-,Z7ç’¡–!°ÆáDo°¯ÝÙòi¶øêeµSeƒCEÿè 4÷iK΋E~˜#Võ›ÙZg—Db&ïX´`•úÍ—, ÃLÚ~¡²­ú©CôslF¥<Ø4sÿ˜¯xʇ.œPðVfÄåÊQ½O@­]rÿܱ+ˆÕä¶H7¼> š›WO›PÐRòÀ1!CÒg&N6e&]ô6€µFBFA§òÈÅM¹Ìõ¢­¢çÎ+°¼jÊz4N–=rœÐ'e2Ú©Þuª£Z8uKá` íY/eàuß]2ãGò0!W-¹žò­dH˧wxh_—e (P*Ä©`Жæ¿MÈ;*‰Ð+>ê ¦“4{ü ¿þW ©BNHE!^Š©jÐ`} Õš†C=ßg6T|šß0·ZÆò-áöÀÞ}ßÀ?!®ëV »i$MŠÏ¿'@뜇4öÚ‡diSEzV7_Ò¶&q'¥‹>zR™`«^W\ȈK Óy ZdÈœ…mÙ‘š9k›¶õÏ­†qÑ#¬‚D(|éçà1À©Óö·B¢´ÑOc!AŸ»ÄÚ]’7ŽÃ\ƒ¯ ’ëä ÙÄÍwK¿£(¢Ï…œEò/ì#¦¼tS“ÉJY§ˆ™á”úÒqJ öœ€YgªÐ¾s+%ehÒV d¡¶;ˆ0…š,;}³âøBݨ`ª¶ÌÖdV.Ç€(÷y]'éä¿sNÇiç@P‹ÇªÆîâÓ@]*Ë÷óJÝ.‚Xšf¬Jƒy©;_$‡æ÷%ÑŒ/EÓtQ_˜vÅI\q¥*A­mÝðZf¸†;¼¶9F)­ S çï|1Ñt}ù-RP 33’(*‚Ö³‰àCºG&†ˆa9ä‚l€ãFBŸ€ârgüè{•X»ìô6ÉQõwqøyÐj¼s´€Å†ç—ýOjÃËþik¿j雲ö>´2¬j5£õƒ£eÄñÔ/ôÁúåÔúáÔòoQiù‹¾i )ֳƿ;D’ܬR›Óšy©ê6#~ÁX+~¤·X÷_’ÐîpîûN‘ÏïLÙ7x^Î> [ꇋŠüÅ"<¬ÜH0¢ª¦rZ‘P ¢ã…lSçúÜiÊãà,ªX=IŸpÈåªù¤°Jè  µ°oÕ˜Çëò[ܦPÙêÂÁêÌ>ƒS?•ÕdGsð.Ø[¼)šÀž&ß$'ŽØQ\÷7i€Iù[’üfm¯hõ®«úõÔ½4c¥ô-jyËr¡ÙppA›MÈB@ÉW!;ÚÇàNÔŒ¬2Ü¥¸P‘‚xJ`èþ È»îf=Ïg˜(¯EÐH)Êu'Žß/RÛ &–2±ÆÃ+Ý+&4Ðùô!sêrÑ|ŒX‡YzI ðúYÙž•—)x}œç74zûÈgs¾.\…9µÜTZ…kiÔß(¢R•«+KÏ*¢ø,ý&=ãÅ2£LáªH GS£·2`”ù‡ÜH¾‡ö½´o$%¥Úp#KòS=Ëìâ™àKÌ.o’ƒ m´¬iÒÆGŒ~Œ]çt°)/û%³ú'±½la4¹YÜ_SúxÉiL$Ò‡6”Ä¢¸u-,_3O»Ëãy“øÈ¥õoûÉ‹æþúCm AmÈVv„ÿÝ ÈÖkËûÛ)ËuÄ£´…­Î0:3C߬^u@çeéœô°Û)×Ë'¸A·Þÿ!Õzù°ÿ{2¦ý€Bþåìn?WƒŽº id4èC‡Ç5ÿK*Þ4{ó°…MQ™n¤¦¹Ò¨¨8ýYz=IƒpKŽ&2ƒ%µþ?Vÿ™ü‹oSa@y>¦Ziœ`f¶ª¡P½°s¶_Zš§$z¨)è¼á(ËTá$ؾZ8³ZÔ(ŸOå¦Â÷5ÜÁ>wÀ· ã¶ù¾Ôe/&1•»Ü÷‡çh õ>™¤bG€DGëMý—¶ˆmਨÿ«@±ƒ] ÞÆ`úðm®… Ü6@™É(¼tÔj$ÛÛz£‹ããÖ~rm%¶m ëÙ0e™$Õ \:J}fÙ °km ÀÚ `é %ùSbE—=Ûà0ì]Rü•% ‹üš» ÈOÍןZz4¥=‹•BͤØR‰ÇT–ÌÊ`oŠ74Ϫ;5DÕï› Y:¿b-µ¨¨y€!©¦…!½µ¥"µŽp=ÝÕ@³Ä´<Ñ1Ý_ýRq£) à–@µ‘1…à{£– a8‰½V¬ì믜À'7}Ût/°‰ßs±&æùÙ×®pìgæò|¿ p¨’âqؼŽI!0KÙwæÉsÏ™>¾,“ܺs@C;ðù‘[Ð¥pҰǃ€ÂÇ\€“‹3»ƒâk}*gZQŒQÏWú¾3·çC¶Òà΋ôe788×âôÜ&]Ù‰¨V…Äd0±|r•ÀŒÂÝlq™pWȯÞ=S„™H­˜Y]ôà:ž+0p‹HËšó²/©œOÙõ¥÷Ï…^,<+é¸x‘í;Ë Àç)›ÇQ÷(Þéañê£À‘Ö˜o¼è{…¬Äˆ)]4 öBÐð±$àƒÝ2Û<œ›ÇY”Y¤ø7Ss!n¼¾¶ÊZKh•îr{äÎýææì3éújö䬌}›sõ…³ô¡AÂÁ¥˜MñL¶4Wß'¤] ­[“©ïV¢p’½µ+F—êb¹©óôYÕtÞ<“¯ÏHdNB@æäêµä¨¾Cxº4Þ†,z ‡à@+HàÒ$¥%Z ŠÍþ;søí[ßéQ›‰õRF±mœˆÛªêÈü£ìL*a|øÌt…WÈÂÕ/7p­·pyør¼_àÿ8¦àï =FWTÆöj€LGÛ"È mŒvÁ7ŽiƒYÑ6ö–"‰7DÕë·ÔÖi¡ûW¸ñëzÒÀò0稄8–sG†Z#À±ô}ÑJkšà)ìà?Vâ À¡RÀp§³‡/©Êÿ‡Ü4ó,w,TÆ6EÒµ¨‰?WѦ5éæ!.á[™*+O©8 ëÖy€˜WªKÐØi÷'ía¯ºskw-í8×b=ú/<žÿÂÄÈ„8iE²¢ ô×!h¢¸1Ë¢wK¾KOm.j&?¾É8"`>Çæÿ?ý…riQÆ?ª/m¼*ÔÏX·õU£ºƒ£WÁçCeó3ÕF{+¤Uµ$¶ƒÀÃó‘š(>GÔ#Gmå½( Q½°ÛÇùaªë0EZ@é l ,±!{ªÊ°­\Ë£Úl–g´˜æ/)y=ƒ‡RëTd±­#³IzQ#bv¦¥))=¡tÝ?ªéȈh]´TÊÔ‹’Ò¦bJ``ªI{§›#Ñ–eÛ'ª$…zEÿrk _¸ŸÓ@6>©öuñ±kuÊå‰,ˆ×O°ß%ò0󷷻ꪕ_D©¼V¥üL…²¥=.´­:Yé}‹=lí#^ÚÃ6ªäQ$¯S#êô€R¤‡C#hµê¡w©nÛølð1LÛIGÏy‘JrzD‹ÓVB£Æn–H „Õ3q!ƒ£º VV‘Šà ØŠ°Jôæ¹CyÞ ÏOY0=û¥;*ù+æ´1È«ùn%sõÐ#oJeZËjq w´jÖÌ®Ïè‡[׫ºØý¬f¿Õ/ådjØô¼ª\p°*>ýæáˆ¼0(o§õ-Ójƒ97«·¹×W el5qBpx…ú~|°¥Ð&ñ1i”bÉ™Œ<ÙsEáî#›ÊúIì~ˆ°)uŒ6`ýœ0wUj8±F¤ŽØÃøövzƒŸgOÑdšC¯7+R2”vCnÅ G®lþ ™ªëårºWÔ0ø¾£ºÙÎq°ø9„"Ž8ºì ‡ŸÂ†‘<žkçðX’%ñ, ‘1Dú—-!:"âÉ2Rõ)s^déT½³HÁÒaõ1>ŽÉ$rm&‡'íaû­ç£;¤DáÂ1Ê.r«ë·ö£ú¼Yqí1ŠK ½~¾PsjÛüÓ+Å÷^Ñ#¯8“=ùŠËTëÞ¸ÒÒÑ[5Ýã„õ¢¤³"uGõ•Dþî‹kŽ‚‹º]§ €UCƒ¿0¨Eð”+¡c9:q˜Tÿâ:3|U ‹8ÚGïpkêÖÿÜ#§z°«x·?BïSþC œ/x,å£eJE¡Å P-çtÎîJÓ{b‰tù[«->@ø-6•¯…ª ¸v4Ë_†%‚n,žŒX~ð!ãªõóÕ Áa^°†ÒµÊ¨xž}C9£VÜØa-Ã^<0.Ê–Të·g†N+~X©¢ô& ¢MÎ)ê‹'lƒ.ˆ%LGͺ5®„~kQ¯}þþ¨½=6šx ûôHÓ+Á/ˆÿÞgwSÛº©¸+Àµ,j2÷„l*)Üö´ÀƒøO•­R zÖ*AÓ5k“q“uOšá„6Ћˆ ¢Ñ3öÑí½Ôj©š¾~w,ñ´þ±Ò¿€-¾nÙâc¼RG¯µÀ—#“Â)Á¾É>`ï-±áóoè/¼Ûî˲î]¢,XIÓ0_tbIŸ3°øœgñ4[_QB”+Z¸Ÿîý‹áyå8©jü¬¾!¥.~ÙX7îÁ>ŽÛtÇ|M{ aÞ‰Œ—¼UTÊŒ¹Ã]æ5w1¢YåDGãÜK]²Ã¨í¢ s#$Bqnâ˜*š¹)kΰªÌí-"²Pš‹†øH]H×øRóEèÒ`êäŸ&YÉ¾Õ ñ%Iµì¡~J VúÑc&Ò­aHõYR;íOÝþU¿à÷ pôº¿Ç›8peú{©½.Â%øQÀQ3ÁâZ;ÌþŠBH€€ {Ð Q&Öm{nP¤MÔà Çìh!,°ºIQ[u-<ôÍ€†>!jâ- X™—ÄF€:‘[,È´´0zMÖ£Wí„Ô°gáKÅTÕŸiÝeKXÙJÔÐë®çµV°î®£”þkåìx–÷qŒYrˉi‰™––ã›iíöÇì´ÞúÈõHRˆ;õy¼Î`ûNI›+§­-¢[ØÝÀue  U„ØŽn7Ü+Êñ‹k¥V¥`Àµë| F è!»™Þb¤Åì).™²²/J?‡±‡Z¾= ̰'/¾kË›KÎvÌ2s£É5»Iãß®Íæx¡YŽ;ö[C!h~Ôˆ0Zo¥SÊÕ‚™¹h’b·ò´5¦·)|™ÇÚlG‘°x*ðõ>i‡U]´Â˜¥§X´¡&Ñ&TWÁïØ¨° >^ì.®Ïˆ# à#<`…'Ù•2¬ ·Y•*„Ÿ¡k‚ËàlPã¾ Û‚/ F§o¸eç i/5˜l3 YȆқxiVLF™¨2v’• )eºÊtq‹ $…â/®rÀWû+ËÕây»òq à9ÁÚz<]æ+¦ DéB_?EÉ«üïËU¥_e2‰‰ó4­LoË}K—/¯*õ%í9,ˆ xß„íQô]ÕôSn.jC±ÉÜÄ=U†X<`òôÆ¥Èè/5Ò"! 0¿àôo|Û2nA ðÔ,¸‘I½›ÃN=æv:¿PsZ¦wz( ž=‘e,H‰ñQ’ –ÆE&6u•éo¾°<K.‚1jžb|‰EºIçŠÂè…/¹­{¶¹¨—aøÙ HèW-@‘{ ßÎ7K:‰®HÈ2Â%›g¿†AŽÇ8[JI£Ó#BßNÖÙb"ëFÆz60²×o\ÓÕ‚nêú’²`ö|уøÆ{aö!^5…Ü yI½ËèqŽVô-záI¡ô‘­pÛötÞ¡—ª|q6PËÖM%F}‘þ3’ÓÂØ å"çªú²°VÉžˆjóÅ|žÝ¡¦`ºz Åü:JM ‹* ²JšI¦é%slëÄ"9ºû°ÜˆŽøÖüg oéšúp®Dìç%J[|Wa‹e-ö6-7vM©P?–.êvr€wpFˆ¹”ª‰]¨Ò<§ÐTm Ï Á’hÆ’ÄH¢'¸«»%:m/{k»3ÆnŸýjÖ8¾è‘³ÐùÇV½æŒ ±â£og€¨a˜3åyå– •îϵu›npYn‡OR~¶Ÿä}ŸÎƒÏþü¸ví•¡‹ƒRB‰¹H+üÒ‰¹ ø’½Piä6>ê/¥œÃo„q¸Ê3ªÊ%}'£¨W7‰SëÖç·Óß Ÿæ‹…–=WyRøÿ”“MO?åçÇsg k9>=¥(8%!V¦—µ¨[=Õ Îûƒ7ïÁK*A…T ŨDî%Ûør·7äI¶-Á°Ð¾í+öL‚ŸÛ‡ÛSŒ’BRÿ¿°f=ÙøP½^“ü#œÇF.ß1Wuù DZ—t{¾×Øg0¿8ÓŸÛõëOàõïbG?\ìèûÜSÊš=Ë;¥¬“ÿ=õ„¶ñi‰*ÚSO{?þo,举|T—Nçøvìäƒ:¯î<¨`ú5èû4€²C˜v̀ΔïÀ$ä…RcÏðŒù—r5[xO ÊÇE„½žJvn¤‚Ë î0ÿ‡ïÈ?J‡ævÜ!*Ïîá¦}u<—T¢z‹<@^¨0QŽ$‘ ˆøÉ´'HÜf„Ó‰Ðm\`U3·ªçNvUQ‰Ù6o²´’¥°êjú0 »Zþ¾~’ Þ©Gâ8ÅÍÓmÓNŠñ¬mð£Å³ºš÷q Íà¨\}à¡âdHç êî½Dô ÊjZìAÕ¤O…p$Õãù=úÍ£KtÅÅn¹΋œ’©#Ï+Ù2çR±ÒY‹žý¬ËMÍÿK;ko±èh=­çT’*vòg­HÅQ¹v9ª3÷èg”ŸrLØ¥…§¶)1Êÿ¸¾’Y­YÒ2€i¼KŸ¢sbÔBl©U‚Å®ùtD)-´iˆÒ>EZ ÇÆ%SCé#Uùï¬âƒúØ ‰R¬)ªP½¡h×O!aÒæX… sØ)s—Ö-‹–¥ëÔow½ûa¼Š7[_½ ;p;àÁBœ‘Jªñ%¢+ôf´T¿*öâ_ª¶‘¥½W9ÑÈÚd18»ûºÄ, ;{ÔG¥ZdÑx[vMìD¿ßƒ Ø)œÖVr¬)?»ìÒyØmâ¢w$Qú{»RꙩÓÛû&h¯—¢BÒð÷]ÝÙa¯ƒWçn;—=á(ÞwGK[äÈìÇ`»-ø ¶F;öŒ]›Àךc¬›ŠqZP«BåŒöRu‚¢…‚:!CóÝ‚²3‰]hM‘ M™ç^ֲ̈́« •ÑÙ³f«ºNöqÙ…‰LŒÞP¾»@ÐvU{‚5zža5Xo6p7"Ws*ïüh»Ä@€³{†… žxñÞß9þŽ‹ØHÕšs8°1s3˜†IʲÌ9òa6ýC*ÙðQI¬A.WV… ¥¹[ƒ’‘‚áh4è~8— ŽYÅc9Oºœ’>Xb3Âz/‘qÊ5™ŒLy”_]U-Û:­u¬W_ÝY ÖK¶X-ùͤÀ·²óâMµECï„%DT¤°ÃušOsU¢=ŽN€ÙËæ€‰ïDEGÙê¦FJ?Ö‡¶tmŠçŒŒíÙÞÑ"L´D½÷ÞÊÿ…rŠ)`O)‘L&éòw<,Š“‹\–Ü[ŽH§duüY¤Ì ޳¥ßk¶ˆ¥Ð˜æ A‹G”¯7d’žD®á°´3è„c ´Â@„òz54–©Y®ôbw*–:UÐ6C÷Ú¢ÿ BhÐ=kPt*IŶ(ËBß"k¶¬àãÃÀ9jšý>]± N{÷’2hY­Î¥3Cá–„ePF£Š õÇ€L çSÁ4F₾k~×Kµ‘BÜÏ] 6ÂýaI×N+¥óI]Ò¿I3¥ûöŠ 3IyK&0ÁõŽ`¾}øýΆÀLX +éÁú{'¸½çøìPzõ0Oïoä`Ý$ÐÅ`Ôó7·×¥ïáŸÃãŽÒáçãè ³ŸwAª^'ã!)\»ŒC·­ä5¾ëu[˜éVJTCͶ,úL³jaRëε…¹s{0ß&³íÑNµ¯«/Jéè4¬¨$TŠ-´‹£"tˆt§œÝÞJÍÇ7,¶´Ì_ð& À¢Ÿ=ÎúUbÊ;”Ö †ãÑ|tOK…pVvšfõ-ªþÞëWC–¸ãÑ©𽆈³sÝàÌ$ æmÔÝÃÝ€àAw…¬´W®«²òd~AÀø˜g†ÓAÐÅ|•‘»ÈÅ@³Á®fï³Ò¦˜¥Œ’øÃ*ãíò8ŒÍØ1½·€)«ÉÝ#æ¬EÚùâÅÅ>YÒBO¾Ù¥|ø¤úá„Î~§ŸìöÅïñç]êz‡Òpt†?cƒ†Óßž0þº‹¾xõJ‚>½=„£éý Ÿ:êñ¨®V¨˜<­ã¤'+Ô£½¤4lnêõ*ÇA/çóK¥ÕsŸÚÓ½xšVAã¨È`ßrÁº±*aÅwýçeàe+f7âÛç|EIøö9_#·+;]PCó­,ã,‚ŒÅ¾×(?{i%JÔMXÑ܇…a TIë1¬‹`AðüdgŇþª‘©J„2*г¥èòyzX&ó?]çú&úsé\ÇVò<å–]Ôœ¶a‰×ÚTRåC^²|LÉe)`%ÿ ±qª€°æ‚X'†Úyþx/•2/@Hž¯–O®oJ­h°\\ÏнÆŒUÀéRàÒÑ|1`òP"S>©Yã‚ÏÙ<ƹà†TE´¾‡¶îFüº×tÜêÄqù¾rgBþ·ª‹›b:É—“¾°š¡­¼ŒòVŽL93·•!Ê,_°¿2EK…üÍÔ× ºÔ2SŽ.©F–P¾¤[$ æ-æCu—'X™xñôÇ© uù(=ÌëSe‡º]àK¬òÆtP«:›ƒL6'Xî×/¸Ë5Š3É)a‡lédšÞ‘®Jüí×PÀ—(5Æ–ª‡œÜ¢8½Œt'NÉ'vû‚`mUC˜<ÍÓûé Lf•³<öuBjt{ú¬?Á)-“Na%Ö^zöM+>a|LqîsJâsŸ"l-áªß‰+2fcÅ‘ÈÞˆóuAE2;“ iäÑpAŽS—-XÞçäLéCú!F¢KÉߺpgŠ H^wéè¤pH§`l7©Éyý¤1*`þOÛ!ôCÚ‡`äþ ê¯lª6¡8Wò%ú!¹ªL  ‰Ò7œ™4£mGF”fÅ^`¢¯`dꃽ‘µ ˬY¾—q °xQ&Ú6¨gvAZU€w1¬x.®Ó[O±zñ‘('8n{”8_6§¸]¤7‘ZìäÛ«Õ„Âm9£TÏZfÄãCq*SÚ˜¢¢Wy ð'âa7rý²%ìØè ÊaIðû2K'O‘]6_ØU~†²¬0*g³š7ŠD›ku;§ø7ü‡Q“Éë0ñèƒ8k+ÅIqÐ2êe=‰ d½Y~*¤ø£÷²ó9øøñZZ6×.Œí#\Å 3¢|a¤‡Â²T/ шKªS.oY¼-ŸÌ>ϳ¿ã«àv eL}~×DÊ„â&4®¨ÒXèÅwØÌÔ'lÃë‚­Hçwˆ•Å‹^öñ¯CÌpºÀÆÓµ‡¬§Ë CõO@mø·/‹PÊnd§’g‹€†ë>Mç€iöê&7L¼Û;¸¨nU҂ϼÕþ•÷™C¼¢>¬{öwšŸYãõbÒo=Ó›„ÿÅAèÍßð_Óé'•.ÉBE–ÿÊÍÜêeTâ Um¦4v\QÜ]íJU'¬Ï`w8ÆÓ8B×ôB1æÙ†á5‹ã#4ÖÇp€G¸ ñ(aQ£Ï1ƒÄ¸£|´×x¸Õ«²Ö9ê˜#T…©ÒÉ\ò²V× ±cT—Zêg‘¥ˆuñ1'8åoÐùâÞ ³âX‡Ø8ô7'h奯<|v6”õDÈ!³â·cD×ÌšÆ6‹çStWöSì6öÔ ™ýjjA¦ª¦Fúu·žš¸lm,¥f€»˜¹Æ©[™d0 ÄMd’Õ}¢$yÈì ì²ÑÞ?®?8öÒtzi†{ùY£bù6!bð¾NSm»·qôeñ-#wH;jWš°|fÿP Á(JªËŒœTT›e5u^+«t3³þÖzù<”ÀÄ›iŸ¼zC+Z¤W(æ,lòÐŽ‹ cä÷€š#•@ÓjS0lÁs0“JB·4yѺ˜CǦ…pµ(‚’0ö3pãëftJm9ñêâ(_$ Ed39jì‰ÐéÜ›/5ÍÃèdMvó± –"Â|CKð¤¬)ãùã|.©EP&FfêˆÔ>\¥DÆ*†¡¿ÔüyÃh\(³óïêÀE·^ˆz¯÷Âdæju­ÝüÄÑ.í÷íáΧJ¯§ÊÑ7,Xzû6_t]”˜ØyDŽÕ#¹*Q+Ìù`œˆÑË ârôjv¤žASvsãtCj7Û®Z› ^j+Õ)12À\›ƒ¸¶“ißߪWÏá§©úÒLm)ùlGL)ÉöÌVçï©@»™‚ò2éœ*Öü>ÅlÖ¦¥÷ŒKÛÉ™Ï5Ó@| q/PÅB?{CÐV¨SÕöûWÅ‚÷zÞ²à[æJ°Sq¿i¼¬{+ƒòîN‚SLÝPLÍí£=ÔÞ’Ö£{±h\Ï-­¾vÕq­V‹5€«dFЃREO£´”—ˆ¸z’øM"‘mó;ê±íjŪ¦ëLk)XòÇü‡4u[ çYk`Í?áÎÄ?;­4vì3E㢠Q…~&M…sàA¸^ômU¾X¶5ÈgM©MOB¾áÁÝBâó(àݪ‰9ûf$UçqÀÙo]:¿E($¢ÍuCâ_0ww§á}jÉ¿Vºª÷ý¡|¿—ø¿àêÑ™aDßÔÝß{•3ºa;ün7:„~Ò=võ¦iâú8òËf‚Ì ÝƒRóÃoa†ÿÿö¾®­m\kt®ó+ô¼7'IMHºÏ32m†4´ tÞöÎI ›'æ×Ÿõ!É’,hÃÌž}ðÞSÀÖ·–Ö÷ZÚvN³ ɘ藳0økðùàËŽm™]÷‹¬`a£”àßú± ýŒ¹/Ø›ÍVì¹hk›ƒ4~Ux.ûý¦@U{œ¬t ™175zµ(Ä>Ã’™Ü3ãÓ¶AÌãÄ1 ç67Šs‡Òμ¬*Y$ý¡ëy’Í‹»6çï%S>î_ÒD„ÁwMçpQ† OÀ2/8ì6!:µ–{Mé»Z¨Áz…AP¥a˵­G©¡MEò¡ñ¼¡Ö¦•B:…½ö3GË\/oë­±È"ŽœyB<-þZe‹öå’˜“‰‚­uDæ°ÏÚzÊŠY•åM¶});é¬Ê†Cr÷€"R© ÜÒÁc¶TÀNǚˆue[¦”zT›÷KgîMÛô Qì¿ÉQæGÒÊä-ÁV¯Œž,_¸_Lþqû… âO&‡ØçGí³³å=„QM-¹¨CÍÖPÌ2’YJ3×M‰EyZÝ|„p¶rÊé%›ŠL²[JqTüÅK,yt6]l˜£+sÉf+ ‹³’õѰ‚h¦NüŠ[c4à:|kß‚F„º–£ò#4h`A 86¯ý2„~—ŒºŽˆOF1 ò9Gü0NÑÛ¯ÄMØ© Íô:Åœ¡±L9ˆa>Ý¥q•Ȧ&Žä·p¤00"%|Ì@T»¥ ŠímÖy”ÌK@†UÖ^ºj.´Fù屡óÕn[Ûæ¸‹oÅn°¬¦Xý¾CUÑæ¥ÂÐ0ŸñhZ\õŠãŒ¿Ö¸IOü¿Ô…^Š+™4ÚTÒt(õu™þ¶=ý ?ëR†Xƒø[õ+žCôèÆí:ú½^ýºÕ©ÕðWN`žù\[ÛÐStõ‹¹kÿ-^R cÌþÀ^æ¬þø÷*iiù ;aÍe˜ø]ñ¼ë¬ á”Êdød’]¡ôéy2Í&8}?ß`ßô«NLv$Žyž%NÔLƒ>)×ï‚­½>õ¬_ŒˆÙo3_™æg:3åY¾ƒù”pBßÔ,µ¯" vú„51*U_b«š.ã(Á\(Ú=“ζ‘&¹Gºã³÷#1*Íçll1xB~$ˆÁÓŒ&^$ˆáK™“®PU^´œÇP!à]¾9-IÿާclfÊ__>è–"‘Ú£P« mÇÖ0Ó¢ZîíæÂ1]9×Ög*½aqÀ«DÛ?u¸Îge~Ê¿ûçÝ»_Ä}ß8\‹¢/²`ý0LãGD # œ‡wxK²Îü K%{¯¸åŠ‹õ¨±SüiÎÛ$š¥ f¥·ŽÇ]‹Ø³–ÊæC\‡cÄyvn—ÛQ°g6:ÿx<Ò~#ò:š­­x ±ÚºU¢îA$8¦Ë“BŒÍ²q†ä‹í’%jlØþ8}„S (÷eö]&¹Õ{0 ™N³àj7°Ì€ºÆˆ3rÌØ,`8D÷“(šfèèÕ¬WWI¼D60™¦Tý·˜òsªŽš ª¤kÇ8`£ÒÒµyg"½ žs*ï7dÛ‡x¡I³Ñl¶Ž›uY‰„§/ä¨f93F+Zúw|p*ÁîI ŠCÉá:¶AîNúcî³î=ùï["¸|¨+©f*yƒUêèž´ãzüé È¢2¨¯yìºß…ü}•Ñ¡Ð(ª¨nÍÝ;̤W…zÏìf>Xü’»èwÖvH¦ÓÎù½ßOKE9¬¶YQnS'£ìŽoK˜“®‰(z¹©*È\š°H «y¸›§×îR‘Þ2¥ ¿‡ñ m•ÿ²YbôÈ1="C 8QvUž&£Ú®É„½ §ÀHUÌø.¡vé[Ô ¼£$‰Ò6®‡®Æ›¯FezÞž$v{Oy?d :ûGÞ¨OÎì…Fœë÷è<©LÎòÃðAУœ:B·-Ù7C¥ÒK›«ÅüYÚb‡¬õ à™íeP\.;¤µ-þ*£T¾ƒpé9l_¶~t0Wœ)“ Ç2L¹%ÚVwf¡B—EÀÒºýRåš–Ùôe5x–@Ë[ L“R9¥7CÞœ=râ¾Ì€g:pL©P¤!^©ã?×;hU–áqÅ>³«‡òº@/7õ N‚tÐ&Ö0ž|¤`|–? Ç¢]/; °͈ÛÉ•;$2i{Uzwá<Ø€OÁWb-˜6… EfV̳ڿŠrÉê ê6¡lH¬RˆËû[JàBkµd4'ò…ú–Wv@– Ó¬—4 /² èf¿G‚ÕiüÓJœú³$rm6<›¨†å”‰Ž¥ê÷Ÿ:Gú®ÙOtÉù!@z”4¼c3E6e1GÄ \ÎðaüÒ²7`¥ÓA·æµ™h;¯¢û9Ñ/•~ ]«œ¹ù³Ê¥qò£é’–¥ 5Ž@z. þåÎÚójOB™ä›ØRÌàþfó9Ìhã in¬+ !6µ= ~OU.Cã Z׳¾U¹Â8!7ýÛGÿä¿4¯ZåG a'¹ö cí3üúJ×±R²*«BÇÈ裔Üùs²Õ*`G7 CZlFlåaIú¦ŒaÇ™ºÁw7S¯þŽiß‹g¸êÜ/mþò¦Uü&ƒ Ü×28áM!;€¯GuèØûú™Ù z"O(„nþ\¨•Gä·w¿äræÙî—ëªïµõF1ô>ÓJáz»ë$WéÄ] ¹Fx|/¸sð•Ín,üµ|-ÍÀxs1›ž÷Ü̽ÞY?!”ž{LD´´Ì¸ë6MÉs[Û…½áË!8( %0úõÍYýÌ[°}k†¼»vÈr íí3Põ2=2ëaom_©Ÿ & oßPHÀÈz/‰t9ÓjÛ”w]b¨, ¸ÉÉKxÙs:7_6r8Ð2ŠJ"R|%´€!^ÚÅYÍ‹"pñÈÊþ"åN§#W=Ëà±Q5Î~Ñy@ˆ}@ˆ}“97L¤nhL%>äßÔ0x†òÆqdb`w‰×O\¼®qÕÀPyJ<Öx[ÀâüÍ •€½jÑÅÙòZô—ÒRä¢Ìu9ñ,^¶Hé fè(ïlò©Wd›Òû‚Pâ®JUH;- ^p¢0Á~|™Ö6¯ò¶9¹{ƒ|ŸäÞ Ì½+¥.­VÓÚ^›°IЬ…D‰î«ò¨ñÖQ–ÁЉÇÈF`˜òûèÃã”!§ &ìN’éþÀà]ÒP$ ¼EÞsÁ„€>{ÁÀÁükT—(ƒ<žÊT—Íf«.×i,û£œó ¾ñ¤@ÿ\Ð%¡U¤Gÿøj¦I]\ē،R>{b8ìÐE~ÍØaÙÀp:þ¹÷ƒA딇‡FεÏt¡ä?NÏÃ>kÜ*Ýyu=ëw«eZžÏ¬ß9$õÎÓ5;z#>â};ÈQ[ÄPB‚¸ñ¹~héØg¼¨¶ÚÃ|/É`‘¡ÄG²ŠŸmiKò‹Ÿ]Ÿ˜˜ÃªOX>DaÙ~X]+Wq¢Z íð˜Z‘„oxÒM«žÅ)ôäл:$NÀôa('‡¾qâmŸ.™ŒÂ¤MCë Þ‚ [Ý©¶È®…У2§(ɸD@\Ë&ôGÏ8!Ü.² ¿l晆ÙÍb{:‹“Û›ÆÅ†už&̓íÖîöþPë=™EÀ&Í1yY€FÅ—=jÏyt# —i˜Ñ'lÚ ÙvÕc¾ž u«„}Iu‰ñ,Ì`÷·Á´ƒxÎÃ{ýeAa 2{VåPûÒ!#ÜTp”ܶ‚YÌ[ذlÚ‡ÿaßb–5¼%FZu—¾¹JÚC?LC={YÅ·” xÓË1Ä~S5ý7Ì^âhôöÁ ®‘½6ÎËl+ €_ó#ÝëÑà–Â'žf5ÙºyÓ B:1ê7¶U“èé “™ÙB¦j~Žh/cøêN•Ò-Çxå§9œ3Ðaó$N›jê»M³/µ¡=/‰êÅÌe£3nïFµÕr­Rb “á®Ý/r@øùÆûù†?/-;¢Ê*éŒYeª´(ƒÕäR*4§ÍmO‡”„Àw)Xâ.Á^Ñ>9]¡Õ¥r+h÷Mpä€j§)®|n¬Ê麰 ¾EnY®æµZÍb;rA¯õ‚¶4mzM¼(xò ‹"½VWÂïöG·Âƒ6 ˆÞþäÁ½Émf18“æŸûM·:: N/1zXM‚ÄzÙ*%ùþ#J¦ò7›#Îf6G<®Óñz– <ÆãªÍq2t”…z|½ž,Šº¸‡yŒ12 ™ˆ0ɪaM½•¥Ök*™v)6õ%˜TScYÖ›øOæTÅÓx,r!-Y¬Çf<.[βÖË‚F›rëgLœ›j3•jÖŽ_yÍ®UAÁš¬WÆ[3Pà©REÑ ts}D(Pë´ô(4ÒóÚœN3Œ6è"“—I#M‡X¿t2Y-MpETVk…zèay¨Ê0„Ül4¹Jâ ÉŸÌÙJ–339c¨‰ 2ÄßHtâ£'ëA·Ê¯Ûæí™&á0m”Lc þ´O’ž‚]šrÔ¢£†-¹L‘!* 4úÖhÕ±K &U"ƒM£çV³È¦ü§söÓtùC=V \¶Ø«±fæù“ŸG”ÕH"Ên ù¥!Ö 4²rœ2ï™ü>«?ðW›ù-2ü¦+×y*/Ï/$ÿÎ|yŸWgö˧ñÄXj¹ÞquÏk–\ÇœñWÅšµ ÌÔŽýÆ$šZû.V„Þ-¦ŠÂ‚¸Ë…²†sC»Ëº÷›±õIÎ øXobÌ kaLïf«EÓÓ“ÓSó²“jÕ›’“ôÍ“áJ·À%lÀ[ÕŠ f …YâY¡Ùøæâ.W•~n‰¥¹hoŸ°s<¼Y­¸þ“ðÎ> Æ;GáFžàòý I"D–Á£~ÊN–ïí{ËÂùO>\û¾ÃuP€§wîtrî)"Ä•êã©$óY0_=¦\Ï =ªbO‹Jöñz%û˜©´¦½DÁ>.W°_LÁ¾N O½KP.…X¾r¾±œ>f² /£†0œÇ<šD[< xj˨ˆåx€xƒ¹—‰W|£›‘Òns‘ÞyF1Ÿ½iéßvþœ¯àêÔ£„Í¢HŽoÎìä8š– »D‹2Ë^@óÛV).¸â8@󑌟ܔ“ˆv 1ZƒnÖ·–gîÈw‡Š7u¼ŠÝ/Önò…‚Ð !ÕÄž Lïù‡ãô{ô¬ÎWFçnr×ÐN.tìh°²ÞZƒŒQúvúE"RJîÈc˜EÊâoU†™R%Á[ªFó*|!œz™FÓà)ÿÓµ0–}•¾ÂÞ¡@G§&À±»«ì$ˆ›Áup½ ®ÿ ®á×–6ÄÎæüo® ²réT Z•6W54FÙòZLüµ'9-k]c¾ jGù ^·òC?¯¹ª")·^7 *‰|þ„Ú0 hÆy©©—¸þÓ’tÚ€*ó™%+"€sÿ§¼fÚÌÕ1¦ì×nºjG2ä!©á\¯¬EúãöS­Ù-siZ%º­{6í]Ý/3 <¯?mOSÚ¤?Y]œÔ,³_^@1Ê-_¡TL1×Ù•²Ùõ‰ò{¥š’G ös… '[¦Á°åÏæhʇá̵1^jÌ—–þùæú(ùûÍú¨ûY»þ\5öÖo×—½ûvIÙöå ùŠÌ ûþÛÚ÷}~ÀmK޻͙øép3Ô (ÈžZØc™ˆVýZ rp2}Ê9¡ôsÚ‘àzå®ëŒÀͽÎNw9÷}Ê·r­ÿÁ|^®ŒB· 6}^â… ¾qâIŽ…½ã?æ¦3Â!¼{ÌCÃïdøãc. ­5hÙÔ U¤%. ¬?ï¤0FÔOøÆYÁV1®sYÐF×iaüˆÓKN›÷\+ËúøQÏ…gë ‹ž þÞÄf•fÿØ‹©·6ãrQºUÿE.Á«ÏÅ“ÏÅø©>ã¿ÕçbŒ>> óWù^”’¸ÿ& z«¥þø×¹eÌÂ`6fßôИ+qœ|Å%þ5~%nsÉzE~ñ¿ñˆk‡X+ìÉ%FjQn~šˆXkª%® â™dGˆÓÔßéâ“×ò¹±•AòsHŠ-(g¿“R£ðþQ/½fp<ä’ÍÆÿ@G“X7ré€õ)[wSM×ckÞy¥€Þ=мt™ƒ‹(õqO@?¹hþch5^Žfžæ#Êñ‘x¦)Ç×ÈÔ?íÃNlPõé}'“û`ú€?rAÍ–a¢’ý„н­ò¿é&ëÊ*Y¤ÄpÍÒô– új³,¿l6M´uôX€ä¥²@1 ØÙnµ€1h¬!¶÷(>Oèß°¨jÇát‚~> ^ý¶ä Õø¢š A¾Y³Ö ÷yMC-à|¯bËø½Õæ¢]Ð_5'bœ±I:å˜ÔU³7–ÞºïL yÜüzÞ|ÜxF5Wð`þ!‡'±Y£bÕ­¿ŽoMÕ{å¬rF¹\Y릆 ? àÊ_àÌ…uØ‹ø>rê<Æ>±ëü5ö?ðëIaý€ë[ÂqjÞ”—Ù‹cVÉ?ÙZÝ òºí%<’NâwkùBÀkÉÍYzú;júskújöÆäU8Ž«|‚ƻ„$ ms²Ìíßã TƒÝ'õª `Kªÿ® ÒŸná¶Q¸mn ï…wìÂ;5—[[ÉøqÐ$%”I”Ðml ž@ …?5þ~(Ç>Y)úiý<òÉùdˆ|6‡yþ?Ç,™Â,Ù? ›ìÿ 6Éä¹Îž„BöŸBöË»ÒoÞÈ ˆ"3EVÀ ™2˜_w¯»…¯{Æ×½Â×·Æ×·?Žd8¾Ô`trüB×_Œ Þ| ¹ž§26›ækpœ¥xE"“ ¡³ð ”²‰ä“)AÔréuãZçX¡½ü„‰B²aP«gQÏ?ótîý­÷ŒíÝ{þÝ[Û›þ i»—žßôÜKÃï îÐm³À®]`÷yg®ÉJï&£Êå ¾-ŹوŒ,w©ºRë?BÌXFóÛgÓ{œ½é9¯°!çÝ+[P"pȵâŸoËu?6Á$Èý17÷/à~3yÖ„—äiHÊô/y.š*ïZþEÈ©nH!\·. LU·ä…ê–€¡zëEGo›köÿx†Jó¶€5  ß@0— —NôüœD÷˲…Äb²ZÎRúqâ*^FxõA¶šƒ ƒ.€¤™¥ —hÒf}%¼Û„Á¾5¹ño5™ÅS€,}ËåÛÞ’üKÎè±}I;PËÙ(’J‡TáGô§!h^ëÇõQ¶š1Ë 'PÒ¢Kò¸)+ÒÄrnÃÓИ5fá]†î³­wû€Ñ<0(Àj!n¯Â,óhy•²ó€tG"0^ų%»Å1s¦n2Ò¶¾«ô6ºXQÊÒÛÛYdË< .žgx¡%—Úv>M'dWg?Ü7`D­rÀß¹x«½/¹ì6Ȩ!xÂrŒné4c·ow$œ¶;Í݃¨õÎ^§©gÞîó2éNŸÖg>ÕÙéA£½Ó:hM·Z-{²²× `÷箆-‡:ÕC%¢b"±MM·±á@p(.¶×TÀ…oᲫ UÏîÁ`s¥§…ÏLË„fC‰áôkefnr;ÀÄ…Y_&Èê,S“$‰µk' Ô:3†kw¼Ï8ºŒv›"VNNCñÁlÖåñá0‚êí=øoÉw«©82dÁ¦á8«ÇT3БZ‡Åeɹí¹sCÎAMΧDĺ ^Œ8SqO4’5‰çÄoyŽ+Cy|°k† ‡²ˆðª]:¹7ìIT0ñƒíï²,‡61Ü£k»7ô õÁKl 9[ù 5³§VÝX{¤­²‘ª ë} úìGùC:)â\¾§1¢l¹q©¹&ãR^Ý_UšÍß/_I RÆÐN%ÜЈ3 Àˆ¸vËF 'A‘L91b…±+æAnIË3\$ä´À5c9óµ ÍA~ø ý^U*¬vŠîÓUOé>é­jKü}Ȧñß7‚‹ocF“fïoGmÌž WuZMþ4ZÊÄ9 Ÿârt1cYê2בe%C!צX¶ œGGp°ç~„—(º~÷z:r \çñtk ä isé*¹\W"Þí¡ûX$‰©úƒ]V»pM0 -ðhj¶ÿT×»Ì[AUò=`rð«dª©Œ}úIhmàfd+к ïê€.¹ý KR#rµï{"C•Ïßh!)…îNE8½^qšyÏšÉ °Öå¼`Z8«'Ûñ•LªåW•벨âÆÀ›L:~NvðNOŽDWœ*6l:FvìòO«ÍÞí!+}Ù½i£Ñ‹<(ë––k}W[fAR†½”âÛRMmÓÒ(Æ«~ÊõYmCŸežŽø¾VC`œ~>}FI5ÝØ`,w/ǰçÆ&ô킸œÒ¹ç*ªÒigiù>ašµÅVá€üݹݟòpþÿ~ï¨;u_¨õùÿ›{»ûyþÿ6¾oíìïì¾æÿÿ+ž£ôö㪓’®v@—Ç Cwâœ[þÏG@Ià&qØ•Od#‰¦B^¯í¿-†}£ûá8]„Ëtñ *Èìc¼˜ü¨!>Q˜‡‹ì*ŸbÐP·Ï ë7İ—Òü™Î¦Êqgt2ü—8?ö·ŽN»[8î­æÞA¥’.M'Cea´øŽ©!*•a4wÇ«<áG†dHdéj1á0óqœ„ ¾€. XáˆH^_[™æ¤¹À"8…)š‰0YÅ4¿c<Ïû £#² VšGËU* ÙC¢…–c!H*ÌEDYQ(Rˆ¢f&j·*@"âI°';êa°¼/):™ÌÂxäŽ"z•J»Ø9¦qÉg¯:gëW´©þ«!¥ÁÀ I­"§¦TD:Ë6f@/”n%ZÄ5¤—˜ö3˜3€Y³8ˆbªEzhÔK(à=Úî÷}ÔSëo´àñ2#$Nͤ‹¬2èŽM™•8¼Åà*ìcWA¼Ayð=UZ2©Ò‹å*ŸÔÇÙ-° 3P)FH’Op“e<ì³½‘þvöGgØðû§áé—Þq÷X~gºâèôÓ×aïý‡3ñá´ÜŽDgpLÍ {‡çg§ÃQå:#¨ù?ô¡3ø*ºÿûiØÄéPô>~ê÷ 1h}Øœõº#¼û¨~ܼ4 §g•~ïcï ŠÔi±Þ[ó±;<úv{ýÞÙWêï·ÞÙúªüuħÎð¬wtÞï ŧóá§ÓQWà´Ž{££~§÷±{Ü€Þ¡GÑýÒœ‰Ñ¼`:¬ »ïáõ‚ýŸz_`ªØ ¼9êô{Ðþ ×áÑ7F qÜÅÎ>b3P¤;èß­À Ì•‡]ÑïuðÒ&¬ÍqoØ=:ÃEÈ;‚õ†YõƒÊèS÷¨¿Àva :ïà6GÝÏçP>ŠãÎÇÎ{X‘ê# ;yt>ìª!ŽÎGg½³ó³®xzzLÛ3ê¿éý_Ñ?ÑìÏGÝðÛY‡:†&`á3ü~x>Â{±FtGÕpxN7èÕ*þ€Å„1v ê1í ^q5ø  Õ=ÒâЖâ]x?Äm •êàŒ`ÅŽÎÌbÐ,àY%Ÿ£tß÷{°KG]üzŠ­üÑuk°Ã½èQ·3Ðçù™ÞÊQ·B¿pÐþã]‹ã/=¶, 3êIè¢%;ú —ŽJGf=:r8:Â@ÿ=I€`é^AFS(A.€Ä+Ïå…)Ÿ"C Í‡0A8YŠ©â @CÉzI¬( Œn-.Dõø´[“}Ü¥‹qf7hJX)#še7ª¹4²Âc;R㤠ñÇÖþnso«;x¿µ{ ‘¡€á0f7q ôØv´¤È¸÷¨I¤|±(RõJ>@*&Æ ´Ÿ>DÆyxCÁL0gÅ’åC ¢{·²¬Â±s³8šˆ4à ÄIY30Å3ê 5 Ù-P‰X¾áÜQQ%Ä8ºp NäM± ´JMJ¾XÍðO2ð@ƒÿ¶˜+z|‹f‰å* *CkcަBi&‡…” ˜m¹C2€ìÀ]ºšMd€2]`:“ø;¬Þìa xXKæ-˜ÜtfYäYfÚb¤¥4<ü)ûÞbBù<$ñ ANÔÔM¡œbxAñ"œ2 øwàˆnpÍ“†I®«‹ QÇ»8‹(µ–ÌžÈÙbLÓˆ–Éx‰2´Ü‘š¤¤gAeq^À©\BŒ  ¿§”H^•\ @r¿Ê¹Bd!¿ÇÑ3émœ(&'\q\d d¢iE®Ènî<²eÈS€EžE¯Ïëóú¼>¯Ïëóú¼>¯Ïëóú¼>¯Ïëóú¼>¯Ïëóú¼>¯OÙóÿÆêAs°odepkg-0.8.5/src/hairer.diff0000644000000000000000000000000012526637474014024 0ustar 00000000000000odepkg-0.8.5/src/hairer.tgz0000644000000000000000000020152112526637474013733 0ustar 00000000000000‹¿1’GìýÙ–9² ŠÖëÑWøÕC‹ƒ“á#U2w3HgÐ#Hg$)B½zÝ¥TFeª[)UKÊ]»ÎÃù‰s?øÚÀ8œdL’²J̃Çh0Øì·×o?Þ|<ùËc~øô“„ÿöRã¯øü%ÄIôÒÞ_‚þ†ñÒGí•øüñéóëž÷—ío~ý©>ß¡÷r òïŸäóÏÿ/oþ¿¿Ü¼ùôá]÷oߣW7ÿa/‰‚æ?íEaõ{}˜ÿ4ìñ¾ÿÍçⵎþ<™xøy‘­7ùªðV3o“]n³åi¶öÂï…ÃaJ9¼'·«ö —ÚìN׫Ý6/2ošMVËu£ðgç㉿˜â÷l9ÞÀOü^.è{‡ß\ø¿yÚgúËÈ/àÏl< ý,„bð•_¿líçç«S2^̳ ¤ÁwSt _^.òI¾õÖÙxÑxqgî¯:¯äûi¾Ì ;v«ÁÝ*šÔ³÷ mBƒ j*ýƒn4è µÝ(dí‹ÕY½ò¸g"À°\'‹¼/N–‹  ÿN±áåé©¿œæã3üžÍà4ÏVÛ•×ýÈýÄOýžß÷ÓTü†~ùa쇉¦MßC˜@щ׹û‡›±ÓÌâ.ãê<ït”O3HÚ^ûô|uš oìÍv‹…·o×ù•ðÊ;Üž(`AŠ—›IðçþysÔ¡yÀŸå묘B©'zÞsÌ+þ¶%ž8²Øˆw`Í^‰1²öu¶Ý­‹Sx'0ù°â ÔÔ[­§ÙÚÚ2,}¾ Gçm=råv.$1ïËIÀr9Z†'ËÈì¡z®ïÎf·Ýi`$BÞ‹Qà/—3³È߀¯¶ì~û¢µŒšÍ­gî¡hc;POÍÐëe~Tñ¢£Qåt\L³ék aOT¤6™ã— ¢ü}p‰êkLµG\kwàýWÛ‘ >´æŽrÒ»ÅÚ«v> í÷mW᩹ ›¼ØM^¾©bÏíYÝý8U°_Ó?ïpÔô—yk %¦¦³jñ—ªE¢LÞ§ÑnI¿ï¼ç |m.Z;O€Ç=S$&éì„äóð…Y”ªèÔ¨&úß&±¹Rû©„ô©ê[®…/Íäî=ß»ûòÆK¢÷0K"­ƒ=$kPƒmáØ.НI¸ïÇ#Û5¬ÇÓÇ#©½[CÈ$¨¢ÌveV—[´UÀ¯bµõ.׫ óNA"êßM—g›MVœfë³wUšaf^ƒ MFÐl±„O\|˜I6 ±õÆ+‘GÝÙx±Éº‚èÒë9â‡öŒêlX’(úíšéÚ…yþŬsm¡>æRMõ¥jÔ®:GíÈŽ¢1Ð6ßQ†ãí€Ç'÷7 Ž—óÂ?ͶãÂÏ¢5üË¥‘0RF‡¶ Vz-k–Bèƒö!3¡D€ï†@ øÕ.5zŸó{S«­,B¨†W¡øÑ&ôxbÔCaö­O“ ¬õ¯lC¬Ì‚Á9¿Ø< éѲ~ŒO‹Õˆ*iµ¢6•‚òåâ’ß`6‘y¸v=ñVÌuÕ†©ùm-+›Öe™VùÖ˜oÓ‚´±™tÞ‘+ÑKÈÕ¡ìv.—FN«ü%×îy•?0K~ÁÓ°¢>úŠùr¦ÔHôˆTŸ*L÷¨¯¦"ËáåRšµײQ’°Ó* +Õ×G£eß’'i¿Ö*ç©ýˆ¥?´Ý¯jî}t‚ç´ÿ«¼#V­ö»†ªŒ¹žñVTñÔE>œiûÈmÜߎQÐŽ·‰ŸžŽöÙU=Wä5ܲìUªë¼õ·G0Å/(C~{¶ô‡’&omfŸÁÔßuú±ì~ÑHc3ã¿®¡þK@Ú Óü¶£Î9Ï1©Ä”ÂTf5¶dµös~ª&ýtÿ¢?½ëªÿ:r×·¹mðÐØív4Bp<.¸)À©"§w¥¾‰ý+‰ÖÇi‹ vˆñC>]òÝ ­¯Á=¿µ _’r³R˜·ß7û¾ïñ`ûGmU e¬f¯BÌ'¿Öèáö0Ü2–ŽcGmo<œeu^Y×á#®ìØÿ˜<ÆþÇfñb=¶£— úÞÝf/¤ÆEú<‹m¥Kr~¤[zHC¿ð{–ÞÒ¾cû!›ˆäÍ}wføÝÚ»u¸Y-ô=þW4ÉßüžÄ]Æœ ¼ÊÒ=ì¹Ù5iÙªÐRå¾e®:¿XŽØ¶DûEÚ/´{ñu¹Ú‘fÉû dÝIÕÉÏ»5žÚî}¦î7‡ZtíPuzv`ÿ5öf¾¢þó5DÀÇÚ:v’özþÌà—e%_d/ê_Š‘TíûÙÅŸ”K|ßi{ô¶¯¾,¾Öî]dïE¼;dÂBŽËxÕJb»’˜+‰­Jâ{ofa0ÑvÏÌ×Úu(k«2@Ϙ™ËÁQ;ˆ×û ƒxœ­ÄãÁÐ> í£ÀàÝs'òëáÚŸo;3´íÌ郓0ö4Ck[³~gJë››áØàÜÀbÿ½Sêð…Ø­·Že;¥¡{¿ôûèQlwŸŒ÷øj@½+Ú¿ï.ê—Ÿ’Úeµíq[à°;þ7½wðx§q¼on7wïi#çÛ÷µ]æ•+ä¿ï ?ÊÞðWf7Gøßfþ:Óô0ÛÐßYÐÞ¾ž·9§dcPê8þ%2f\"ã½w·?÷ï²!^9ñuv¦èÐÚÃʾÆlyÔž¢ñ¾ Eèßxóý+‰ w=3tÌâ8O­µ¡½‹à]íÖ=-+(îC6“WÝqC__U•­šÚÇÙ{øÉûîpÇ£šßÉðWTþ•|îŽYÇ{%üë¢Í·Æ½¿%‡AÞý0ÿl÷»Å·â_ñõ–áŸéô´]Ç¿˜‡†Â-Á’ç}aO}gÅÿî šÔqD¿%§‘?÷ùó9žLÃñ$ÛlÄñdîO§!ü‹à_ìÏ&…_à×uà_“¿ˆå—¾)•ÒÚuž¼0Éó„ýPÊË=7° ýl½vx¬äëÍÖ_gçÙdËž/ëËñŠÕ]«ÊQeºÚ.2àÙÙ$'Ç”qYQ^(zß°ÃèXr¤ÿÉu@ß®Cû|‰ÃjPsôWä[¬ÎrhʃT níÇ"æ3ËÂLéɼLˆ !ÒbHˆUÂâ¶yÇ•#¥œRD`™F£ $ÐŒp -&®ØûSPìwË¢ ^(AåÚ0êåA}MúH©¨ŽL0û5þ GKp{öãw èÍñÐ#ò~5.¢ä<Üm£<$T-Ô/+"ÏÃ_:J-•»9ž>A ó†øÑæÒû¯Õ±¨ºÛ¾‹öÙ¿ÜqZÂÔZq-•»òk.–j¿/½Xh'¶~é³· jvI­•õÈ «²MÕy°=OÒ’E6§éÉÀŸ*úÜêŽE¨þµoË"¯([;Vø€¬òþsq¬åñ‘HŸ@Òö£ao<j¯6Ý û/!¬™°:0D$ÀÊZkì{žcãë88nïëûr¼#[~ˆYù¾0ÿœ 3­Ù3ì<жáX”•«q (S p/˜¿3À³\kí˜ç¯ÈÝ*»OÇ,"ÇTç–{PžcéÁ¶‘_Ÿ¬ÛªØå¦ìª%;5Üaû1k —vßЄ,:Yíµ°"Cm*ÙÜe©Ý5ˆ¿åu¹°Ùªvå°"enؼܹ«QÞ±½mߦ·ÇOJ’<ä¤@m®I};Þ˜O‹§ß¯V´6ÂGO«¡‚1ükKÅô„ Á •–îèæ¼B~6?­· x8)š~Øv ©lö5xÕ]l»ð*hšÛø–¬¿ÝÕºËà&P‰Lg~Už©ïZ)ÐÆ¦IeˈÐn6AÙöJ™é+ÖvüàÀ¿Ú•½²Ú^Ìøæè@'ØÔ‡~ùÆw\Zœ“ÐO"õ­,Î’þitÎS´-ËîX»¦r¹ícçªkƒÁ¾†l6Ô¶ê°%yNs’·×¢äÝÆ¨ä¹9¯úÔ<3oŠõÓ{Àžäé³qØ*ë)ÉÄv"8Þ„äèBe¢ f­p*ŽÊ–Ìɽ•NvlƒܪmþkâÖ-m–Þ±îË¥cfIuøw++¦÷8ˆXEŒRl«R¸¸l¯êâöµñ\n^÷€çâðih"T-—W°przR‚Û§B«”·G¸Ñk;RÃOþnyP4}s8x5¿ÍH‰jÎŽ{·šÔ^òX“ÚKöNªåŽä°Ø`P[“å'‡\MŠ_޲Êãä•bgîhò7¢é^òªƒ…ªfüwCÉtÚÀ_ܯäk;‰”+ÓiJºÀ<[""C@ì¢ÙçíÆÒÔbâ 3· ²)ÿOSsøõ·ìiò§×w•I•? þéPÿ¼^þ4€ûî;ã†é¿¡ïÌÇYc¿æ~ÿlÿîc¹¾ªs÷§qÀ©žýûVi’B{ó[ÀÈ/èp0»Ã^é _ ²~]_ ïäeO_ïG^¾-‡¢ï„æ;¡ùF|›¾…y<8‹GxG};òÉãùG}'_Šlå¨õµ…5W­?E@tå»Ã×w‡¯ÛLÊw‡¯/ìð5{@‡¯aS…}Ýx$—¯z¯£Õ³ï__Èãë¨ OIIìióØ>_ß¾¾;}¹»ûÝéK{õÝéë»Ó—!É)Ÿ¯?™Ó׸iè·¯ãjõð1®2\m°Äüéµ?¾ðgWþu‘½ôçSvÛlÇg™Z~朅¾Y5ÁªòKvÕš¢ãt‰.ø¹"O,èXéŒ%½«¸‡RÉ»sÜ)\ªó)®Õ€ÔSp(-ž}ƒå7½6V¦©¨,6¶Žk/­’ö|Ú‚‘Öׯ8_)KIòßn¨,³À©*ˆë CßmšþCÀh`*€ä€Ú¹"¯ßfȪG š:Êeä!"tŒÐ7VãA\¿p¬(²Š¿v¨¤ã…Ñý._„-JÝï“ÐàYÀRû=@¾B_vž– QåG[Âçñ¡t´×[’äÔå^¬V”šX¶§úº-‘ëÛ_¿-¦Cݶx(G Âö};úÒ('ý›ààlå á°Ìñ”ëåœÕ öîàmpÔD›û‹Ž.z5„Á¦ Æú¯Ç†?aüú…ùý—mY~A†þ 17î;‡==þÌðáV|[ôÏ©eÔMá—¦mÎ õ ª%G-ª^«……¿÷ÏJ“r%6®”0>–2°ÿ¦n(_Q†G1n²ÝãùÐç4[}~ýÑóþ²ýíÃï¯?Õç;ôž¨¿’/TmUN³ [^ú”ïA›¹„Ùº ëøE¶& 58ÏŽû$Õ¼b›ek¯ð©T³5)‰þÅ%È>>ÈÊZ[¡£Æ¾¼§ÔæÆ ª¨@Åêµ¹É=È–IÂ<éÙ_ãâl·¯óWc2rœ^{gãÝfƒ‚^¶ÈAR¢ô.Ê‹ËݶÛ„­ðF,ò!yãúÔ+è8¼BÍ (–ƒ‚¬ãõz|í)s!«ìÏÊ;ÍŒ^eSÊ“¤·=&‹¦»ÈºçP~wy ) z çlWkßÛyÕ2g[*³Ü-¶9èæ0¹ð´X½t×{o!+™¸€:.Лƒ |…£ºèlçÞeþbzþ꥖½hB¶ºÑµÅnyÊðBlYOæÐV¶i½•ê'ôJÈ/TÆlzr×|,pf«°‚š(-ðZäôÆ[v ‚c†Ý&óH£Â2«Óí8/ðq·s³€•¥6×›m¶¤Ó :ºlŒq4šÖ$éÿD~Ôlu»]ø Hßd™q®ÑÊçn—}Æf_æ ÈLs´Ÿ!¦½ÊÖ«.sÊu6ËÖY1ÉÔOºÞi×[®°Œ¼ñâlµÎ·ó¥—D±/û™ý´c¬…ºaúªä¸;é.»hÏm„Ã~3vÙõ¢~Ò}¸Eäé“%ŸÄtKñ EžÂC™Ç QæA¨÷ef µì K!®ÀÄB†ú äÒ.¼¶W¦áúºPOPDÓÓ.M =va:>Ý4ý/šM±ß)KLiR]¹Ò_xKX+F¥lá‰Ë?Ñ[Zò`/ä`£@¯ƒ`Óab&“¹ªíB¯!cT×-hqË-²«7:Œnâ¡—“­ªØU¨&sn²Ã¿ZF¡4ðÎð•€0¢*s‚”õæ‚ßl­9rŒCmë‰$F·5'W¦lÔýh{åЭ‚žŒX©%õl÷11Q?'Äë|À¥<ÀÒ¼4.¬¥ae­[ޱô¡å(o‡é—í%w?E®zwÞnòõ‹SÿbY®â s?ÝÏÞOÃäk);@ u“qêUùz=[¿WG¦nòquš€Ø<ÏÖ«%Ö&JœB‰u~6ßzÀ§À8€G¼È‹ ù%ä`Ëé5UÁRÁ}äu€µˆ6ó10¢lK(Ù-2ÄÞªPí+ˆq¾wå=<Ëp0…t/SˆnÈVð´±,ðÒ.ô´ “$U؉:&q J7ÏM2ã&ÑL´.N«Wü8-±=x¡Ht ÿœX‡Ö1ÆQ’ö×c|¨Û³ÑÆÓFȹCl¥;-¤;‡‰ÖøŸLŠ77©Öâôqô*.ÆZí„fcÔêx B£íÑjý2ŸâóéQêŒ7~<…>‹S¥v”]£‘4pnUÀ¤Š=ç ÈùÛlŠÐ=˺aó»RôðJÑüÏ ‘Cù1lÄ,—«i>Ë'r|Wžn­<´^ó"@ëúiÛd(:«·Ð«þ-µªñŸT­ÿ{êUóZå½Ê!¢<‚ruHBù“«XJÔx,%k.ŠÔ ®…Í¿«a·TÃnؾkhô¹“†v$ùãß&å4öŒPÔÊ Údµ¼\€L\G¸Þjo©$ë’þóoI_Ã2bB ñuÔD‚ŠêœdOoèw 6@>A|çJÍhð6ï°#µ¾…öõWfˆ—ãõV•ÏoU>_ŽÏ˜ëk³’õC¨s·üìÊ—ë… ÿ"šéñŠé÷ý·ovÿmÍŠb›ŸrMmä욃¹,°4 ÜY«D‰ÚÊD쬨ô¾ÊæZi›kKÝÌÕ›Üz³VšèZÏ.óýú)d»°Ùæû”'´Ö£íºµ]··y «V€ä“©v(g›:ÛÜHªìªÏåz5]ļBí1©zœ+©Wë¶* ç‰£Uª¿Èµ¹&0Uà²G™V`(Í% ˜ºôZ©Ù늞«wyåÝZ)áÖdrîZûg1Ø }®g².*tl“j{¾*/õi2^ ¾Ê ¦{¢ßcàh¹rèà¼fÊ1Xó^9úõÁѧ{GïÆQ5|j>ÄðS•ëÎÃDZõœÙ£<°bÀ}Ç€“A%éÎ&#É!ÐpTrzúŠv¤ú ú#ìH.Uêþžæ¶5éÁµ©}»ùÕ©Ó5Ù–NóoÜÄt´I׌D!Ô‹X-ºÝž~&ð”Ê}Íý²ÿz†%\ÀKK²lÔ­MkanZö¦\¤æFêºaËe”ÁdàõïCJEЗ\ô;wÑ`èX.FkÔ7¬RÕDz~¡h)dÙ–øÛblË–y2_•—@ëÌ™Ä;ÎÙ嬜”:¢š ©•ÊRº);tL< „é˜XP1Ô}KóëÝfB—”ÌÆ›µü'ŒŸsÝ”¨M˜–ºR““&¬ÌÙ嬜”êž°°:aw2gÃ…=Ï͇ÑNïbÄ5Ž'ßíšßíšßíš_Þ®yŒëæ7ÔTŽCß £ß £˨€C¬)V,£m6-Ï™ÞÓõæ»Iõ_Á¤ªÏíw›ê7oS/ÿ팪®áÿËXU÷Ïíw³êWrÏ«ÕèŽ0«Öªsßm«ÿ¶ÕCÿζW§IÿàˬS¶þ&ŒxÇi£ÔJ#ünµýnµý³ÚÃäÉjËO¦„pjúà/ðo§Yle¿ˆgÿIŽóñ%s‡Žò-ÄMÙ¨¨%ï\fUþ(Þ ¢><äh_[¸üêíóÄ/ü•¸ ÌsCu‹*ó‚:æm€ëÏ€Ozôvüp 9½ªaPôMviQYèT¥…|Øò¸˜VëÀ|Ò¸Ù[ãzõrãèÄrÑ¡`ÙÙÜ‹Zð¸ÜA µ)‡ ЧϭN16Ué—¶'ïˆÒ¶•Zôð¨b^vÏajºU÷å<ŸÌ=\†’ÏT37æÛ®š:¡ø/YQ¥5b¤¢Áß´¥z·¶¥âç{*‰&{Lªô9dW=½ßQÈå”ÏBÂ_u~ã°i/§#NGØfOïx>ò_ï`¤´›.§hŒ\k…TÊ5Ë)Š?ð²L:ßéõ )rq'£¯x¹œ‡Y‹ÔûR"K=ÞiGÊLF©,Œ,OèúúòH¸úúû[”—Óòqº`“2 Õ/:(€h¯Ké åCf§]ùþG:¡Õ Öòþ³Ëéw¢2NwÒ³÷˜Ç;Øöœï,íû„¤ƒ,ÇWAã|熚¾6¢¥5‰ØG(JøvqYjr©Þ!Í0{¾Ó¦‘êÒ©lÀN-™zÈYYʦcýШvuY=¢ºT'QãêQ£•C‡SÏqœ£:cYìê0ª¥2¸áx~aŸe=¿P§Yùçíϳ¦¶BvìW¦þ_Ï„vzÏÄéZœr•½²õŸë *}öžE=$—gQés¤ ëT”ääG!>ÞAzÄS¦¿}ãݼûý·›O÷¿ûïýwþá¿ýõ7ÿµÿöýç¦Ê 7¿Þ|ôÞúÿ˧l¯}ÈŽÿ÷ßC*ôûï¡ÿûßåÊüxóúHL¯©Ò¦™ø_þ?Í„_^ÿüÉnçýçÔÞT9?ÿöö“Þgxzí}þøúý§w¯?¿ýðÞûð7Èsã½~÷ë‡wÞß?~xsóË娤ôþß»Þï¯?ÿü!ò½8vâÞ {ƒ¦÷ó?áÕG¨Ý{ýþïoßýï·ï?}x/¢¿AêÏ>üoïo>z¯ÿøü¡ë½ùðûß»¾÷ŸÞuß¾í¼ƒ~½þˆ=¸ùùãk¨=†ÚS¬½O¡D=¿¾ýÏh‚ïýzóþæ#ü…>}|û_¾=LQä#ŒåÍ Ž^qVïÓÛϼþ|ó €ÊûøáŸ¨Óo>¼ûã÷÷ŸD1˜¨ªúõ7€é}þàýñ÷¿t"ŸnÞÿ|óñWÌï0rQâÓç×?¿}÷öÿ†joÞÝü~óþóëÿ„¦~ûîõÇ·ŸÿÉÇ2ôOå¨`Þ¾ÿûŸŸ«ïýïÞï|úìý|ã}ºùŒ­ãA_½_ÞBÕŸä¬ýãCG%¼~WŠ«¯?~|ýOïï¯?¾þýæóÍGá'ï—›7Ð5Ööæõ;û¯8á¿B¾²pÙŒé3 å¯Z×°ü‡¿<î0dµl@+Z•؉݀ýô b –ýùõ»×ïß@WÊ.hØ*^w’ÿM& V}òÞ@ RüñéæM^G¨AF¡=½×zö¦&”Z'Ћþs훓bÔ0A”ö¼ûüöïïÞÂ`Ëþüã··o~óþqÀžÊ9 ì¤5ˆ0úôùC9?eÙ7¿C£8UÐÊë÷¿¾ƒ:Þ#ì½ÐF 0/»üö½Â=À'î Zòñ ,Û_}Õ.vøðþÝ?%ºª,š×b´%XÿÏ7ŸûE³¿sÁO¿}øãÝ/ˆí¿¼ýxó—& üÏ]ïS×ûõõÇŸFŠ ^ÿ!û ¢ÍÀ¨·ÿùÑÕ‡ýðpæ= hû×?øøÀúOÕ…Î%e÷X.´”±¤C¢Îgµûîsß7½_?àð¢ P5ýòÁ AïÿÊ"S€š%ÛÞ‰¿ëæÏÿ¢Šàe“~‹9T]TYxÿ S ý5Qû‚<£ñºñ¿ï EâÝM—øH㿚²oP^+„ Êüz]ü¿àóÑÞ¾ÿã¦ì2¢ßQ*yûDký­×½ù?ÀITSqÀ3ó\}tô« )Rœ×Zf}Ô¡5ðSï½ÖÓÒ€ëÝçzó»ýæw~ƒÌ–C WG‡íEÜ^hCùŸ`oÍZ1A‚Ò~ó»j/2Û³@sƒ4UˆÄ=„ò1” M¤Ë]ŒßÿÎX…HjŒ¨06ý½fLo+HýÓÕX/° ÿÓ;ñþË‚?VFãžh/°‰Â_œÇPÚ~µ9£¨µ%'Ю#µf'LU-ÞâG[ÔB†µ„½Êœãò4`ùIüüÇÇ÷•9z÷X÷›×A\e)ªŠ°0‘O¾d@tÿ{ñöÍÍû77ÝÏÿõùáÛØÿ;ˆâ~Lñ¿Ã8 Óã§a?øÿûK|&þþϰ>{7MÄÞÄ÷²ïSç„Ož¬o~yû DˆŸÿ`‰( podÿŸ>üññÍ ¥üüö=ʵ(K|òAÒÿü‚ôä%ïÉï~yû··oˆÿú$ü¥½ÏÈÒAÐüÏ·¿ oÿíõg)þöá°J”l`iýòVHPè ˆ­ÏŸ<éxfŸˆø‹Î¼ùðË É° _ Øýâ+9Ô'  åàT…5è½ÿÅê ´r2ˆÀ»®@Cd`h¨f<|¤ðõä—oþ ¥BÎÍ €ý¼úˆ²Ð †ï>•¦yÁrzça<ä²YͶ/Ñ ~ƒ:n-SôkaϰËka]-¦èƒÄîb»ÎOwÛ$ü¿ã÷$ßüÿØ«¸ö²«Ëu¶Ù û™ý¡¶—h¬Å<Ûø^^L»i^œùÔ@–XôÚÛB¶'Û•OÍVË‘uœ’¶ãÓ|‘o¯©ÁY¾-°1¼anì=AÃk>!g¢ËÝúrµÉÈÍlšo&‹q¾Ì¦]t½*V^ö"+¶Þf>^,¨Åuv Ôé'ÆðN3èÞíøÔŒoš¯³ÉÖ'O*ùk@ƒŽ-|os™Mrüñ$»Ê`ãõ5yBA¥›ì§äÊ1Ôx9>ƒQ5ö@ñ&d²[gKì-*6Û|»ÛfÞÙj5¥þn²õ téú«·XmP»MæCÛ1µŒu˜à=<œî69Œü»Ö»K´d7av_D —cr,CÈâ^LAX°Z_c­Oyß{9ÏàÅIÐ#6x±ßÖÓ²AƒÄ­6LïI‘-ò3t’"¯2¬æe¾Éš0Q9zZaØðËñ5Úß¥Ó Zëé'`鉲>Í#ÚªÇÓ9ö\䆙ßäMl“¹€y÷‹rÙo÷#øÿÇ׿¼þ£û·Çic/ÿÂ(‰BÉÿ£ Š€ÿ'qçÿ_âÃR§fø\§ã]£ðg“¿ò¯ý«¬˜úsŸÅSÍyL~ÖÛÕÂãWŽ_µùÎÇÏÏáÛ_.è{‡ßµÙ—ÀNü¾!;}ïð»6ûfµ€þû9~Õfz¹Z_ø úÎù7ÿY»ðsúšæS¼è>VjªØ—Ê'@âõ å1ÐÆ|6ó@ñ¦ðD‰ŒgÙézœOš¢<ï9c™Y¾Þl½€·†×Söµ6 KÇÐk_AÙº~6š5`2›¥;j&›˜Œ dn Þì^\7ËMú€|Ó[ÇKÞÍeaÀáeÆQnÔ=YMIŽ81.´ÿ]q–u.vÛíùvŽÌ«A8çåùXVNþ¸/€Çå}ñFê{C”Ù¦Ïï¡û—Þ&•1ïYI_u)šäÅnµÛÈÍ[eéï Õõæ¹£™u³ÿøYXMQdkŠ»"§Íý-ºKxÀ±€×[Í.ñP,y±Ï3øÊ©Wf`2ï„QŠR^Âõúþ*[/\®Þ;_<÷Xï²þßɺþÇû·¿ÞtßüV3Çžwvóñ7Ð-»/_¿_SHüaišm&ëüR¢& ?‹ñ–ŽILHAªÈèÝéjuñ\k¼xäýÙ·Wõ $Ö¼wźÀªôìµ.P"TXn4+#ÈXkh)#ÑeܯíÚm©b>Ás3Ùxaâ;Šv^àTœyápBß3À›©—Ms $öT{â/È´|¸F&}>peC} ½µ¡Ð@äm¼äúO¿¿~÷Dûhĉû¹wþúý¨,„u®¨Ü•!¿t/Ñ…½<Çaõ¢ðÊá¢-rU=Pu•¹€&¼FvͰšÖCgêq`Ý‹ñbG¢S–çnÄÔ*„¦ÌRf%Åmº‹U›B1tø™‘»«Ä w»]Üȶ“®#µéa£^±ú4[¬^–€¿Ò²æEN”õªC£Ty®ù„‘‡r°âq]VF—ÔˆžÑQ Q•×ÀW¼þúÉ.¨PÍ ’A"ãï²OsGŸJªwínþê)ö…T¹ÈˆbÊ:@P,5æ;N›xózo«5s g@qw›èH×x ¾coñPd ò¨¸N2úd¾¥DúùÀйF^ÊÀ['UÊ]ì§]>¹€†]nQqk€˜?¡¯œ_ò@Õ w¼ÓÓv•p‚¿‘º!¨IA=%¾œy üv²È&x ë¼vôCðÉÓr`ìã5i]Ùb#Ù ˆ¯EVœAJQv$ç>Ð9è!8KØ?îüp/!,9 žsF~>9Å]©»U[è"Ë.Aë¦T‹k†Üb… C@D±?×xy­j½…~‹˜Ölcê»ÖvYê¸.;:ªu©¦ lòX}mäf 8*?BHˆYÒ.>qÅ$±ÊåE§Éè€J=p£„j›’>ò\ghØ¢• Ç´dd£ðsUæÃØx8næ¡ø< ÿ =»¼\¸*¶¾[.¯õ®Ë³}ctsÃòAÓyÄF®Þç…«U±ÜmÐ ïKsyØCM‚ØOg×þ¿îJó¡lƒ*¨#ý˜o)yÝ®khTX;àØËSL39Púnâ3Û­‹|3× a0ÈA9íjŽýéè4W+9‚cuŠ'S °—hBÃŽ{<@ef»/š:¼‚uT3ê$%ŒÚ×é 5q¼I60Ãuu¢¯ç.ˆ#Åó+£ÚŽ/²ÚÓ`ÝMï‘è|Í, —ÑZ‰¬sÞ&³¢F¤5¢ iÆ%WJ¼‘£¯‘|x­=7€$È”­n1T@¬òåšù¤‘ÏÄqB àÚ{ U?%Î*lvH&j¨*/íçD$óêéµµtK"»A"&z³]ï&ÛÝ:;.Tã¨0»2fŒ—È2Í'}´(B3ÀÂÓUA0± ©­ÒQðŽv¼.Á#êÐÐ!k.YNéóÌ´Bõ·¶O¢íÆ#¯<@[¬Š ÕÎ?ïc]â´/žp•4 %œùtÏj«‡õ=q´jun|º„¾Ò‰JÑ"@¹îuã9YãI`žâ®á\AðÙè&"pd×aæ2†fVgÞ2@b7ž*Ú1«‘øÍb¨O‘wª#åO—OI›V‹ 6Ñ4Ÿ{ŒãM9†ãµ ì!8HI ;À1ÁWåÉo€ôf³[fS‚÷;p÷ ߺ8»< C¨³8Í%ä]š µb’$ öu;ômY—«CüGIP¤ñÒ§y¿µ 0^6¨`ïÿ¯£Ä”¡)ëÖ-$‡{˜ Y#P@›.êÉ5õGpVþáÎZòѲ.ú0L‘;E\†-¹"wOc€åÂ8#¦œ8¿‚í6Ôê8¡;ÐSKÚ9ËùõûÓŽao„ªÏ)ÕYœÎÐÊ1ÉИ*EžF+BCZ]_šA_˜ ø¹Ù™Ûª£OÀ£027옂a²˜“è쯜¼*ûÓ'ÿ«q>Bç8wûƹ3ÆÉ»¢¥½ü‰7êë­tŽ}ˆénÍÛ«Û TB&—Hƒ AoIxe5t<ƒžà~ëùÉ„Ò `*Yq\ÃÞÃD»ùí]ÔN¸F±ö¯V‹)) ¸à/Ö¿pÜs~´¡õv]«xÖh¬ØbZ¬)Ö%3¥ VÜ{©tòÊUóöôú)ÞŒ‚iź³»+?[çÓÎå f×{*¤¢u&”WÃdéVx=^¶¼éTÖå\OÄO%…A(À¢@Ô*Ë9‹HŸ’¿ŽqUú ˆ^jˆ‰H‚ÙkÈ>ŸÖûd+ÞBâsTéAm]õÊ®ÑsCþÔ?ba ÔÜSž¼§xWŒVœ½‹‹ï¦ÄûŒ²ÿ“dÃã|A›_âÒ£›ÓlWLjêŸüÑó„w°ûZhÉþá‡%ÙÚ%|€ÐÕÇ_ÂÓU.$ÝÜ„(2 +”ˆÂ`½Œliõ„½R €n¾Ú-¦À8• «)MrQôÒ`ë,–çªN|!¢ôÜ U¦²§\h—+'ûcïEâö³,Ïyf˜ˆÞ\Ž'™fN~J[×O]k _4BÜÅQÓïvåï É+ ¥Êr[i>¢íµ+ö€»Ž×ÓÒé†ßîé´«š%ú —(  qÛÓ *Îj˜S„q×S;4b»r‰Å6«KÐY¶x{RF½ª…Å ì AM‚Þ8 ;˜´ç“–*‚•:éO‹.`ù"o¶û˜MÑj dÓFq¡]l–ã«Ö"kÇ-úÙŽ›í(p®K¼òË]/•å<QÓÜâªä®µÎ±æn¦•½× áVÚjøå¥ÒWÖñÔ!äl”xjúV­³¦%Ò4*½edO‘êYà¹Yh¬uCÄ,”Q*Tžÿ´!²DZ7Êšª3»{·Ÿ€l„WäéP—ìUW»ÐZÚ8`̳m¾º•Úi²0ôk¦àB÷£‹a’šBÑéââf¼uöÓ.¹ë†@1t¢¨#i%…ÍVÑ*ÚreÁSÔ䵂f€Á‰ì>”ÝA…ÞdÃ3²tÕ-œåñ€ê.úxúÏœe¨¾áx ×™t¤~4®&fºÊØBO3„µ”L—ÙgC°ã ' ?”œÉsè5)[g®©}—ij¼a”íW¨«-4 ¬­ÕK2nf—U)Vp›rf@ÎÀæEåÀ¦±æ›­¦ã¦W6¹[êF·ìå7_•…’ ÖÌ×Ñ‘6iâþÉ^v{¹’.• tb‘÷·:mºýš^ÓBå1ÌÈ= áùl#Üf«h§uW/D³"ÞfGjìÄè+’uôAÉRg„³ùÌnïI²Ë#yÀ5E§th•LV4>âÑÐÑSŠ”ä ¹hÍ!²ÀJ)Æ“ÉåÖk{Å:;ŸlÿJª!+ÒL,»Uë”Ä€|£`2Òx¤°àœ¼DØÄ:Õ¥#PÒE !¯ Õ¸¦ÊÅI·%ã]Ë?za×lIXi:º ‚Í@ž_®s{Á§S`È[£,Wú‘+è dðj2¹‹GÒš— 0*´×ë‡ðsSFzeš3‰ºgšØÅÜ׊èí㻬6s-¤M‡ ª€V¸!ž½ ÉR›=@ïŸmxlèø؈+Äî­õÓ/z¥íEsR¯¾£`ºVE=‰8©__Q\_Q¿ZÑ i®J§È PÃ*|J-÷ÑP@—@Ð ;Í'äç(¼âè;Ï6›U•áXE ´-@sãMƒ{õÙË9’ܪÇÐIv¤¨1XåK²¬âÕ>;x½ÃâC:h«êöJ@CΑ)Wq¿®òHÖY!b«-Äu\\{3ƱÍž­(×L £vC°*¼?Žð’Oÿi{‰Æº¦:&>Uþ¶—ÈÁi ^> I©X†¥²Âד‡hãåIJÖ2Bc3s/ÎèxéŸá_–+[ˆâÀIs5 iPødI (õ`V˜ñé^ƒY÷¬ë3ÄÙažhH:xùlôÂ÷^<5.ýM_hµ—TÝ ¢¯ª*Íi¶\FÅ Œ wA œ+tî…Ñ–ÑÞ”ÝÀ¦¾Y-³ríYþó´Tʮһ¤4þvP({ή–D)dePâ7n‚Dù"gá|[³yª$;Þ±¢½=÷Ö½é ˆÆ}>‚Òеátákc…‡¼øh‘¦aó4/çøPYn_?!Ë6<ºKpy-¼‰ÙeŒªøâµ/Zˆë:̹ê{mÚ¶d÷¡#øóbøK'tØòõ\ÚÐ`ÄÏå®™ÛŠ•«Ú•ÒqL¯¯±\¶a0°&¥Å˜é!÷©Æ\w ,|@Ÿé®WÏ‹o:²I]ÀºÄÓ] ¤3‡> š;†¦¨×qÁç*Ôg¿ûÀUou¼y^Úvw¥rZívïê*Å{ãª} ònJ§çÅ$€*páð¸×( .K&Z¬msÅ3shÂ]ÒM_¤¯“ /ìÑ­y;Ea O&ÀMXÀX~Ü„í‹f%º£ÓFŸµ2²±£Y‡¯’ꀱ¶ 3„ÑþÐÚ^XÐd¾§JwÞZ#0–u½œeöá¢æHBk@‡BÝV§¯Þ¥¼ LÒ–+C¾Ì\+频fV:.î&å…ÏfyÃŽ Tâ‚,Èô£(Þa…²ñÁûE¦«¡û©ß/…áb3ŠíÆ£&oeù#†jÜá>qlƒ}»Á¸Y¦´Qˆ½H’†€ûœÓ´,Œ²VeÈòvk vÈO?QÒÚùÖWýÁãJa¯k”%IdHw·(ƒÒ%ÆñF ãRÈ_U]ZdÕAw8 ÌšÑTƒ&aÚÔ—77Ü®ÔIIÿ'_õФ“uFo>¹leÚa9¾9›B3M…ms²'ê=«3ž]RÆv%lÑTÓ·Øu¦4Ýt uòöráù lɰƒÕˆsMâŠ\¯I"®X©…fÒ9>©^‡ÝK³+YZE}uUªõJ"¨HòÛÓÌ}:QL×f_7Í•#Ë:C{2RD­£tÿÒîÒ¸Ç5ç¡ ²fA++qû´r¡Hêêài(wKa¢íS·nJõtBy-ŠÖZ ŒµØîi½©µTÙdº õÓ=ebÃ"S"ÑÓ„}%OS,Ün»À‚“Aû$©wm qB)`J;½.·àÖÞF[0kjy­\Å’€kQu!dÑ´œâlWÙ—å5²œx;—ºÆÍè…ó(0}ô>/®›z‹hçý½EuB‘(”žùŸØŠÑ÷uÔ7ò£A›wlôü©•c™îÈV&v'€‡Uišü4¨³\ßRØ’º c½?È‚AÝ´ \•}ôà¤ÒjQºˆÈø ¿†4«õËñzÚ9O.ðGy9Öç‹Ý׺QIY‰|ÝtˆAÙgÅ(2mçÀj¼Æ¼Êvë_Yz` ]:š•žÖöÉ/Û:™¥v¾¬ÉxÀ0(¯ÕjÝöŸÞMö‹ëéŽÕqMj¯ºr >´  ZM-þj®ª¦?Q¥8ºê`‰œÿŠ÷‹Õ±3ìÐd럣¯³ø´\øÈÅdÜqš„°%k¶—¸ íŽÔEÀ”VOˆcÚ#‘í™HŠö s©ÆË2ä Ôl¸S’ˆX¼Rš'¯Oaê(Í P™GÙFhƒßG&Q,Á íÙï ­Fè—^å{³ö³¬5µ2ñqe½Lz\™ž,#ã¥Qß´ìQí ÇWea{Ððãîô}U‹»3 ·½¦V&=®L¢—‰+#íK£Ð5èb3¢·UÄu0ØQ‘¼¨¼V5˜ÝÂwØ£ »Zw‹ ÎÈ@üÄ=ÊògRþì9"¢û@Öèù­æ3ø½¢£]$¬ÉfGÏ|G<µ>¶ë]Ö-_úÚAaNšs«:äêL¥mÖaM¹¹AXúµG6áAðÖP‰J D“œŸTáQñª@¥¯òmÝÊ‚7²¨Ž|[âü>ÛvÓC ™ìÖ9:ºéÀÑ0æ!†ÙåÝ`:%í…TÃp­jšÃáÊwLfÑ{]íeéÔ:³.gÔ<}ÔÑ&Ù»"/¦áHº™h‰ÑHºŒh‰ñHºhh@Uàê ûm›j¤ï]» sRëç³tÅHýžßÇy¥ é;¢oɳ\së€:{·k²DkCk²°´Õ‚1SüÞœÊe8 ”arᥜš¡øRÎK¨‰\´ÏR})ç=ôô¨L· ,¶¼ð ú¶á VáækèCaº–´ã{›)âmO˜Ñ@£’䈭J׸ìÂn0 ÚœóGzð'Lè½)åqû^Ùé·¯5e8bcPö¯¡¹±R÷ÌÞa½£´'è-Ýí*jqÏYCÈLõm¬¹–Î_]Kk­¥a-v`—* r`I…-·ZÍ<&¨Öò@XÕ†·XÓ.A“gEø¸Ê)ý²-ÔAOÙÜy‘owDäêe°äS‰=v3á¾f¦ÐÌ ¾)˜…‡‰ê×Ðû=¤^Ë:4RÏ –}#¡×–e£\l÷³[>ò±É ÂОÀÝ*'¼’&c_úÑâ&Íjëý‡ YpGèèHçxD2YtÙçLAÞ‘¥€Z½Ÿr½zÙQVcµ¯uøÐ¦Ç†ôNG»5NÞ+ÇÕ,ô½,Òf‹z`OÑbZ9í¯¿ÌBñŽò9gQ¸{j!¨ÙÅÔLâv¬$¨½LÑV°árW€!ë”ÍÙ_ukáŽm‹¯‹iåÈ¿•£D*Ãùêt”Xé–ì­gí¬i·Z–»i€©¬0ÕËZRž~‘^¬ø”pR:]‘7„‘ïuÂh7#)jÀ;™‚ê‚‹¡Ü®\Ñ㮘 Ô\` ©v> žÑÞ¹ ,Ö®RfkEH¦šòçuÐÔê‰HžTŽÙLýDV.gaùÓQò%ùh˜tm E¥?DzÙ2yÞôQ%Â/˜BP)ðkê£û€^>9 øì# œ|ô ðÑ%@¿rX`¡ÚE0H„ÌDÆ’Lµðk›SöœòçT §9É©LŽ…ð›Râ¸Ún í¦XQŠõ¤XM&ø•ÂÖ—bu)Ö–Fø"Jn¤ØBŠ ¤1æ‰1OŒ…|‘à‹_$ø"Á©5j?§äÔƒœºSrêDN½È©9õ#·;’SOrêJN}É©39õ&§îäÔŸœ:”Srì’YMJSʘRÆ”2¦i|}_»ßÇÞ÷±ó}ì{»Þ{øÕ×ë/?Û>¬ãêã°ú8ª~„å",Õ–Ãqöq˜}eÙÇ1öc,×–Cô}‡ßÇ é'X.©-‡é#<úŽ>B£Ÿb¹Ë¥µåzX®‡åzX®‡åzX®‡åz}øê‡5…s‚jN`Í ®96'ÈæÚ¼¶97'èæÞœà›€s‚p^âœ`œs‚rN`Î Î9:¯‡tN Î Ö9;'hçîœà×<'ˆçòœ`žÐs‚zN`Ïëážàs‚|N Ï ö9?'èçþºâ}*Þ§â}*Þ§â}*Þ§â}Ƀ¤³1;ãûäoI>ðI¡¸à ®Uš®ÉÿDü'öñ@ Ë`Ìý]‘]]Òwáó…:Rεœ˜{.‹ûÏó:‘zØ\O»”WûÈèâ|ÑŒgJ 4ª»uiö{&¥G•ôx¤œ»Í€!O¦êçæ{áè&­RRÑ­ÂÖM:1Å_–$Kf÷ˆa˾¬´Á@~°ºÜÄ[Ôc4¦Ø-­dÔÈMÈj7?õFÓÍOëm£§¥NâpÔH ¡ï›'ä<¢^Eüªíx.Ã|wB#?%Ffb¸œD”ÞHÙaŽ: óaÝ}ôV¡â²Ôt MwöfˆGÔŒžXD£¨4Æ£¸|HFIùŽÒò¡7ê©ÂpËïðÝJãa%æ%QÙ­sÌ£žPm—–R¶FRQ(72‚¶”ñ‘D ‘z\¬'#ÝI˜ìp½}Áב6í|áO@ˆów¡ jº÷ã’\¤í¿Ú¡ìÙåjSdg£M~V4è|.o»Ëâ¨Zäúañ©éã/‘«©o4R‡ŸÒœØ¤Û¼¬®Iª«’‚çܹÏRÉ«Åt4WZ8RCk~ˆ6š6$qö,¢½ýª=oAwȦCg[Üz·8·GMGUµÃš'»{¹UÝõçW[5–S؃wìŒí3P¾ÅN…½ƒ†!Œ½Žb'1ô”«¦`¥z"¥i ß3£4òæ¯ÌÒÞÞ^<€Õb7Ò £ì>š›~²A¡Aýô©o,ìëñB°¶ºhò•!4V˜Hž Mïl*^غfÞ •Ï;xÞiϧuÛ« d8·#Iñ€7¤gzêl&S;TBÕÃŒJ€ -01GGŒ“F„8È‘¢ie;:á‰Ð ul©õ…ļxQ  yK”kµ¸E-טrQÞµuKHå,¡’m.©"ÀÖ¡ÍaßGs¬¹kn:•T•»u`\w„±Èç°²Õ*'d/Co_UÎO§±ð«-v„¼Óñ&ŸèXØ' )j¨0)Ùa‰kOØÌ£NŠÑѰBG{B-qwšD-î´–”?ƒ˜´°%ü°–T:ª·œ³C+ðrTñsÀWSâÂ`y]V-—xyWx²Œ*ŽøúßNíð9]´Ë%î:¢#xYïg³Æ9Æó¦£0¾lM!€1ÓkÑž1ïf¼:o6›®ÂײⶨÆÙ»ó¶³Û´yŽüv¹¤ ߘFž#«…¡D~+8º$Õ÷á葼8a‚7ÌÏÃ{$´q_/LìœY!f3‚¬m¢¾œÕ $šÇKjãœÑ岃ºª€Ù_Œ°s>¶ëȶÚ18÷¡Éǘjv®ønžˆ)rÁ·Â%œê&ˆÃv½" b?«XìÕͺíòâê‘Dd¹º&éê.½œ›^üÇõu…L£]x±Ý» °¤cE\“‚/ÛX…ýúvX ݼێž‹É>dñs9Ñç4ѰRO\m»g¸ìð~HWœbö“SãÚsøç*è÷Œ·=ð»£A6˜"+ Ê´Èd9¬£*Dü„ý29n#Ãk.ÆkÝ-“5ÛêÉÍ"{9Ò¹3+Oô­c÷¹ôh~bJ‘B“B*’:å#QªÏïZA7µ¢â)7önE_·ë·v(É6©e"q¿‡^sÊ¥L›b ر-µiÛç DgÔÆ”NÚ º©ÊB§åÆVÇ.K `×zlœtÒ[$mËì¤OZn¡Óì{‡/Ëw.Øs«Ë¢]®Íá¯EÎ$|îÛéSGo½ã%W™ƒ8¼áïh³Ð|—§£ÆЇzêé´¼ÁäN (Ç/;çËžÐ'€a<Õ~¶içÑ®EÀ,jp/©M@›–Ö€Í@,º¿h°±Î0Ëš˜²'Z éžõŠ{–› šfFÏæÕ T\ŽEo£ÀDkÑ[ºâƒ¾ ½ŽRèÀlYC¨Œ9x—Ö<²÷5•9O”Ð]h©Ÿdzw4¢/|Æš¯ÄYëʪ—k^ü=!%ÛÌUYémÕ—ø@_ø¼·Ñ£¶qX»¶_ êX‹¾›·éßÕå|tÕžTéëì<“ˆ\±Ò­¢N²¤ÒùHÚSÂ_n~/™†Eà_½B@iÛ üÍ©’¼ÇBä–'ñO#z£%…?áþC ^i‰&Ff¢“É/›=mÍ{|©7öé΋X½²N2¾ ót©Õ€ŠÛ øÕ¡“&&D2!ĨÄbc¯"(©²‘]6ÚW6†²±*Ûeã}eiÆ ç•T4j@¯é¼ˆsÇ “ïD *k“ÿD ŠÓ¯¸åÊ o…,©Q]‰ˆŠÄªH¬ŠÄV÷1å¶´Åju©\ÙËûÀ*7ggXƒBŠœã•"è#';ÂQN#Á‰!(ͨ©Ër …/`Š ÝƒƒÄòP«AµtXlUó*ö° þ)÷@$ÓyñÆwü……7Kc}*ÃëNJþȲ~tù‰.`ïÌQfÚäSYIuíé{ mÂ@ÃÕÕý6.ó9«ü¯^UÞ#Ú(×ø¾V¢²•°…曊¶ui´9*mƱoº´ŽïÅÌ ŒO$oî­ëÝ8é Æ”hdÓâ‘ÕsO^ÕãP,êq$Öô86s–ë™óF2oäÊ«V2gŽeæXË\Þfñb=žšr¥îÓzŒŒi Ø,e†RÊD?:öe 3dù’äÉŠ´¨(ñDÍ#úœÎ8ÿFUéÔs¸žÚ²)ݳ†_J~#Ò€_*ez]¬t¾ZýiV¬–RtÑ’± ~µ4É'”HDÄ Ðû2©Ò{™'ªd2§Œ[!; þ<) y„Š^ŸŽ§F3¾ISøej1í€äÐ-§¢$<ô„bq^9 ±ÿDã<Á/]œPµàE`Y‘é#×XŠ­=sdlÁ?hBƒ²-r³Re@â;mðšmGoQ§Œ' ¢øz°6A®·óçmá€[”8@¦vp¼Íb1£ÝwÅ|~"„ÅuR:‰?®6"¶ÎaUíœ÷ʈõ´¨èó¬“FB—äh«– iÛ~!=ìˆôqlÿƒ¥:ùJG+Ì`κÈèE&T[áRÖ‰ÌäŠÂFSÕ´KŒ»ÄÒñ§d赋>í²mG ²ØÝû˜„öÂR 85ù©RÔµ¥©?;‡é^rµ 4Î:¾Hä Ûà’ÊIÓ1ÕtBX ReŽHÙA¿’ÔM¿ÒTæ08P-ÊD‘*E©Z"UKTW );©2[¤Êl‘ƪšXU×US5‰ª&QÕ$ªšDU“ÔU“P5©ª&UÕ¤ªšTU“ZÕ|£†•¶aQÉŠ{DE%)*AQɉJLÜ'%*!qŒ¨DD%!*QɇûÄC%î¥lˆÕc…%BÞfc&ú¾1߯¶1#c踷gÐrü}ƒÆü|ß ÉžÐM·ü[4aó@”¿ÇÛžù¢»3î]ÊÞrŸwŸ›vÈ ß¨M´£ÎXÞÞ$ÊæÐû>“où¼ñåž³ÆÕ»ñsÀLù-8;~¥¥ßµôïZúw-½ü|M-½þÜgõü&³¸2éVG&¿«üùZ*€}*²<çhMü#É“µûMè·©{çxõ ¿y©ÂkzÝ臾9™ÿ0¨ûâñºï¼hP$3²”#{háã Q~‘"¡DR£oÖ´!L±Ë`‚è–»Õ ^%3ck b’KÏáhޤJk„‚ð|V»³¹÷GKÖUFç-ÃÇ4qÔe“·¸iòà5“÷“5Aû#7x2TCu]á§ŸÁ« ä°vuÑ}§îTb8xî0ÞErðösðÆsðª0—·»î¨E!Ï1 1®.-ù#åšbút7|è¶ÏTË Ùàx#-/ë5¡x2Šˆq›.%a©¹?7/øÅ!³ÒÜÓßè¨__î ¦â•‘RoRŒÏ£"¥x®`)ž+^Šç  ?mŸ6Å3ÐBÙ=¡et¿3q¥ŸÈh@Ý•Ï ~vÞJ)¯Û¬ óâý; »ëÕ£I¥ç•n;”S]Ðn —Ìœ§Ìdæ5úŽ[ÓQ¶8P`P-¢­V²ÐÓŒØ9Ï•§±R±Ë.*…€ …©*‚ùÚ’H­ÎeO¿æ—n´fÐ!,; ´œ„‘þ bˆ`ë 5/е€Ça}èÛ ïìí©8®TѵžÙEú uJ°J¸§ ® uä’„…âgW9ÐDèS2úù°?l^y åÊ èÞ3yc=¡æe2Çtqíá€w Š #¢gkÙ3ŠLž¨á0© ûý;5N«b“¿Ê¼m°bùÙ0>óçFsq¥¹ê„5V Ä»Á—â!{ÐÅE~†Ý9^Y6ͦÏ<£ùÈl^¨ÅWZn·„<½ »6Üßµ'Þ³€Þo<ãz@ºæp¿ã-ƒÏ@Ãt“¦Ñ « ýö‡±ž¬^Dbʪ½A4ˆÀ± Å8Ë‘]¯èC˜Å@<Ó HÓ Ê{AhæE×8&iÐôÓ!ð+¯œµ´£Þ0Ò´úfc7)4až±ÓQ2„ÿ{f,Ö÷F]&nšRrFªö~:èEQ!Œf,LtüKVÃBƒý a0 ÁÐÌZB¨ ìGCÀnÀ«xH00³&¢V@û0D\0ž‚þÐê@*—Uh°üްd¢A˜µÊ©aõ`‰¤¸HÅ”A€µFeVuv?ô`îÁ2„½hh+@&0¯ƒf%ìA¿ãÞpôzfV1,@‘Á ú¸œ¨q€cì›Håº\äH¢t7z˜”بUbÖ¾Á ÔNpÞ¬Äb¶¢W  AÃB&=àƺNc1[È~ÃÜÇÐÕ0ä×(2³&"ë0’,?@m0ʙŽAœÆP5ýÆC9#k"†íBEI`>R¯žÙ×DÒŠ~}çƒ^h2ÏÐÌ*‘°×æÆ,0X~q h3ì™YŰXöô12 ¤&µ…}3k*! ð©Í‚fè A(€Øt‰Â!ô˜*Ì( j0€ÿccí«u| Ó,™z• Ê»‚¼‘–W.DXN@NaÙ@&à5qˆˆcå•ü%ØÂüS"v¥= ßƒ¦ðR$<ˆ£ð : 㚉>èyÅZZÎ}Àˆá0„Rkl’qö$@¥“¸?:†) Ï05ë•«1Ap½HÝZ$IšXyïŒ2ÅP*Á5ô¬¼jl)Ì*LnÅ!P9 Hâˆz^1¶Ðjä,P'ÀVÒÀäs©ä‰1ð +àsÜž`ÁL.I\:Ã^ ø'°‚?ÎVÞX[À@ˆ$쀓0)Ȧ­¼rlá ¶‰+¸}X×îƒÈ0W}$½þS10ym¹,a~C 4Q#ÿÄÏ€ø½†g‰ÄI ž=$7Àz°ˆ¡àxV^96@ò³Ei‚ xeõAÐQèmoýJL€<ÿÑ©H®–&LJ,„ˆH/¬a`þÉЬW®MœÞ¦ *q䄉…i$å¡€>,!áàÆ±…g©¤¥ÙI …€Ûƒ“ l|H%ЬK¦,S_ßÊ›*|éV3€€$}*ª¯ ƒß§6¿ïK:äÈqHÃ’À#¤_êK±,B€Áä]Á0ÔAß—ü~+2£ X‹ô 4ŒžY«˜Ð&Öu«z’p´¡Õ1Ÿ @dn +Êf)àଙµ'Ř×>®˜£>4ÒNafíËÕÝ z!-[X…@ †ÀBÌZ%Ⴕ“$ˆ}S‰¼9ͬrm ƒtŒ´Hxf'1c_’­ˆ„ïpÊQ"P R3«ÀA‚¤D­^ r#È= ¡XÃ’D+ @ÈnØGÖ˜:AfSâ1”³öðƒXŒ´³ â H½5)³öy ì dœõ@™AÚbKÊ&ó!0¥>ÈÒ1pô^˜Y¥È<Þ!#‡Öa¾a¦Æ¢îKÙ–{ x:$ 0 ¸¾™U`@`J ½Ã´`YÙÀ’TVh :x°֜ظ'å(àc¨•D(ýaqÆ©™U hõp‰:Œ§’<Ò È«¬’´‚,˜¢@².ÐÍ J€ª‰>¬DaÈm=XõDQb,–ô3«$¬Ä\¨ÌaoðÀ¸ A®/éjŒÂ.°Ì— °XèôݬU r¢EO\ 0»2IO¢ h†@k£a1aM¡'&HúRÙ CÔ'‡!1C•"HZ ãòÜ둈€ÂšÉû©”z”D`j“J0= Äq00³ Ì ˜â[ÔRQŸƒB@ç̬‰d,ÀKzÈ\q© ÖƒÚ¿1¬T‰‡P| ³€_Q€LÆš‚´'EÔçaQ$´`’·„fV¬!j™A„Òq´H6Èj†, Ý˹_ˆ a«+D¥.²jíÉe¯—†(š£ÂU–ZY¥, €Œp‚pV¿ ‘ÂöͬX ÆÀkÊ 6…kËÈ*5Ì>*¼ fæH ͬ=Y+4Œ"Es ´x˜˜8ÐÀB!ÑØeŠ$~˜$vúR z=„µË É ,E`í¦$RrÎI<Œ) s´j¶ËÕ÷•„Z4 D€{ÐÀI]V^±hü@D †@C”hBc+o_ÎLÙ0 ¦cTŠ…âÀÄ3ÉG¦áÍ% $Àõ¨+¦TØ—Ey#.  b B¸Ô{C«^3˜ƒÄò>jM ôöj¬z%w@kP‚ô;E vÔ$íY¸#õ|àù¸âPÁN°RSú6$3…,ÈDy"Pp#×ú8Hš‡¶D3PµQ/6ÍM9³SÈ +Ú ì(ÏØEHÚ¨†;Ò‚€Ò!‘`ư&V^Iö€ãRö‘[‚ ˜ÙB f%G$c'* =Ð{±µ.’Tjå°2Дšc„kb­ ÉS£Û]ƒ!, à% ER ¾‰À³!Šè€ £Ìí)&|S©•£Èž’a t1Xp( ôM8¤Êô‡‚*,e •A€â*oV^M+Gð“”t0àØö:N¥ñ@ÿ£9$Ë^ФÒÒ€úJBa¤¦ FS(Ò(Àõ­±Éµ ôæ„u4)ì¼ËÒ"Ìú(·÷PE*L̵oZhs†h¢ Ö€ð®gÒõžE€â´U”Ý%DÓ¡•Wòn?ÄìÌ!,¡ ³ž€H€ê0 ™ H<€56É`à 9(ゾKJ&Ñ+¯T¡ ÁÚÀµß')ØES%‹’>D«*pë^¤¬2ddÍ…ä±@èú(g£–¦žõ¥Ê‰„º‡š)’ôÀ~`­ã¾Ü¸¢ n€ ˜,(P“ÂÀÊ+-$(b£ïE 2è±ÕCÏ+×fŠÒÓw†h‹Š [8ÙïibZõ€Á‚ò‰›2QhÑɾÔ;A&ô9:Å­$4l$NÞbߟwçkßÿ@Ðöãݦù2+èͤº`1iÄMÅö@‹# ˰À€LMfÒH8çgU’ˆB9ð¾.\1‚°M 70[È|§ô0à¬Q™Uô%¬ Å$ƒøfˆð5nʽ¹(MpK„:àýÕe3«èkŠfD…Ñ≛§}佑Ù×Tô5p}´¬0±$/FÀfPnˆªïi­ËéæQD¹:E­ dú¸ƒžYbÕ ZÚ)¼‚‚€¶Š íš#‰ )¥1ÚöAe. ?°k–ÀZ ê'Ô LH=K!.Û߃vÑMy0-(ü&4Ot¡J| ñA€Œ´_›ºÑ.F¡”€€ Ôsò\èB;Ð(zh  ºUŒòSbäL9gÍX €DHŒ»ÑCSúŸ4zœ“v•ÍQ©ÖJ}NTƹIœÄz4Ôâv‰•ûˆ8°D€1ÊE âIèDeÜ.G õ Ñá>wd%*ƒ^2îÁ¡‚Æ!XÓ‰™U 2,µ*=ÜnNÙ ›Ãˆ¡Y4ß¡õ;NAÐE_‹™µ/–ZzûÖ÷°º@@±î P; b .û¨f$$Ö-- HSíÊQÝ Qc†ÜÑÔ@Ü $•5r,Ü Rè: €H}CÏ׺P % å'@ð W]zgGÂALÆß BÿÜSíš%‚ãf<Ð߉@âæw`l’ÜR¼™<œxS‘Sf»‚‹ùøÓ¸‘ûWê”]§Ð¹óGŒi;Ï7Z3ã`å‘')'„¶€î¬vq¤°ë4u¹^½È§ÙÆËÓZãKH»’‡2AðÁƒhyg;§ƒu«"+ȯ”. X-v” ÝK»¢¾þåõi÷oÒrKЩé/hÄÆßÕÌ~üÚÞþìá/¸w¼àQzc}þøôùõGÏûËö·¿¿þTŸïÐ{!Nª¿’Xì†gýx—â¡÷ "ÃcSþ¼þôNôñœ¢‡7ëó'žŸÛgèk³/ÇÈ~ðl½üð!O:Ögz¹Z_ø úÎù7ÿÑÎÿMóió!ø`±[fë|2^” ˆÕøF>›y V•Ïp¬0âÔ{™ß/OÓˆƒÛ^¬#ôMžÅ'³®·á3T^þ¢;PLc¼ƒ×›çŒd>Î×ÐVtÖõ^Ž‹"[ÛÜ G¬7t5BæeEö"CiáDHÐÌ3S~ÚeèOæ4XQÊ‹ßÛÀ`^eë¶igÏÐùâ9üøøþÓçîœóÿýõçߺ¼ûëM÷Ío5óëyg7{ýñ—îË×ïß×Òå³É úã• uºZ]<רO— åÅ™õ $V¼vÞcUzöŽÂ{”A²YnºZ›Ë5´]ØÀZq./ô›óü'/L|GÑÎ ÷™‡¡8±‚.dÓœP]U{â/M7¯ÖóH|CŸ²‰·w¯?ß|úì}úýõ»wÞ›?Þ¼ùüöÃûçÞùë÷¼þøO/ør±¦Â¹¼€—ÂÝF5e“õ¢Ð¦¶è¤ÄËËVUtZe.  ¯‘]A3žfE»@ØáÜA=\R’2ÓŠçntÓ*,OÏ\÷‹ŸŠä ÅxY4|¹KÌP+ìv»ˆKÛIב‰Úô°Q¯±É‡³Åêe ø+-+^o‚´òªC£Ty°vÊÁ÷Ÿ\—•¡¼){–eU^ƒ2^#!½\¹À+&ðb¨ìlŒ¿Ë>Í}*‰Û´»ù«c¤Ø^Bå²"Â(ëѰV•_!,ت¸>°X­™§ªn¸ãòQ‘7ðw¶ ð]Ÿ§•7–@Ž `ª[ë¼vôCp¾Ó2`ÕcX›ÈÕðô0%¾²ÂÄ™·ÈŠ3H)ÊŽäÜú ]ŸÌi–°Üøá^BXr<ç6ŒütPStÅž <#¾ñ½5Þɲ¸fÈ¡r»XÍjjÀ‹x­PëåÅmìC}—ÃÚ. @×eGGµ.ÕT!/Ô0ûÚÈ5Ì@S~„³¤]/çùd.õy–b~d¸º@É_ªmJúÈ pmðì6Z)\8Ö %#…Ÿ«Í°ðP2šy(¿BÏ./® ÆÞt·\^ë]6HÅ+1©|ÐtA±‘«÷yájU,w SÎÇâV@<±{ØCM‚ØOg×þ¿îJó¡lƒ*¨#ý˜!ôÑ(ôß‘… KƒÎnYtÄòÓ<^¯Ç×¾›øÌvë"ßÌùh3ƒq÷Рr,ÚÕº“Ò‚WÓtÀÁ±:ÍQ°ÝÐä­PÔÝ®Üã*ƒö#Z4uxë¨f`i!²o\'å3Ôrÿ¼éTxsšw¢¯ç.ˆ#Åó+£ÚŽ/²ÚóN¡ÛÐ1_\íî‘è|Í,çã3ã:/sÀäC뜷Iel‡‡¨i(hDš1Ǹ£K}äÃkí¹$eÌ ¦5°º±ø:µú•׬ùú{ U?%Î ÃZ‰ªÊKûy¹8¶o,Ý$„Ènˆ‰Þl×»Év·ÎŽ„ Õ8*Ì®ŒãYyìj—ÖEèXxº‚^#¡ ©žw Œf‹| ÒÎZ \‚FÔ¡ C<Ö\²œ—S#nðekû$Únü8Ò.ù,VE´¸•BàÍ^Ö%4â1.`Q@ÃPÂi‘f¯®‡õ=q´jun|º„¾Ò‰JQ¼%‚äºÓLÅü‰).»Íú Zit# 8²Ô0sC3«3PóÇاŠvÌj$>EƒƒêSº’FŽûéò)i3šRß|îuJ0Ž7åŽ×‚<²p8à 5ìÇ< _-ïÊvKâ?Úµ˜¢¦»uqvQ A†PgqšK È»4jÅ$Ic¼m¥¾Û¡oËB¼|#WûQÒi`¼ôiÞo-Œ— *X'À{Åÿëè1e¨Dcʺ½ Éáæ3Þè hÓE=¹¦þÎÊ?ÜYK>ZöÁÁE†)r§ˆ+°%Wäîi °\¼%–|? ±½Â†Zç#tzjI;G`9¸v_bÚ1ìPõ¹1¥:‹ÓZ9Æ#ÚáS¥ÈÓhEhˆB«ëK³3è ³3?7;s[uô xFæ†Sð€sý•“Weúä5ÎGˆàçnß8wÆ8yB´´—?ñvx½•α³0Ý¡±“DÐ3¾~¼ëi@#Èò- ¯¬†Žg¸çœ‘%§Üí&+ŽkØ{8h p7¿½‹Ú)×(Ö>Þ9I hy±£¿‡<šŸÊ·ÒXËÍtg±.™)]°âÞK¥scú,<½~Š~ ˜V¬;Û¹»ò³u>í\®`v½§B*ZgBy5L–n…×ãeËÛHe]Î5ðAüTR„, D­²œ³ô)šéÉëbÅÚÐzw¹½Ô‘³×}XMÙÖûd+Þ‰é²W]ׄI½²GôÜ@õXFØ5÷”gï)ˆµz5rújL.rÀk˜ï0ÎþO_ŒóÅÑk;'£]ÑIz4Ô£ö?þè ŸŠøýFóáñ¼~øÁQ’Í]š«M½“ËRºÏí†P…ГM­ž°°WjÐÍW»Å8§2bU å¯éŠ3ßëÌ·RÃÐs6Æ©¥é#j¹i¦~yé1s‚”š°¤H´!IH”³¤fÆŽ:‰‰èàsƒ:šrÏžr¡]®DìÚëUà¶,O¦/ &b§7—ãI¦Y°ŸÒþ÷S˜ñE#8Ò¨éw»òwÐäÅ‚l¹“µg ¹·+¶-€¡×´\ºaM]Ó$;íªf‰eSEªHJ þ4ƒŠ]ˆ™ÂÍ_±D]óõˆ›Õ%èG[àh°Ù¹µ$nXëÁK¾Øà5¼ró€Tbap+ÂEÁ‹»«»äãé‹q±®Úއâ®É w¼MoðÑ ȳ>c <ÍÖRòMæ€ ÁQ+\˜,øP’6Ü¥›£l_Xšd%AôÔØØ>…ªØB³pÁÚZ½' ª‚£à åÌ€¨€Í‹ÊÓbÍ!ù"[MÇM¯lr·ÔjVÇ$ÀÌ×|Z‰Ï›£F—'s· io×cÙõ¾ÕëDÇ´¤IAbX¼,»\-h…ëç;êôtÝ(O?quqGfä³…@x¶ΩU\Ñ:èΫǬˆ÷¾…rY=ôi1]Lä5#DËgvÛÞ¼jÿÇ5 a‹ê/¡¶Œ°¡ã”å*(dÈZ(kqo<Å8ðÚ"¾Á_I‡*3Q¸nÕd$§1ß(˜Œ4Æ&ì8'/6±N*BÊoL;xãÕyˆÊ…a`WÞ^Ø5[š}G×ÒÙä4ïªß´ov€™0ä­Q–« ýÈa×9jÛ4’`¼L¹µF>Éšqñ\Döá|¦*ŒôÊ4-l‰3¨öêç¾VDoßµZ‘µÒ¦Ã/T @+Ü’Ç^ÐdñÉž@ ÒÏ6<6t,\0âÔøœÖO¿è•¶AÌI½úŽj€éZõt$â¤~}Eq}EýjEƒ¦aÚ)=1ØÈN梖û€h(~K 5¡Äe ± —°*Š@ÒZ€ÆÄ–|Û½ýV4s i¦z dG:nÃU¾$k'ˆlÓ¼ÆHektkƒ¶ª¾¨4$÷™òØöë*d"¶šÑB\àQ´Åµ7©ÛÜá†rqÍ2jGºÈxy™M؃Vnðëš>è-ø tCøÛ^"Û¥1P¬*”î—a©59]†h/âåIÚÎ2B0Ô=¨g¸™¤œÑ3 üËre ùØ_ò¡! G)I¥&Ê’;‹Pº+_Ö=ëú qö[ç#’^>½ð½ÏFgKÿšíH½¼¤ê^}UUiž¬å2*N`d¸5Ià\¡Ç-Œ~´ŒFð¦ìv€tåÍj™•kÏrj§]Rà”.¥y´ƒ’Ôsö$J¹ í^É̸£bà‹œ%êmÍŽ¦Çx‰6ÜÜûé¦×jèû÷¤žËzìô«kc…‡\ëh‘bT´Î’­MçøPYnPy7%¡°ër@¸p¸Ák—%-V‘¹â™94áÃè¦/ÒIÐv³Ö\Ž¢0†{à&,`¬?n †>³Ýûh£ÏãkM³"Õcmfcù¡µ½<° ÉlN•XËEwÐ#0–u½œeöá¢æÝAk@‡BGVG¢Þ¥¼» +reÈq×´‘^ IˬnNâ ”!ÅãÙ<‡ô…†]‘o}U‡ {]£, 5$è¶Eq ™=®§R®”òêªê2!«ºÃi`ÖŒ¦4IÒž1ˆèz`îR½"U–|¡+Bc^LÖ)£\©«!@áÝôQC-+Ì‚îã¬H{'« HŒÂLA‰•™Gt¸ì½ÏªJO#èAˆ‡ }PŒÄ)-UÔ=®lŪxõ˨Ù(t7ؘ, T¦ìÔðhí@±æò’<0ÊÚš.ð¨(ʃ\ ‚o”?TIƒ„„0Nˆíal…ì’j¤Z޾wŠÆYÔjU>Œß =Št4'l¶8@`ÓGÊ­T‰hÈ?xŽq³õƒÌâóŽŒËæ¤*ÓܦÂF¸qU‘Õc)Éòc»¶ *4\ì:SB[:³‡/>V¬<„S¦ £y.d\‘‹ì5 ©k¯P–C:ï&5Þ°áci¾$‹¥¨¯®  W‚ŸÁÔ üwžâ¸Ù× ÏDFT¾•¢¦¡¤I|þÑ,3nHIu=ÁÒºw;]ˆ³d/ÍY ö\¹"J™mPò©q™‘Zÿ#ÝF²suø-¬ÝžR°yzäÞáoCñ>Ì~E鯮ÛÛ^uÛò…~ÈUžHEó᫃jÞ)nÞHBíWƒ©˜æÈþWÈí–Æ=ýä¬Ã'ïã)÷aôc¯‚ÉKthr § w ÚYnLNså'°ÎÐlˆ«Lëh@4€4îqÍÙK 8‰—"êÇÊJÜþ„\(’…º:xÊÕM–éVA8æf(„K:t'‡±Û=­w"OZk)À'9a@Ï}ebjž"¡#ï<ŽtOÉÄÓäGGôT‰BøAvUˆƒèåv‚r´Òx:Z¨a3#“XS£¯iå* #E4%àíþӦ௢?h -Ĺš²?.Ãkd9ñ <ªr… ç1Lúè}^\7õÑÒÉfu½E㺨žiª°¸ë{>:êùÑnɆy=¿ ¹Ìa^Ó™D„èb³ÖA¶AU¡†7Ò-ýDé¥ c½?HÖA«° \å§Hg† í´Ü“§pïÖ¬Ö/Çëiçt<¹ÀlyË·DV€Ã‹ëÆ%8æ_™ƒÐ8ÔXW¯¯:†åÛày@f€XÝöŸ> v"~Çê5¸ÃÅcä0M§³[Má¯æJg:TTŠ£_–Èù¯x¿XÁM¶þ9jþx ¤Þ§­·ÂG¶"¯ ®õäv@w 7¾c¼%kL¶ŠKÌvG*öwAÇFÕâ’öH4@{æàâå3Ì¥^oO4|^»T-…éI¹Ö³X^À‰ÍìFWض9çô K£aCnèã6+¾´£ŠsAºôQ w og5•éX=¿Ãƒ\ð»ZwEâ'V™ë!¯Ÿá]‰¸š“凌ÁQht–oHˆ¶Ÿù•ÖK¸Y1éxçz|v†édžM.X=(ÝËK&æ].A èââòúdŠÏ™ œ•qáÛ1+Q¼Á±.“è‚äÊO/»ÅN(’ã?³J¹aM#}~Ú­¶#ÑÇÑ1+‹HI´Ù–R "v³"§Ìƒ•ëÍaöeºbc˜ zyzùý ×xæçþ³æÃ1@Ì@Ì æ{€˜ æû(Ô¥ŠUô&¡RZmW–.˜9•@—¾&N¤ÆúFì~âœIz/k0iC!b¹"/×+:ôD¢´ô{yæW+­¤K¾Èé4@?™ Tâ¨äÛQß x#‹ZàÈ·Ç@c²[çèÿ¤Ã#.á?<˜ÑZûç¦ÛË^à4 ç¦9®\q3Œd½×Õ^–¾Ž3ë–=Í—Dh‘½+òbޤ#ƒ–¤S‚–¤ƒ6óT…˜v®®°ß¶©FúŽÑy¨0'µ~>ËÍþÔïù}œWª¾#ú–Ñ5\së€ú˜zÇk²LA­ÉÂÒV ÆLñ{s*L”±Å]ÞhÈ…—rj†àK9/¡ŠP@.²ä—@_ÊùÀ=m==*Óí‹m—äQ?Ã6áºÃ®…@AûÕaa##')ƒ“šã›{‚+VàºQ÷fHª½{í3ˆá®›íÈ “Z0Rû#`¯°ˆ…xvBUùÚî%Ƚ…¦Ú\hÌ7½BŒÕ«¿Øöä~`GI·rñWD{"œfîˆÐ[_[ë#2n»6„ÓZSµzpüÚäyF¿U½½MD¼mPÛF42Yh¥ êXZHV¨`çB9bº€Oâ„QÍô•Åâ• &ýZ˜ÌQ¸å]'Lè½)@pûÚü’û…_kÊpÄŠì_CóÁ£îYËê¥u8Aoé¶HQ‹{΂׷±æZš\ii­µ4¬Åì’BD,©°åV«™ÇäÕZÓÔðkú¾ö9ëæ1>ß#N4ß¹rÂi®ó¥ï’ñÕÖû M²žÐ¹‡|×E2YÓØÏ á\GPiù¨^”¶^½ì(‹òà‹:|ºÈc#f§£]_E§<•³Vú^iSL=°gr1­œ,Õ_f¡xGùœ(&\œ–Z9ªÈLâv¬$¨½LѨƒáfR€!ë4í°JûÂÑ6?,¦•ã¥VŽZ© ç«ÓQb¥;L2klg­˜ ªe©k&˜´îWÓ©¡T¯Ó’Õô›^ðæ¯§„«ÒW%Yðr<'l0ò¢¬¾“)8Ñ.xÖžÛõ+úoÜ•gGPŒ½Æ,\` ©vž žÑÞ9¢,Ö<ò‰‰ºPq×8ªÕäýÚëÔ݈å5ŸÊ›òõƒ[«Kq¹zvë·ÄƒV×uœÆÒV‘XF8 î2ôùLŸpdô»“ÅlÑ&õ)Õ¯Ëñº¼“âš/IQ§ÄyKÔÙè\™aCË^…£(,Ÿ@¥…¤vQ¦Ä˜i)צÄZ Þ<ÂZÚ Ôez¡§aí3½öÖ>Ókñ“ôê‘2Ò‹vÒ*Soé]»héÔ7Ï2lIЃ!Iù*Z0ƒx•…Ú›|DïËW¥B·Ú’V=®o™‰šÈœªÊUUR¿op\ ¼UNy±ÙÉíÕº.}têòk´°Q­·CBOCý°5´6q%¿4%¿Äù„ÄvIxñyN©QÛäáŒ5àsªÂJ:ºuDŸ8 }€]v¹¼üÎÐcÁ1ù‚œZVòÐ#ûe&ÝUørtö·ÐF@•ÛÝ6³yÕ˜CöVzzmW ­Ë»`xpRÖõx Y+!U€ús¿6| î[VÂ¥ØñPô°'8 ­<šÑ}áQŠFM>)ĺVä£{)RX¹ªÕ`[iÕWz Û´üШBÆ-ÑW!3Œˆ…m$sMõ3jjÅeb¬Þ_ê'’4õ0+«˜¹ª˜•U  ge1 8ÚïÜQò8a@§Å¦?DúÃÜQˆfÓÇ­`w€}Úøõy¿×çm^ww}ÜÔÕoÐH)=kFX…™ˆ'í4¢+=PFÔ„™Ú‡TjÒL@2uÁL½»d$FVLGÒÔ @—»Õºnÿ“££j{Vû·?ñs‹ýÅFH6M¹Ãxë-ÆÃ;Šf‡îÕÊ÷uhÿv]ýèZ•ÄÜ"¼ ߨöÑ“ÿIœ8dÓ8ÿU迊üW±Vâ:ð‘vù³ÐŸEþ,ögXJ ‚¹?£‹”/CøÁ?fµÒt»ÛÉ„±²YE¸XD4³Ô¯µ“¬+®î#&\ 4ã)š$îÕ-Ý‹ÞörºO­\ã½^…ôÑwŒß×~ã¤Sz?£÷3|_©¢Á¨„yðzeÆ&4ðõ“ €WËeaƒP²6OyùT)h;h¹Ý®¤T ¨Ù J?å@Ò†V5Ç—ôN ¯ëqJ& X9E ÿ=Òd‹i*©UÄ¥\`L“å"ƒeÿN‰œžúK¼´¿g3xÆ©Ž 0vaôÉoÑ7—&¼Æj·§¹bTÊŸ ÏòÃ?ÿ‰}tC®ó';ì@&îäÌ7êËaË£átø7»ö uñÞ¸C§Ø  (ÔŽ&ÀyµÊ§x· ‰Ó‹õd„ª[Å•©ø[‹åʱ™2Ÿ@)½ø¥¥mÃp4ì†QÅÃdÐÃh˜Dýa4í¨¸ÒÛ0Qì0JÓh˜Q0L“($±êÛ6hÇÝ ˆ†áCO‡ ÆÒNÌŠ"<%a?ìã¨ôà„ƒa¯¬'Š0Km ã4‚¯þp8ŒÃáPË0t7v4 £~Úz!äëkYblhØëÉ D^ÆC¶2K#Oº1 ©? ƒ ‡i§a gÁÎÄ1 i¥á LÁpЃÁëy°7)Œ©¤1t5ô :®7ãî$ݰéƒtí„à éGýXÏC@¨ôÑ úè<ŠqJôLØXÒÇ€Ý)ÅvOÓ Ú4æ0vŒØDLœ`ˆîAoÐO‡©ž ¨b—"®÷†Ép¦½ãÏGz–˜êö‚x‚½Š`>“aßpÝ\ÒÞ¤´–âW;”ï/W›";mò³‚0Üç}?IPP.F¸Ÿ‘«ð©éã/‘«©ûÜR‡ÇâœÝl¥—í¼¬®I"¶ æÜ¹ÏRÉ«Åt4Wê²KË9‡˜§isAhå" íU{Þ‚îïC‡Žµ¸5ôjpn†š;¡ªj‡Gv÷r«º B8Z%uB I#”ö¢´íå[å„§S”=ï[4 A«·½Kƒ÷QvóbžÖzÊÕ S°R=‘Òj“öº9º6•HCé㬻‘^%¦ÑÜl”x kE ê§O}cK¿k« †[¹]ƒ Éÿ©é­¶+¨™k®@^ãM8ù¼ƒçö|Z·…²sË‹ä=x“Az¦§Îf2µC%T=\À¨ÄˆH:Êáž>’âaú„Q¡- *]­"Œ©Á¨=µçÇÔn,š9. ¹jEÊ蜠~VìKtF¿ÚÂPìŽ7ùD¿« B…ºH=;,uÔù :0QèÈB³&nr‘³Æ–2¥Úir¥vN«„VêQGõÎc†o/G•hBK¢ÅB[±¼×`º—K¼J$Tñs9Íç4ͰJO\m»g¸ìð~HWöRã\¬9üstÆ&Pü®Áh-2¦ðÇv™2-2™Þ¹­ ­ÅNDäU”Eò–á|í™§OK2íÂÉQg,Xs”I¨2¬8<”ê#»v9BÍì Û©‚Vi<Æ.ød!ÎB¶ófk6IóèÙœ¦ûÎB!¬Òªê*ݚܿ[ Ÿ¯LÑ¢‹‘êâ1½¢cŠøEt"ÖgPd cômpuJ!×zT¤¬9ÐrðQ¨R7"í©©”#QBwa¡Ž’}ʵ×D$„Oc°å_œÊ¨,L)˜‰¿'$b™¹*+±âG¥úè Ÿ 1zÔ6ŽuÔö«AkÑwó6ý»ºݬ=?,ÐÙ§|Jïðò˜ÏQ’ys¢®I'È,kƒÅIá_…|(n˜É‘;9v&ÏÜ•Ìܕ̕ìî'ñO£ù‰©©MŸF“°¯´´Ò"3Í9äñE8b½±]4­7‘zc˜¸ˆÕ+ëpÀ«0‡ÎüÔj@Åíüê ‘²‰Ï‘xñª°˜á«JFªdd•Œö”Œ¡d¬JÆVÉxOIš+è³c¾¡?Žé†¶³&°TÓFCW Jâ¸UÉqæHfŽdæÈ‘9æÌ±ÌË̱•Ù½«{`±-V«Kå>FW­æ3¼&øN Ž?t>ÂBo¶« ,Ä?Å>*]ã4°Ön«ŽGHxñÎT¢¢°Xtž?DÙñäÅYÖEâÌι\ÉÒéҾΜBcæS³ª:‰O·©´ ]öŠ=ž)²µa]ÎYj{Ö nÇ5ݺéH5Ý¯éø–M_ÊvãºvK«@¬ÍÕfµ!û8楺–êpÇá¨:9ª€ƒäxT”¤´–Ç!/åqÄ+ylóÇWåR¦¼‘ȹò–+™òÆ"olçµàʸŋõxjÊ`ºñò˜%$"YhJeÏñQŽ4¿¦#-Çñ‡®’À/K³#Z_VúôºXU¹džN³bµ”Jå%V…_íÍì ånâžOƒ¦¯L¨ rÄŽîy*#ýž _,1ût<5îÏç~Ä1o-P/º}©†ÊáQôTæÓf[¦yNOýíü'ú ~-ª*·ªïÏ®óö—*«¼t;ýË(Ö‚Ð|Õ†Q`U·ÑœîjPžjóqYÜŪï·ä;Tà¤A;zzp™Spö¶ó—i!ÄZ”·ÕÂSñ°ƒjžÃ³8ö «ØwâÒøüDè‹ö*¤ÎuºÌ‹°‘—•9-gâÃfXd -ª zÚámËFB®hýÞWÍHÈôõYä>Ž}àÀú8¶q„«æ‚UGA}fÖ¸ÜjЧ)--ro=Ž$^C$ÄàO)ÂE¡f “Œèð5½$:<¾É–ä@ö¤ð9³ÄWO š³¨ú‚XÑ,®¼¼Xß …M`}3”5õ¹23ó‹DæHdŽÜ™™ûÅœY ú9çñäzDÒGËOàM"ü=Äd28È­ÒTI¼olÊ{æ9÷§ÓþEð/&çCò“»ük6¯\9=øäøB‹¯÷d^Ê<ù'yîÁx\—|ޤroÛÅÞ Áó½âÍKušÁëF?Œøäï£Ú“ų¼ 3÷>nzžc[žåÈOUHñk:7M¾‹ÐÝV«¥@xÐ* v‚¡PSÉùô°ÓF‹hÂ×›ÓÝͽÿ8ZÙ‡Â|rsjnÛÈ4êÂ;Í’-6£mš%nÊâ?ºp‚í ³–j€?qËE5JÌC§£ÊÑ©âénn…â£Æ2žÌA›%`G'ðRH"ÀWL±eÙê„ñ‹*OPó‹?d.;“>­zºƒdb§G&[á~óF ²¼±GkÃ®ÅØX÷,÷|?EC›O'¾6µÏÜÉ@“C•î¢ £J¬•‘dÂcÞ¡êš'hh°L#ð>„÷HäŒe¾Æjb´8ÀK×›Æø¢Ã?±rg¢&åR¢vBgÖ¸©™pD½5vïÿù:&w«­kGÃU¾}\K¶±]-YH¶ßaÓUj€ŸŠ[†W㿱o@5N޾Sw*®žÛ{Ã{ o¯‡wÐëÂ\nÎØ”kB9Ò1 ¨®.M¢C§©Ì 1Ž(¸>ä®à™ eô±RK¹EAšååI·"ä¿f–šûTÆ2óõæž&xSBß…uzWy¥ë2y:™p”ë”çòžò\Tžc­þ´B…I[MO‘²{B‡ËºtE j³?‘ýŽïæÏ ~–{½q`cÞÙ]?Ñ•¶+ ;ôSßж,¥¡nÁuL„$·:¤¶%\„Õ¢=(+™†b½ÀÎy®|<•Š lt‘@)Œà\ònPþcK@µê›>ý&š]‘]]–wÔmÀ¹?¨ØvÍ @ Rì|Gß9æM> ´ ƒHê™ýÑA ²“„¡réÖéš×WE7D$8ÄHx„4»Ê„AŸ*#–Z‡ýa/Z·’éºÈ½÷dû ¹fkÙ3¾_[ ‡)cØïß©ñò˜m ¯õ0>óçFsq¥¹êì5Ö‡X+ø¾H¼…`=ã "éBdº4ù™g4™Í ñŠ<þ)ΰº7»6Üßµ'Þ³`xØñ¶ñŒëÁ»°ùÌÞ;ýÊÂA7iݰz¡ðoÿ¹ Fx s•cEê†tÒNÒFüO²ÿæýÎ4‰1QlŒ²™JlçÀŒ×ãpà.ÆYº\¯^ÐÕi*PïøÒ®¤&/Â.äŠ=*îQT‘"õûÿeð ¨ô t°!‡£Ž”ñZ/W‹ëbµÌAÛV!zÊØ*<@y÷>^E¦_°î5 šÝûO Í΃±›ÖZíÄßõX(˜W|Ý<Á÷ª6Ä¡;4Û›VCªE³ÝØÈýU•(÷‡Y|¤´ãª2Ä¥ëÓ¡ua!ë½WÅ_¾¾þç·×o?Þ|ÿú{{ûîÆûTù†«ªüÛë÷¿Þ,>üеvÞxÓyòd‹ù[Xà͇÷Ÿ_¿}ÿÉ{ ?ß½»yC5ø›÷éûÿ¼ùµþöÚxýŸ7ÞÏ77ï½_nþóæÝ‡¿ßüâýüO/ûøþÓgoNí½~ÿ‹wvóñ·×ñ^¾~ÿþæcÆw#*¢žA+o ÃŸ gTûô·ÏŸÿþüääÿøG÷÷o½é¾ùíäÿ+äÓ‡¿}þäïþöù÷wO»Þúæõ/ЛoñöÍÍû77¸vx¼ûÕÿñÞ}ôÞ¾‡§ß_kðÁ"ïD‘¯=ùQëÿã ¬‚_º{”6ö¯ÿ~ÐOµþÐúâïëÿK|„°¡‰¼ Œ¯§öMµF¨Ú»¬~‹Cm&ºˆnUâ«Uü…ø³f¡8§?õ})ía ï?ÈýŽÀVxc£ŒCRÅË èÚêi¶ËàA«õ4/Æëk3~uy™wý¬qÕÍcø_qCR«Ð ãòÑÁÚQ°.Ê ÍÖ»â,ë\ì¶Û±¸¥»¤b òeÚ¤™z"2е&çþȲ9:<4èŽÒߪáÇ1çäÚL¨Zô¯ñZ[ož{Y×›ó5EPšzg]ïå¸(²Jļ] Àzƒ7ïÁxβ"{Aä.·´•èy†úÅO»jÛɼFa(JyQâÓô¯²õÛ´³g%hõÀ_ˆt™üw ¿)ª^òGrŒ.s g!×䀶4Yç§S´À›Ì Qò¼ö¥¢sºZ]èÉØ`,ÝË®W0³íÖ¢OžWèx+±I¿þÂW9ÖUl¶ðVEhïz:ÊOÉ+^¸A,Àû6^>ZÆ4§ÊÆ‹r^&ßsì¼ÀI8óáp«ppâ#ó‹¿4ÿ¼€6¸éGn6qà{P6-ã¼ÙÅë"à©…Öd©‡I}”ªª^)ã‚xaIC^îдt- ¤rQtà z-vÐkQ¶HD@MˆÏQÜݧµVZ¿gæ&²ãSÑF‘„à­!x±ˆ³„ºÀârž»sÌøj¥n2 :×0£Ð¿¢«|(¢õ¨Ùºn„5­Ìð&w `ØàvR‰<éq0cE¿Ô¦8¶(!ê«—®rÓ]˹JDË¡sAµsÞÿÇQ–ï¹È9ò·TaEÄ{ ‹ñ¨s²A¹#–… 1²ÐÜz~°—Ž˜Äx^¯#wÚKc R+qäÙ¯ëS¬Ç{”Ãq¡eÍuÿÚk`lRŒ„,iG¿†~z8BgÔ8=<·Ç=‡E±¸æ;ô¡“Ss tÌS13A§e<Ø($vç—Sß,G™*Úã o†Q±-éä¼÷£wUfV2þ†ÕGûýØÌø”¸¶ôa(o+ر™+ÌŸ0QÈ:ÜÑB?´ëSâ‹ c0ЕNj¬8ƒ”¢dŠ9÷>Zì÷~Ô„¥Üb`¯ç܆‘Ÿ¬ª¢+®¥¿÷">îcìhœçƒöµ…€@5ºøà&'ϵ^îÚ¶±õ]k»,u\—ÕºTSEe‡¹-Ã@© a©“³ï§ölôÔÞÑ CœîꦻYÄ£­ÚÈÆ=_,Üð„Å¢GŒÖBŠÒ‘(GMDª®Aúœî–Ëk½÷¢QàîÁÖ[î@H¥ iã¤ÀƒIÀ®Q¬}Ü5'ÞTø•=çc9Õq]Ë¡ºÄ7\ƒænÌvë"ßÌ… YÍÁÓë§hi¦XëN]Tȳu>å‹y½§WOiÇq^¬©K^>ùSFÆ,ëjºæè)Žó©¤è€,#´*Ë9‹4Ÿ¢Fvò•§ÂÊŠ^jH†³ÙÝ]ÅXØ@ÁŠu9ïe¾Pa|…ñ{hÕ.Í.éBÔ2åNÁsOW“ôXØ ä)ÏàS¼îe=aÖû$ž§~5B·g4¦&}Å^€X?F¼ÚΉÂysšÁžkezïÇ„o!8ll™Áó~øá'sÆÊÚfHý6ˆs {6F5™ äsážT@BAŒ§¢/ ¿ó+´ÉE` 4ó–›&´Û*0TŠ¥ÌŽ83«ÜŽz%·öæÀ.f㵇UÍAêÓ¥ñGEŒ þ(‘‘j° PPÆäî¹A1$2ÐRA]÷” írjCLlƒ©Ó¥ÝâÃqVÀûË·–’ÀS2]S¨ßZÕxŸg­‡³|*$Ÿ6b„mgͳťä¥Éë0†)³ÔÛ¤èµr;‡´ø‚$;LCúuybNÈ [œ5øY*þ•Ž_y?¨ž¨ŸþDkˆLP„¾Òü²rEÈ.+в­šˆ›@ͪ=Že-m=ðìËÊG<÷É»k6„ES㈢ÍWùDóî«çÓhÄÐâÔ†ž¶)w)ñïSÚ¥„â^Úx;Ií¸¥U«ŒP¸—+ {£ÃŠ"DIî-ú~;E@ê1´€›@ÂáŸc;çÐy|VÝòTЉ›t|Ã+V*/ÉE§‹iˆMM/ךEE!Z*.ZÊáÑdèøë´ÍÕÂ$6a=$È-¶™¹h§„Œ zEâ›F‰*0&ÛÍŸ·Tes)[?›©j‹ P™U¾ÂHÐGUqIÕ!êcÕ"ÈFÕÕ¿ZP'æØ‘ƒŒ¤(<ÒVi‘\P@¢8ú쉤S¯‘åhÔDGjeo[ñ#ÚÙš=~úRsú0ÖÌ_]Ðó¼H”É´ klbm)Ça×"Ô8mMa£f î2›ðÕ7y…糪 Yޏ'í°éî­ÐÉŠ’ZN¡›ž° [€>S=ž)$½ÆDU¸ƒÔö±Íj‡àÐF¹‡=ˆz Û¯ˆÁì ª¶Éd'¼©tYŽ_i‚æ1YÕØ›æ¡+åÛëÊJaWl³1ή†ƒîEµç%|GÁÃéJõH¥ £uJ„|¡ï¨Ê]Æs ³&ÂëU,)o¸ ¤‚t•û9ÂôÎ!5 €¾MëØ_BúR[škMiÝ7Ë%yuªšâA)SÛ4W{ªëìrµÞ’Õ·ì(½¬^Ñãš­J.»s¨j™MàÙÁÝ+a†è’×ÍÉU€÷( ÓÅ£à=9¼æjôùhÃ…D”|÷½œ+i"‹ÁMÏ U–'ùêSõÄ×X¨G>Ó©"°pðKq–Q‹ÙÃ}'¾ÃÏ5c¨Ô(ÔÉG%ºóuæ9M®g¤Ž_‡†ÅKYÞqd”;CôÓ¯Q7yE kõ)tö «±†éì½—è§^øOÛ5zâ°*GÄîàC„œä·š/âgcEþÄY¤þû̯¶ì_7Ôp …KµXòmV~mxENxa^uÂÞ xޝdYTø†@EA²3¾+z¸Mvë­ì:ä¢rö¥ÇBÎó œcΩ™-õq iãª!.2RÚv6˜_‰û4D¾°Ä?- _ž ² ¾µÃÀè+@:sÓ•6MR+ÞØ*§œ_ð-j0Gâ¯F¶×ðm§¨fŽÒrŽRmŽ\xM2ÐáÞ Ýê‰ÌLš$Ëý[kW wS€<•e tåBF#m Õ.jÞ ]÷uæó"01=dÊvÇß; ¯@® 4ÛÇ~®?×­3xûê dÜ#¹2*.q§rû³øLW^Øã{Y(/d€g½y;KnVoß¡p€ ³vï¹µ{M3/IŠáú`P|#©å2ÈK(—4,‰61JàfvÜÝ­ÛœñGüíiüSgŸh7ÁÆ…FbûǹhEåWwª;íÄ)R‘F÷&VšónMƒžÉ}çñdŽÖ˜àëÕŽì ÿÁû•@/Ç›çÏü⸞µåeÊò&l£:ØRd Úür•^».¾ð£ëÎñYÀ8y¾KÜÐnɈïŇuxÒ%wðÅiŽØñ3×™B4Ðö›ÈôÚ)•šõ'{êFzÐÆJýÑHnºÇŽ’pSN T’w¹ùdžzKëú&vß‚¤¦sZ$¸–£¨Ã¾‹¦š§Âí¹|džòÈU½÷@ø‰Í<÷JáÃÕ›#ðÒ~vHÚöV¤5½ºÉ@Ò¼rÉ„#½—žou­W65EÎùHìkꙉѶ¥n‡ÍU¤Ú+ Œ(›Šˆ-=PËaÚ_én)$=XÔæ°¨[ÜR-2w!K+±ùl¿èh®SŽrèèo³ì0³OúVâ‡YbÄû Š.×¾ oXnÖ×’oË}Iv­‘vk#nž­G‘ŠI’]‡#L>2õS¯ÃvQ¦D#LÖSbL‰ô”Sb=%Å”DOéaJª¥\o0¥×.åiø–ð‰e6±]Bû™‚C®oØÝM»è”ÁVüç­=b¿¾÷’tZWÈ‘øõ jT5sC¸óQàìvþHýÎõŽç·í¹š¥—Ð1oÌ—Já2YM€¢"ÝõJ›´Ð…[_Xye©Ù¼ê -Ù¡kô`iÚAÛ]RÏ4Ÿ* ZÉF¬ÿœ`‘LõBÛP¯}.|î[ÀÝç¼q$|UZ|Ö¦}ºÙY‚¯Ä ŸVV’Ö=®›î|6ÙIò+=UÔ…[ÒOWš‰ƒ&G@2S‡Mq¥‘MqÓÛã»D¤êœºîïá.ˆ»øèQÁ‡å‘CUôèÛòÖ*ór€/‡סÿGþ…ðà"ñ/Rÿ¢ç_CyŸ23Yp€ +2Ð ‘A‡7xØlL GÂ…3Ë^íã)ú(Ü«[ú†³½­pŸZ¿½`g ]†ë¿/ø;¢ï˜¾úNé»×P7t–» 8§¬N7i’]yì=Œý;â†nÚ•ëCÛ dÎØ™8´1ûËË[ìKä ”Ýo_Bˆ•ùFíâíÛ)Ò¸ ™{Ñɪɴ|2]A“ Xi“ØŸ$þ$¥P#±Ÿ%~¿{~Ößsï¬7ŽBã¿È'ð7Á¿±?Náw ¿Süì­¢Y{µY{ üKýqÒúð܇ç>>÷öU¿Çþ4ñ§©?íùÓ¾œ  !x%5)Ú^9á—+¡bv(²|Ðí—×Jâ•ö!E™ÆûÒC-5R©’KÖEü–öj%þ³T*ïûPx ¬÷ˆêpFÝ[ŒµŽh-(ËI0–Ù…E'”šZŽ^ì壀p‹¤‡Xb(btñ®•7U;¸”ryA´d%”&.‹öʾë7mª;:ãå©®{æ)W;Z¤0©ØçžVGÝ–Pµî¾pDe2Ãtç«õt”z:žÏ•R‡W‰â4جŠç®ä+XÉ>`Þ¶V nO4åò–IÇMÀ‡b’ëÆLã–hqÍõÞ“¸eQçÕÏvïƒAŸÙÇ£r¬+b£°±.T#çìk9Wí°‹ä[së>äíÞPò˜±ä+{ÔÌ`O+óQC¬a¼~¶¤ †fbF°dè•~=´)¥a±; ¿½ëP]y?om]hWÙW„]ŠV +޵Šc«âÐe®¹ Ô¹uiW¯›Ä¢G,0l$ÑI*$ª‘D4¿âÖE\Û\"šKÜÍ¥Zsi¥¹T5—ªæRÑüJZ UÔ5œŠ†SýЯÖrOk¹‡‘„AâÐÛî©¶{ªížj»'Ú†_i ¤š²2ºfµO—s^Aˆu¢¯õ¨_E_õ§¯zÑW½è‹^À¯^ ä+èEpÚÙø¦´Ze]1œQ=íâÐb” 6ÙFîf¼›bPrŒ7ùˆE(+’¶tæ\´ÏA«j5¦r¼S9Ü©íT ¶ž€·§ íi_,„†#|áÄڅÉh1@Õ'¢‘Ée²C™ìP&¡ŸÉf3Ù¬ŠåT²Š^)©×ÇËs­†‚ð´-6„vXöš?› bï9ÅdÈ)þµº3ƒc`ãpq¡°hþµ4ª“Í…hÍ ¯Ø\TÓœbo“ÑÁ& [/d¤(fðã¤(….W .]8¢ -$Š‹^ܤïËü#*s‚d«…òâ“JÔª¸ÀP‘VJ—o‘ïb¬¡&KÔ™°Íš<±ÌÓ™ 9‡jìˆ"I¬©„üUõ4­È;¤ïRšIÙ*Ó ›Žˆ$ªP'D$F}¶…M©C˹+Kâ%v@ðèBÄ“Jxõs*3š”ÙŠ"Y™”Õ*@3WTÒ=ÉCª¤çZGžÞ“Q5ôzjbQä=ƒA¼Éè¢jv¼QºnÈ«Fòܤ… *¹±Ó¨>ûBg–Æ™œ¼Ø}E¦NN#­J§÷ne<ÊCˆ÷d8уa‚N¡€ˆUt0TQYÈ´O(i/ºó,a£Œ¯ã¹C b¹Æ|@¦"Ù§Ú Òà­AéÁo¬Ø7FWe‡•)çÀÝïT¼åè0O*Áa GÕÂ'4žÈŒs„‘qÇ|ñ1_öïfZ¡Ym}Þ9½9žŽJñ®>\”zoè:që…ºÏMŒ¦˜(µz¾æg‰—·±å¶$„ÁKB5¶#öþµ †²¿ð¯M…ÄKó×™1ö—¬í@x°¶£T#ý5CRu¡d™PµM“Âív¾³É*8ZÑÊŒVogËs¹¾J£Í¦¹ *2”w™­‘nwÂ3finÌHÉÄ¡mŠ&@‰¬†å”l‚³Ð0›Šv…Š®6 ÒEñêX±uå¡ìG¶Ž ô=Ó[` d–­àW»Ñ 0Ü šm¯áz·D8öH?¾=}–©ãüjžÌ%dÍ«pÔ)ÌDo š·ZÈ0€!Ël”´Ç?©Þ¦ Te«a4’Æ8Êá Äœ©5D¨®W¬ ó°ŒÜ¢e‹žJ÷\ÿÃQƒqþ„ªÈÐÖé Ž¡¹g ß#¬…Ð\È‘ì%!+©øHUÙ1ߊ7y›W>„“Èíâ­ñ…û…q;9ŠÐí$”¥£_¯¼¦xa(ÁO_`ƒ© ±ñžÝdšƒ‡Ws@‰êœk­ ë~uL».lµ‘ÐÆln`{ÊÞbw,ª®%žZ¨¬íœZ¢ÕˆœhÝÍZP'9¹ÕšËY—ÜvÃáQbÏêµµ;i/*àËt3“Á’E‘-F¼épnžÈŸÍô¬ì™Ö¡ßŠLåmÌ.‰¤RÍ6e£ bÚ– !§D--­U¥2”)n1)᤽ÑBù·Bç_ÕÏðE=t—}!@õþ; °4îÙ°Àœ£Å´IdÉ›Äð— <ÊÇt4@‰~XîêÂôÀTŽbÌ“h[¿0·£a%1 GIB©©ž:i“C#9Å‘Ýv ‹f÷)½—öBýT¥q<‰ÂA_zI’ \Å(R…ýHoªކAˆµœÄao ¿–â4¥7±ž’^Ÿ;F‰Þ^2Ja¿§'§0ô0 ð…ƒ^ª¿ëX¹A¢'Ç£4 1†a¨·ÞOFaDÂa¤§§8@ÇI¯oÖÕ…¤°é©Y8êSbÚïúÙÊxÔáa¯7Ôf*KDhWÏí†}˜†¼Ó_öF‘€“^SDÑ©k”LcÊGxÉ|¼¼DR„ëGØ›†P:ê‡Ð4è3$Âhƒ(‰5ˆLãÑ ŸÀÿýaŸ!Gð# ƒþPC€) Fô†A¿EuƒA'ý ¯×–Žú.ã("ÛpÓ {F³=¨/IãAÚ”oE½4$ð§ýQo8ö‡´BN¢a<’H›æ v\–ñ??üòúÓ#…ÿÜÿ3ÄÐ~aÿ3Ž0þgü=þç—ù0zèG|VÓñFìUäÖ1ŸZ&ylÐóñÄós¼k|¹ ï~Og ›ãWm¹åxåðzò傾wø]›ý^ÁF¿dQ¾ ”7#˜ãxq–®ÇùDÞçŽ6z0ÂèòúÇ5#ŠæÌÃ/Ò¤5^· Ó?¹Ðnà‘Cãf¢.~-#…WN«º'ÝYWÞÄ»Á‹­ûëíEÞ¿‡ u† $Sö¸Ú¡‡B‚Ó¯WP†Í»Òs íôZöŽBµ2V¨VÅmƒzaâ;ŠÊ¡ÀéB_nˆH¤˜ØS퉿Z”ÐÕd»Â+R¢Åí•7Y~“1BØÁ—‰âº¥eXËè‹…¥ Ÿ{tj+ްŒ¹š³ü¡oœ8 ì]À&ºï¹€”ãÀSÂ"}ÊÇn»*VËÕnSÓ{ª!T5`K®¾"ÀŽQ‰ã~×»FÌ<60eçÊ¡5Ù †›aåEÓeŸæŽ>Yûë•kŒ=¯¼ñL[Ý|¢\Ô±]µñ"TWø9ŸekÐb峦/oÆÛ‡:ö–.ò‘>­VS–0ã«ÞŬÃTËÕz;.ôð?íòÉÅÂk“‚klèJârè|ݤخe°Æ6ÜùNO»?÷{ÜÏïq?Ýq?QΔ·ÍñÕTÂpç ($®¥VnsoJÂ΋pá•å²Ä…c Z6ZÔ9 ? Bâ•ýÐëQøWæÓµ`?eØ&ŒÉ‹åçuÂtÇUïóâÕª¸ChP¨Ip©éìÚ_à×]™”mPµ¡D!CèßÂû Y¨°@«ÅjYtÊ»öÔÅ/¾;®§Œ):U·ÀŽ~E§!Pw)¯¯Cp¬Nó1E=ÄÉ[mȃÇ=žÓŒö;Tô%^Á:ª™ j†³£f—$÷¡–ûç¸ÿ/˜áº:Q×èª^-ŽϯŒ c™xŸÁ阢<‹šðŠ{F$:_3Ëùø %ÊÎË·7 ¬sÞ&ͱ¢F¤5¢`G-1}ÞôÑ×I3´Öž@RÞtSwALk†¼¹f>iä,Žwòõ5öª~jÇ´¬¡ª¼´ŸWQÄ5·–n Bd7HÄDo¶ëÝd»[gGÂ…jfWÆŒñ %®ó+x Ë,W´=õj‰¥«èpÞÙx·Ùt²E¾ Œâ"¸k ~Q‡~€ñXsÉr^b° #n›êomŸDÛõ`(sRT/‰À›½¬Ëw&ã4 %œù`öêzXßG«VçÆ§+Aè+¨E§[’í(N  6%¦¸ìötVJØ_…Ù^Ý…ÙB¯G!Çå#½Â%nï ¸­ó]¬*@q‘u'0Wž‰Ûƒ0`hQª‰WwׯêÅ«F-Ç•äT›öÃät:;™^ÕÑR‚ßsÎó„ª?šVh]©¡¢­Ä64PðDÍDÉ¡&ÑNÉs;†q¬Î¼e6.@ÄØxªhǬFñ)”܈¥Î!Ë庺|j½zÑ4Ÿ{elR~na¾Àr ÍÖ‰C8æQ ÐZäE¢¿Ùì–ìÉ|ʘ£²Ÿo]’­(† ãHŠHv¸¤ Íû­WãxÙ ‚ukÞ+ù·mI(…J4¡”f³œŽ}Â×x£3hàÍõ«Œú#$KþáÎZÊ‘eRäÃ…Ü)’ aØR*äîi`¹0œ¶+ juÔŠÐä KÚ?ËùõûÓŽ¡J„ªÏ)Õ‰“.Еc¸F±.ïÒÍý‹õįî­ýTüÞJl”‘âkŠuÁ"=5íÒè²1½ÐŸ^?•:‹uǘ>å•üÞS!­3a¼1Ìönƒçi76•u9×ÀSñSIa °(µÊrÎbÒ§¸cö‚B…•q»ªÁüI0{ Ùß` "ï‡ÀçMyáѱ‘~ý{­UúƒØ Ó⡳ƒÌsCþÔ?ba ÔœŒ) R­+¬zÅQŽ—þFÙÿI²¡r\ÞÎÉf]£Êsõ˜ýã?zžØŠv¿Qëws~øáGIV@µ³õ§&\ûÎQ°#5b'ï4xb“©R €n¾Ú-¦À8•.Y”sBsE:÷H1šQ'Ë…znÐ-S"ÙS.´Ë©S.2\©(L7n‹™ct˜ˆ(Æ·¡–»*OÉ#ã©“Øò’o¬ÂØ$ž[“[4¤x!£œdÎmÑS`àQ0q‹l¼©ÄWµ>E«üºL°½ÈÂv˜4ÛQàݬ΀˶ šÊ¦â5H¼’–ž Ky©„´‚–uüuIy®«†q¹Y~jZ"Y·Ò[÷©EXž‘…ÆZ7DÌB¥Hï)D"ËDu£¬©: íþÝ~ Ž¨eNAÝ 8ÕO^¦´óÊ6$B™ŠÙ]ß(pj͆ŠÇDD¨^ƒg©)”0àŽIתrÜ)ŸoêÍ ´¨G^Ô*Z¬øŠªA–´|Ÿõ°9Z†?ì_—%(×.»FÑY†ÍVC®Å} QB›Ùœ÷̑ޠç!A*©2QÐV‡ùÖ!"“v£l¥ã ™³©ú‰ißSIß$A× ‰(Ö{ˆc^Kå» L=15²“ZƒõÌ­^j~!€1c¥ì®æˆƒ¾le€6ŽºÐ,×…SÕ’Ë宑žØÙi¶}‰Ñv)@w•Hm©¡×"šè|昬qBú,9Ö¦Ë?wwî³ý”vs9Ï7[ªÛ"ˆ$F–RPÒ ~ÇùªÏY3L,!.ÄÚS§»‚µ¯åø"CEƒ¦Û®èe¶X°3„ôW‘z:E§E&_xN*äyq†4j£&ª ¥£™7[¯–¤:ð“¢J.]šËÇd¾5rcÆŽÞ<7Ctãa,éÒ¸u£œY½”af+­ ²àŽƒÊ8è™ÒŠ㫘½Ù!FMHy¦ÃnWE:zÒó’}®V« ¦ˆr’F˜[…#/›5ˆ@2ò¤««&öŒ¨÷] ,p ÇéxR‰÷‚02CÉÃch16Á¨EÚ‚’”UÌhªÛ±ÝjÚ•±ÑHø¥â%3 …ãÙf³ªrr«"XnŠë¾ºÂñ"êÌEÀ{r“6:qä_û ÃË3`¼ð·½D$äèöxsb_†¥€&r B°„Å6¿\n.#Tâ€nÐQ ›;C{ríÌÖÛ|YòAA 1lЃkŒ¯5Í:öUŒµM˜!L‡ÖöòÀ‚&£Ub-}MŒÀXÔ ÷Rp–Ù‡‹Ú ­]Ê6½ÒI# »ž’d–xJ׬ùÆ ­@¤ˆFšÚ«$q„'~¢”ÚÜÖWEé>¡žY–¤˜JÀƲ ûÕ›ebDXï£â:$¹"{i]·4Ç£Pˆñ糈F’mÕØ¡¥«®_-¦øP£0Yꌧ$G<&­†4¢Ó½úxÓ¦è´á5/´HxUÝ ’ÍÒ¤ɇQ{K§qäV›‰:-W¾ÐeÈ3¥L™Õ5 ø¼a§mÕ‰ðhijÂ÷"~(CS¨Ž `±2)Œ”º€.ZSÆœi®Œ«ë C ã™Ö=ºd}w¹*D?kNàõ’|;GéIUVâÞéãB‘,ÔÕÒP›PB„'kˆ[²Qå)+q¾$öMc-¶{ZïDxëmBt“'m&àÍûÊÄžb¾gåwy]éž’‰§±w†&ࢠ;;è‡$‚¦!©(ĦÒB îêÊÅ™šÆIµÇ\g›,ËUä@ºº”€·øO›ÓDpMÂã³ì*lô'Ëq«“œÈ®Ñ¶´p ÞçÅuSo±×alŒË[æÐH¢çï7eÔ=¿úFþæ§zõüêú`¾Ïµ1Ý‘¦#ÎæumÖn]7¨³\e,´\ziÃXïò=,/viFûxan%E¥{‹^Säø”†;«õËñzÚ9O.ð«Õùv'ã³Ý™®"¢ì î¶'JœüØ!ïRXmw½¥ÕŽÒG°äسÂL("ºy/œŒç7Þm‹%z[^.&[ÿe'Ÿ"¨úH¶å•Z„?_3G[0¿ý)Ycí8PœËÒ•îò>~C=ò…Öê‘¢K©':^ `&µ|x`L^ËÁ!iÅÍ×Z$X¢¾¾¡É•XËËÙ·˜`Ð!…Çוm›ž°¶[A%l à转@¿ªo8ÄÖ¾ Ž/bîÑ5Õ/×+òÙ!~#¯Ïüj•å˜o¶¾C¦MϫܳCð!«§ ˆÈ Ì«î“2a@¯dY|a€WÈÒÃYÖMöÄŒœ®¬§Ïüj w ®Ï«žç·!S$·Ðê/–Þvß»®d×» 6Š*VAu¼UEª22%)]E® ÈEÊh´¡¸Í€zzT¦Ûð^xš«¨ü¶á cÞ™· ÕÏYiúÐC˜º%™aꧬ Ö§p’PÊp+1ÝÌ®‘,áÞ6gü1ä«pÔêKºé^†$Ì9‰"‡kaó+ºâ5:’H;Ó£Õ"¸¾Éöö€üŸåÛ/Û}æ?Üú·µK HQè*MV@ ¢÷æš´[ç8QؘÞ¡¬š‹ë:‡õŽôK‘Œ®Ñ[:ï-j©Ü²§µ‘ìiCD@íi77WZŠ´–’š–<Œ2Î@Uˆ2X—%8î ­__<&?iJ4‘ñ¿“[ ‹& ‘bMïŽÒ°µA ýœáçÖÇ¡@œÀ£×¢}‰ì o˜qxs2‹’hÝ^ˆj¢ ±ôþ «ãMæÙ䂌gê=‰ä«îEW’uÍ·Ö0E åËéy°<Òu™bš "Z³'¯ÎÄacü*³#J“|f•r×°yÑíte‡2FWG7‚ú14€çûÏšû‡R;{<2aº²gô~ʆ4‚œH€¶ã­1w®œð«¼ÐÃWzˆ/÷’PÕÕï?¤8LjÁOá®iê † BÛ6†Hɤ6ðv Ò¤bêÃq†m½zYª‰J~Œ:ìå‚E©Mý—ŽˆWžÆp¨]‹Ê.¦Ÿ>ýe&^Q6·øÉ»K­Õc&q3VR¦%h Ü0@—ˆÎÀt®bÞ=bsÒÆòÅ´âÖgåÀj>îUâåç«S+À–û:j‘վͺºªe¹›”Ê Ó*©P1<õãx|ï)a§0ˆQ|#ðv‚cZ¢ä¹“)8‡Éß­ú ïF£®˜DåÔ‘áXCªNg´w*(KXÛ¼þ@slƒÐ§íæ6Á–ö×€,]³ÿFùîˆ5 ܞܥ å8»ÆHV‘Ò²é5h#”Ú.´Ä½Ñ’Æ!¥I ¶à #-¦´ÈHK(-6ÒRJKŒ´¥¥ZÚìÊ£´ž–ªÕßhIH èE;i•©8…ô®]´tr“g è}–F‹@\¢þvµ%ñºêKªµdUäàue¢¦¬Sj` ~‹φ"qt'/6;©D¨¦¥œã£Ëk—]2Qo¦ZËzŽÐ¿Q< Qt÷LvÍ4ñ$¿Ô°D2¿ÄQÖŒ0?jˆ]ct¹>¼üÎãý–¯fÀ gè2³š€ÄˆŽâ–6Qøj)ÞÐxU^¹ÜÈæ¹ï×,ºìoWÆqP§b`x*ÂPÖõjYí Zá`î[÷O¢µn™”N0Hû¾IûBIýÞHÇ!++œhÄñ‘6ùJ³óQ2÷]vIÔ|ZIZE€óL¤"Å™š4åí·ú d£©U¢R#-G¬ýN´ß©ö»ç¨ev¥2 Ÿ‘¿3õ )ª0?!R;ªR¢§’½ ò2Nš4}²rú2’0Y@}4oúhÖ¬ÆT7ÓL=¦ÐÈ“ÜàZä#µ!‚›Éƒ¦'h&!ºd$FV¼Z˜&}¹Lªë¸}›Q\ÏÈ¡tÈþéá0j.­Z×Ýê÷[±n·„›‘k9¨Õàã*ðûáŸØÿ"øÿþ¥ð¯‡7^̰xæÏèʅ˲":üUŽñ>ø·¾ÚJWÂáA>ñ^3‘ï˜ä>êG”·ä3cº©õ^Ò]Àí­¡ûÔ*–ÉùÁŒÓ=`ÑoÊûFôÀ%ZÀÍ."NŽùOÂRþÓS—™øÖ¶QYbLƒÑ²e D9TS‡Œ|ô|(Œ ~I‘(!ä— ,/¥Q±QÅ;—î•€nú~.:Y॒'ËE‹þÒ:==õ—xb¿g3xÆ£D*€&œÐáÁ9.Ù¢8¼Ç%Íæ›±Ü²Ü§—{ʺ\£¢ü"%BE<ŠÕÓb=¡xYJU2>†îÕEbÄ :¥-&gM÷$yî È™ ð :Ö/¡µöîŒC†’Æd•Ѷ‡(2‰ñ_äO È$Á¿±?"(2IñwâOz¡Q¬¯z𪇯Rÿl¼\Ž}9cPD" …Vja:¼ˆàMÙ¢2a¼ Ì i1¤Å©G׊Ô!cƒEì}p,Ò»ð«JuŽÃ¬qÐ54õúlW!Ù€3#rƒµqΥǖãpqU³˜@q^ÖÖ$¶V¨`Õpˆœn†4–X \ ƒˆRTÍøÚÔCZ®• wÓЈ^HIQ5)®&%F’s/îRºâvÈ pãYøÏK~dšM°³LM* 66éi;HÛYi§{,Oðö´Ö^³ä-2x—Ùof3ù¦C%]*»#n¹ÌÆ^ „î!™3âÆïu®ß•@Ù+·Âï …îŒª€×ú¤Âš¯.·ú@ì€éõñ´°¤‚/¶Ð¸jÏ1Ð#mHtÈîŋw%hûÈ^5æî™'¤S3=À¬ö\Óx”;†Ýï.½«*¹là—ÂhòÅ(ô•A¨ NbÈóÄKæ¦LÀ‚ÁiYšÆl¯^®£zfÂ0ŠÑ"½U×*®;¢™,3æîÎtå-—x ]8’§À@Ù@yvØžÛF+s˜Ø9³BLeYÛD9«D<Ž·ÔÈ9#Ëe[uÕ“¿aï|lØ‘Á“4³ˆsÚl ¾hvPp_4›'A· ¤îjÓ„q†Œ.L ¦É]`š–0MK˜¦r\ð3i¡¡ýðÞ ¶h®'Ц8U[ø[yúµÁœ"˜qÓ‰`6wMÊpzêp¡µSrx^øo÷5ò2ÊøÁö-ð= À=mzi‹›v'ž‡´Yê}홡}@2èœ:ç{pVOK¯vZèP~µ¥õº4#¥ö›£Ö½ÑaZEËNÉy5Õl9·m“_´‹"jަЇ¶§‘šöi¤Vü42W¼m… &†ØÄÐHÉw¦qÙH\6j$®6rkUñùxa¶ÙÊ{=<²Eç°è»Ú3'Õ†JÝVɹ„|#-”quUîwö¼ÍÅHøF·…C4©‰*ò Cu}±âJ'+ÊEEù±Yþ’8"ø×nðr8Ù\4[­Èµ&0'©·ðã¤hJ¢R;ðȼxóR¹yÝ臦ÿaÔ“FbÔ½°Ëä_€¶1ò6ÀÚ¡A7JA-cˉ”èÑko~2ON=n„²Ï,9YÃpðjZï?ŽÂœI(LÛZ¥œ‡FƼïCœÕ–¯8(7+LÔÀÝaU„.ç¸ì ïêR×]“Oû ÔµpûÖœ ø¨1‡Œ'óf GS{ Å„qMÙêôð‹ª-K›L™ÇÎRN_Åa8ðBN蟱 Dȇ‰lR ¦ÈG<©Gk7]½Öè 5òä>•Vòj¼ÙZãõÛ\u{‚²EÛEÚ6·êþ˜c‡ìþ{dÞÞm²ƒe^újW}àEÅv/¡o/µ(É,ÂÛ½f6µgLît¼y¦¥[Äí’!)RÏÜiIÊJ#—\ÊêL»£nËgÚ±íhP_£xÜRžæ?:a ®é$WYä½¥×þ »Ýômì`Q 6–ª9*÷Þç°ÜS-”|udŽa‰žqgȯ4»Ê·O°bÍ8Çy¥—ë°?lâM V2‚Ù{¹ƒïåÙ=_³µ_ ¡úÌ»ša¿§ÆËS_Û@ÞE€yæÏæâJsƒ»uÅ×ý|vÚ!g»g|X›.9 ›4žyFó‘Ù<ƒ¡Ëœ.UU—}`׆û»öÄb¾N¼ñŒëÁ È% /Kneá ›4^X(1Sù-ªK6ÔMíWåMíákGWU•­Tn•µn¾çë‹Â¸ˆŒÓ(pžŽ–Yºîuñê.íîxQ´öùý÷Å_uï7v1wð½+ƒ8ð§™’TA,¸ê`Zódî©8“#¡{´agÓloZ ©4Û”T&´"Sæ‡ÌêU\ ×YÑ Ý÷õîZwòϺ‹wÖѾY·E"³Ð|¼ ¡ üJpp oăžæ~4A¤(œ%§š ?R/Ö2fÛè”â°o&ǔ܋S39Áä8‰Z½Óp䡟i[Ý u‚n$qmŽëƒ8­Í‘`q/ÒÞGñ´( (A•¤IbURf‰)Ë0éõúƒ4ƒ0D=½@(NÚ°7 q4HŒ, ÕÇa2ˆÒpÐzPYh4”p-ƒa/Œ’ L‡ý( ¬,4æápÐKÂa‡ýþ¦Ao(åEüŸ¤Á0ŠzÙ¨%¥†zV‘$ƒ0 zÑÐÌÂÀÒ¸ â8ÆQ0À†Êk R‚noЇÒAÜ ƒt0L øã®dI{½Á º¸OY"œæa§=hlÐOÍ,„ bÖpŒ‡ÐñÔh( [úq ¡#aÆ}+ Õ2L‡IÚ‹` âÔl K %ý ô`¦˜Šž>è C·Ÿ {I“{ý^Ò7jIæFIoè&½zeÖB Å0Ã!ào4áojfIhBÿþðCW#KúCS}Y¦Qdô¥G}éas³ÐK .3 ÷ŠÆ%€·ý$î‡fCÜ—^ã …!E0r.½”@?Q<Ä äÔ":X·êAÐ)>#pF½Ö6Ô”¤£5 •4¬dÐÓA¿ÈöŒYJJÂ9tÉLaq&}³–„3Œú}€sºnÖ’r_"ħ~0d6@ÀV#H=€^le¡îð¹#_¤@ö-š/˜3t3…I€îö€l¹ØBê‚fŸ‹ S]‚lá ¨˜“7ûÛÛ7ooÞþäýíÃG¡Ñÿãíçß¼¹ùè%”üîíû›×½¿¿þøúçïÞ¾ñþþñÃÏïn~ÿuœÝ|üÅÛ|¾,?¿~ó( °¾P%‰Ë)÷xÎ'ü_Àƒâî/»I§Œü_’ãˆßÀfÞÄ-ø¿ñ&>éAeªì$‚ŸÍ“=˜nX÷æAV[5Aü¬ÐÕ ƒK9T3‡àA±ÙŠ‹•-$@v`’MA^PÉa JJ« µÂšw¨&@·@ðë¡nƒ˜oÖÂT2 ¢ð l*F¿þŽ©štH`…Ž @"©´o.õ± ’ k%)HЈõ€ú¶j‚‹¯XƒÂ2xÓVM:,i‚oXçCS`ª ,q/  ÀÊ Æ+xPøÿoïI{[G’›Ïó+È%J)Ù~ö'¡%Ú¢ÊKI>äà Ùd$` ??uô}H²ßÛM˜€-‰ìnVWWWWWUWv5óâ) rÌ™«üfr û‚Ipkë1LQœ^°É­å{6â,t¥gØöÁê`³ã©XxmX „ë¹³5ÁVn¯`Œa=p@\^á'À¸€×Nqsâ®A¼ÛEñ aáÖÄt#pƒëÊçEÌq`ˆÆc\«`¬ÀÖ˜Q[ èùÄÞ] X€€;Þ`·AJ´‹0,×°ò¢øŽËÝõõ]„aÅòëøD}Äñ­½“â5°hUÿy³ðÏΦf=Ò. 3l»®ì5WÚ‘QÁŠ“hüÚÙQLx?½œLÆ$îÎ5[b`Ò9RÃ-à7´éûßà/¹Ø‘¡h¨™}{c·"V— HÞ —ŽQ{&»÷ë¤yØ´Á‚»†[gcüÂÆW;ØpÁT(,r«KŽ{'Xæq]†±9Ù<5ÝŸ×ÿÇë×_þý¿ûÃå÷çÿøÝþõ/ò¤tÖé#ë6Ö@X7?äÀEAØ)€µÿ0Îa›rýC2þ‹@ã\þãŸ@äI~Øÿú_ÿùËãåN=3Z}þ?¹x› cëú~q%;¸DÜ$윈–ÃpçŠÂt‹ÇCÞYs†÷¼è†°"ZˆÂçP\ g’qÌ0jÏ›ùw±Jò“ct¼d·o(W¢•¬Z?Ö÷]Õ¨ÔÚ"?F§€çcŠ»íæM[uove1°öö¥|èÁ°©ì[2I†•ÝwÕÓvM…‡ðÊm×쌩‡æ2Î ü£ÌÙÖU·~ÓÆ $ŽNe«#‹™AXØÄtŠ/Ø4Õ Ñª4Ñíî’z”,ª¦£àöóäq”¼Tm[wnDøCÛ<×Ý#Íë䱯$Ř"äi?›  FóÛo~ΠÙb˜y.j%Å4£œsÿPw늬”vñz¸©šõ|ùÃïÿø§Ñ‚8äßÿç/úuôçßÿû¿ýnôÏ¿ÆâÕã.é×_þð/£—_~ÿûS•šv¶>ìQXN÷oZªQ²ãl—äÓ,PuøŒ£ö˜Àî3ÏdÌèzNüñæµzŸø$ªaïÐûŠ(Oƾ2_@>_³m×É\PiyŽ˜çTøVgùp³ÅÅr¨fÂam„•ažåªy‘¢‚+b`˜ž eŸR/4¯fä‰èôšTé{˜D²€›vnu~ðÁ¦¸<;%T£H,½XÚä 뎡€˜ö³P[#Í,§Â¬×ÛÄ¥1PL~Ïc©Î)ÿÀåI~ª)Y2âç•‚>ˆ@£è©…\µ€iS¹ |14€™£¬F~ôóÜÈ"ßM0;,Ã)‚ÈõàM7VëìÝ)F¥Î¡6d:WNnÜc¤ÖtüNôÀ¤ùø#¼w÷›8^5N|Í 8Fœh– mLJö»‡çZ2¢Ê/iÆI°a‰Á£B‹_&²³Çív¢æyœmU¤§YN«Ô¶Ã¬–FÙßšÙ*˜,¥šWO{  øÓ]ï5&¹”ë6`Žíøáµ±„)™¿×k>áHÜúž–{éMªƒùrÆÝ,Âaå~‹ë)È F©éÈñŒo/E®æÖH!Í0Ðe$SEøøI4´Çlwü«<¹ PBcÀã ´ªë§]†9¿ë7ÆÜz‹rcÀOZ'.tkäINoï+äÂ9‚,uÈ@ "MHÿiVŒô¬D$õ¡+ÀÁ‰ék¦ËÙ±…oy á¡ _D3L¦ÝiÆî« ÑX¦JO¸ãŒƒè ÊÉãP*/ó߈”†¡ IrXsÈ&ª?æ»§øNÔ|¦’ Ê&(]9g?]£U(I}b­Rúœ÷üá-[ã¿.V˜j”ˆ­YX Ï`ݘ}"TYÕz»i‡:•ºŠ+‹Ì,„•C×6»;é1)ùÌgzöh ØpDÖF•+=DW0"c@™iqÔ¡×Iê”ÆšŸcöt‚ÙHêØ}µÂX¥*‡l ;'_GzI«+àmªG@‡/Í®>Ù135í‰.LÚ` nâðx\›˜4CsíÎB’JãE¹ÞZZT°pmŽŒ'õœ¥wX2sŽQHhNàk«`®ÊSûÎ#Äu#P­©«QÂgƒ(­3aŸ‡Ý¥bŠyO™çÓÆ6б‹ÁÊsÌ(h„&†Â!ù±:ìvÃzÝl@ÃQ‹àEgû½³³ëšiB™W(x£0‰w÷~.TN(sÂ6v«xwtéJDF(œÀ¢‚A¡DÓ¢œ™”Ô0Ià­pÕýV0z¯êñ<¿lÞh% TkŠ8ø>ª‹xqÁ¨ÛG•È;QU‡v3’/Œø„¸vֲߛ ÞL²¯qó<úšÞ%:&¾RõáüíD ¡P¼6¶pjãìüÜÆñW Êë,RsMùÐnàƒ¹ °óÌ•…yŠÐx–4€<Û¬Údk/dàòjÓ£Š1ž«õ?Æÿ¬ÔÉ©M#qò‘ŧڙ xÓ*ήäÊYpMììɆÀ*ú}ÅhêåÌZõÄîݬÅV>"÷;ÎÉ줣>Aå|q뙤´s–7"Õ;kHÍ%Î\ÐtÏ\ÐN÷X$u¾ãa Í®¿örF¹®EæìørVG™ðYȸ P Óä•Ä\þôàùËŸ9øÿk+B°Ÿ‡cýßržâEÕ>êL4 ëqœñü«¾ùCØüm=IñJ£†`kÁÀÓ¡%Ð/˜.@\6›”¤QåHäu0¾É?òTø':Ÿ«f]!Áî¤ Œ(³<ª–'÷â³ççŸN¸Ûõ+;{â¡òü§Ÿ~ Tgåšqb/~V/¤—=vzˆ‘&+v¡Ó÷Z„.¶‡õÖi¥2óðgJŠ=‘”<¶“—ˆ ÞY¬Ñ–zŽÔËÝzêD%Ý‘ªÅmÒDëdÝa7X$“0‹&(ë’¸8{ o"¹r­ú¾ ëýEpA๎¬Ê3Ú÷¤]¥fC­|ˆwð+x•€…†{]W»Hñjû=Îц"ðºÎ«Íàk:˜öá³ï¢ßvˆ’ ‘Ö1•Ô+ ¨ÝïA;“ô²`ÃzÂm‰¸×©m.óZ‰*Ìx3mßÓ›ì¤Gò¦ÜúG–™µA\·ñS+c“ê'›ßfäM$ü{ÐòžàH¿ƒ,Ó3ˆ"Ô×X½Äu‰’ÈŒ.²ëe¤é:wá{ÿ@Eß‚Øå¡à8WŒ>K/ 4âWäd T“EBÕ(bVÛÌþÈjÈvš<žÊØTr5Ööœ9²Ø:b¢ $¨Tl AšA1›@º³fWá%œŽ öÆÀ úí G,¢ß2“È'“ˆiMDìr“ÿ<æP"ïãT’K1sêa¶ì´ß“<ë=ìJŽmPš½L‰I^]4_ûÐh|ó€(uX¿«ŸÖÀ©IaŒ`éõpm,œ¹¦ž'ÚÌÂËÅ…\ä"jT2ó¶…ד&º Èg~ÄcK™„Øx{âÆÙp~lTL¾vÃÛýÆtêtÎ\˜ A'D|~4„ÍæÐ¾ä‰ûzÿR½mcÁVÙÐ10¡±&m«ØQhia7b‡–ûÐ9þtj÷ú´hv^M ”“þªd(tÏsÓS×Å"ÌâåŠ!›1e<´¼»ÞT«÷‘4ÒnC/õzÍÆ~é!÷ñh@‹ìæÙh$O<öŽrLŒl'³“QñFdºÖ¾WÉC·ÝÎÁ#brRFÛ’áÖ0[l·;–´ê°Þ ‡“;Iò…ãÉS bd´A(ÕíÜt Ö%8-  v›»º…™ÿH{î‘rM$«Ù #»øÆçjþ\µ7'ØÊg*§„ еNzH ¸ 5+¶iݽ,ò²Ñ ¦? -ëc¬œ‚^+Ž`®m_H'X?ù _ðV=2 ÈìÐx‰izvINÎÆÎ«'i¢_yدœÁöeÓî$ï¶|A="ÞãNnt‰¤ðqn¬çH6¼^)ÅžµTõhâôˆÛU¡j/,¸h²Œ¾‘ÓÙçüFŠ”‘M³ëìk–Y~ÓlRdÓ¯Y@¥ï´QpÑ®§g42±¹‚†n ©Û,ŸQ{j‚`ÕÍs¬îܱ›ÊS? õW©‘›Àjs?¯.\'Õß ‹Ù®Á}<3oÒ·ÓÔÉ£À‘o(]š…- ÈE¼&¥E²b « %w¥u΄ÄCßNJdsgJ÷”ì“ßË7yÒ‚¢¬ñÞ Ýw¸{[÷…<Š4¦“¶põÄt«DÑOõŒ]ï¤U`¿¨œþ ‹ÑÀ|0¥3tЇ}ÙäZåH|p»VÖûæiMBΦ@í¬Ì;tÃ×=¢ZùÞÖÀw7z* i¡š-X ­ËI£&…Q–Sx0ýêÑãˆQ(Üt…7¸à›O_Êç,yþR>öž²g¶Y¬|¢æž‰|TS†û›vŸm/¡ghÏ tnÑMz_†žh0—wÛ2»x.¼¤ ÖË©´k­ å¹c§)âkø•„€fXäž–ö3ˆZlXõLZú°ÎvõŠœê1oéØRúqØà)b›p¸áÍ2åŽðVØkG,¯=$ÉÈ0é'›MÚÂz^5ƒ¥o90—ŠCmoˆ%ø~]•ãlc(j‡¼]¾“;oèñœÃ‘±õ3Ãx h³½Þ¶{)ÎI©ƒbÍ0E6ùçà"²É¸“áFd–9Ìól¹¤HñÄ´åÍcºî>0@ì‡Ù ›Ʀh×´¨Í•‘ðXê:î‚)Œ·V»;­:ß)"¤Ÿ ¼wôQ§6r©÷—s’®y\ìå ¬·³UÀwÖà€8-™iñ€~°»&ŸÂüEz-þ¾™†ßÂYÆòIÚ„ ŒÍáæ ¼¿·1]v樉$há×¼ÖÜ&ʺ¾Ss{sbB“Öq¦‹iÕ—Q8Í)A@„§B°Î1Z4LÂ4LIçVìÔÀw¢„°Mî‰_9G¦5*f…¢42j_(3@SMúŠR¡mö™ªŠNóùµ]—¤É”T¯ëðI»é ç .¡$47Ú<ì1ÑÑ1mlð蛟‹2æq5ü^è͘—‰CRë ûøX›æ²pu5£ó¼>`e†{ gä¼åε3Ñê6ÂÎ?¨9·êZ]K®sb!FÅW+Î9j-ˆ ml¬Iès\™f¬rÅŠŸt=oóBÁ¥Ñ3Vð=&HLxpÁi…c¸†'´F&½º¡eÙJÑÇ„yý–šoDUPË “=ÐAŽëÎ,Ì›C§›å-:°ÊÅòVÝ,¯"ºsÄmA¿ÊÚƒz¦¨pPqvù^«KÈh1ŸÄõaH9ù¬&a°*é¾’ŽžfK¨­¢t3yl»—ª›ï«Ù ¿°º§Ù„ðòݘʱLÇÒkÈ‹­a‚^>Øœ˜VˆzM®stZ¯Ÿ²œŸ„W²1¦B3Èæ;¿:è°FßâùzûH>œ'Pæi¬:à#Ù¥|$GxYÒ’C±/ª]&Ý`ÎÃ_Dw¢:×¥ŒD*?$’?(åúÅ IÔONC`%–Ôñô¨øAV ŒäöÏ>³” špy²1q‰q}èIgÅÙ[ÉJ°µ’­¯V®z.з¡U|‚þ°n‹‰‰ÿ |ß’+)v¥±åKæ7©ûì$|%(5ð±ÚЃ÷gìL‚Ø!oŒ ^ ‡ +« â¤8Ž“Ù¡kÐôfbe¢±2ùXiѦCÏÄÁnAŽÁG 2þ.¥ÃEÍT F+ôsºA40Úv#þE 0ìÞŸêúTw}jt=Ðoî§™,asJbšXp· \™K–Òd¢»Á(½ öCü|ô‡ñTG®tG®ÞÝa«1—!Ë*¢í(Š•X.kư°û„ìøù¸µïìϵîÏõ9ý1»c‡e±PÁDåM^J5‹¼S”J"{ ¥Zo´m¹ L4Q˜÷ }ß­ ‘eÔ×|¿=‰‰Ÿ8v´õ͆ˆ$J­§B+žƒÖõ$"P ì±ä¸ï7»‡7\—p3à‚?ã÷Ä`[Ò{Š ²ì‘åJ%k‹‚ L”ÍX<Œñ¼žŠÄG‘Œë(s“-ɼ¶Ã¸IN23O·å ¡ˆ¡³•¬þ "žÛ+‘óv¤öþ€²Ì†rƒ_Q†4V!©°½L¸#U% cOÂ3Ô[6¤Ó¤Ø.k‚°Ñc›;Ùá{‚9ÆÛ/Êého¿(möìû$ÃÿS¿ë!” ÚõÐ&¬ Š6)mNìúÍ‘ö§B·iZJÅU¤ë¨y NðQDÕP4ð2ã×ý¯1ð¨mŒ,Ÿ‹6Ž!àöØ ©£Š½B ÿm/+ܼ¬pCð²BÔËŠ¶w¶‹þrç€Å Ë\Ç1Pé%¤b Êõ+ˆŒ<ÊÀ©¤ñмË6޾"Ê =ÇzeG_ål„ûc¯ÀÇÊY/ðŠ—U·}) ¡*mº@‚h[ÏVdxR!Hû²­F ÆcA)Ss[s¹ëô­ïõyI &z¤¥/‘iÇ@ö[‘êì‹S+¼ :ë ‰+?ש¾ño¾ øˆ}Ù—ôÛûðd³g¨=œìÿ+N}¸q¢(p,S‘Lz^ Žm»OþNîÐIAQb3¢.Iq¤°(ÑbNnzâ6©.Øy%Ïvcfà …,Â×Ê+åS ÙA9aõÑphèåœ×Äè½Î´¤äÑöóa-Q±àľ£µcßâ×8·jã†Á½-s­¦oFfpÞ²­0ÜnÂ)X)¶}ÊÄ)Í0~Bõ±Àr{_Nû^2`]tâu³«ê2˜–tƒWþÄR’©yœÃk\u’+/J:èwf–À4A·É÷By‡ó4Ïx ÜÐß2(67y7?”x;]Š'¡³8:TÄÊì‰)FÐðÈòÛN¹ÆM`ó[p ]NÓ®Câš^ÜÛ'q,qýFBJý*CÅþö0 ¸f“EÌGFƒ 0"ºq±bnÚÎÀŒQ(FÓÆÔ36Á(†>U>¼ûƇµ¬.x]Ó²ÐÆµDkx’¯'òÁféRé¥ê·EY¨m=+ñÖ@aª~x-ñ¶qçm±(ñ¾qkþ¶ zfÅy½¦šæ½—ERÒãÆù+ñ‘qoAåð‰Y9Á›ðŽÕFÝ«èÞ‹y ÍEp¯2ïÕT ÙtÛ7uSã´€ç`)ðD-0ôL܇oŠt€KÃchu©FoK|d¼ôEpê-µ*R}¦Iî–·{Ú§û¹L³‡hŽü4KnÕ8!5Ô*›—ÁùÇ…87íîðð„Š/“¿1ÔËÐ!dÄ'ŸP¥E­¾VٓàOóŸ€²:W϶&6OðKlR7Ûe‰OZ ¿ìÚ%bÝu±„ ¯‡&"¨lÎÂåÈBccâ±ù0"Ù%¨âtÆhäÛÎf‡Ý')a=™´9*›y öF»LÌ.–’梚•öÿåqH- Lx0̶eýà˜3~×(*âºëî˜dx‘­6™Á·™›ÝŒØî†d7#¯c3äâ¬eÌÚ‘I• 4™p# |fÎh%ȈCÀÿš—žÂh€V4ätÂt‡ N}}xU_%à»×0AUâEFž§~,B_ÔãJ}CñC~¯Õ7\6ýú{iiÄù£¿·Kó>¼X©Å2RévP»’¡f-c ©-Èã0®ÒŽ€æAt!n–flDÈÐJ˜¡u0#«`ÆÖÀŒ­€Zÿ2´ú­è8& «º³­Ò *÷†ßaß¼‚›ÆŠ ÝJ‚Á¾{w &ûö×TdJ·oßÂm€ÙºYŒ±a{góIÌeö“ÃWÃó7¸‘á#±;q‚Äž’F£¡lÆïœ™ õjŠú¦Þáþè¤T­àì<5'…Çøú&e­+ÓäÌÞÙü-{Ðø þêuö²Èp"epë%«2ªZgX y2XÂOç\j?—ÆUC’Ø¡âa]ŸGÖEßšª‘ñf îw¿KJ—6ØØ„!eDC¼ÃèL‰“"ê ­4z¯æ¸Íÿ&°L_J×9ã[ZMžï¥áºi¼-(>/‡”å;âÔk 4ïU~¡§Hzø UVàÆô¿¢ÿH‰=&åP}¤ÐÓ5nÔ³=T‚‚@ =úÝ.©é="¼Iüvˆ¦ x$Äž Δ©“š¨{0•°M¿2]O‹¬)`¾†½P” û1eè ‡À=gpû 8ÉB[6az¨È{:1< å…ÖsºÅ™¶¢ã vÑË5†¥¿Ü@ß6ø»'®t|ŒñÿÃüÆLbÒ³ >k)ïb6qV²À-:›éí›*XæNËt` ÆbSSRWäQÉSS÷KCÂjLC2µ*4°“+›¾øaZ-5¢/KÈÌ£Öéb m¡'T@ÅDR¿N|•©ÖŠ€yB?)u5;åå~Ú‘(Yƒ0Zjâ”·aÄY§nàÆÁ¼qUãÀƒ{¸}oÜD"Û5Ü®­ÛòöjèF¸‚Ù†0£c<¡þÀƒ|,H„SÄΰ·ž*öµ5Àtœ[EjéoðÖ$µUnD^ÓLíX¢ ÔîãçÐv: VNv8] ŠP“BŽæK:IEÓM¬ÄT•Ðk¿²ÂŽ‘l0 NöI6œ`RØ€TØ*üæ ß1 W¶_÷´Ýµõc¹kÑ>gl³OÕº·ÂÛ¸Wd°ÿÃ\,ú½áØt>î ƒÍ@ØrÒ>Ú)ùèj>NSÝ Ê€-YÚ1Ùþ‚¾‰·iŸH…ÒŒóTÏÚ>¶³ ¹R¯{(_U0¦X¿§&)ŸeÅ¡«ÏÆPyÁ·YÎŒ;¯xãÕ¸0 % ,æ0oǘgž†‡Ò¬‰+‘Ù:ïqIàí,"D#~#‰¤8d\¼’’œHFò„yѾ#fð&9ä¤Éãváya)v Jg6Ô]ÇnÅï±èL´³LÌv7 &”©ƒHY– °þ\e©0Ñëj’­Ú¨ÝÄjÛý±†Še å*‘I"‰{%ã^Žq½D B‹ñrí4Nª(åާ'­f¸M£<:]]ÍŽŽÏG½îiø»óÖAŽ|KúÆœD3&ýL•óq‹zš·@«µ]úXŸ¢Ä¸Çè· þbÂÒ"·—$fušo:]ÔÜJÁ¡‹‘×1þ³·ýY¿ `Ôn÷#&&Ñ™“¢Ì‘$ ç g"Ü¢[ǾÈsOœÃ+pL)N™4…$gœlpÖPl‹:'1{&e]Ø- ü#÷N=¼HÜz*=ã©.0§u†ÅõÖ}5^À•6&žØa_'H°Žè‰ Äcb“'‰Ñ‘kÑ?oø-û>Ï sî~AiM]odYsÄ6³v½Þ—¤uŽMÌ7’ É̆ … ½A—€äòA´W‚‚–@YÃÔ³$êY2õD 9F7º+áçÏ”íz>ÖÈÆ–$ëLžÓ"ar©’j±–—þ‡1êC å>×"a­SXÎ8GTÛ«áMOâÑ6ý¬%÷:ž¼ýè¨ÄNXä4°„uqŠþr]ˆH6±ºo«Y¹t·õL¦ù¬7ði¨ijT¤7O¬âmBHîËö¬Eo—R‘îèØa?!ôã‘DÊZ­Ît’¶,›kÇ•îÍ¡†Ð¤€iò`[Å {ÞJMB j†”’¬—{¶P<í ƒ!™*„Î2ÔJˆ2…ƒá³d›cûÑEÌœäKèù’°Ìòùµ.íÌ;~~%]›L]&Mnff$y&O$ÜÑ9û“Ô,BóöÝÄ`»/7ÅX¹‘ÌÊ•RFJ²TjŠïN‡ï¥B¿¤ËwӠߌ4j}3~úó©/F{‚–Îáu@,Ïu÷ˆŠÇd³ma»ßÍæV¤ñ#wZ±]T;6 £zù¦Èü~]£.l51?Ó´?5êäcÖU\Mz[}ÒÛ_‘Þbä†Ü!99äLo‹íS-kÂkZÊ&”¬ˆ)]]E)#íš0(–fÈì“BþPˆ1œ¹=€g1)ZÞ0”™ ¯pZ»Ku­¤×òu°0àq zÆ-Œ¥^àôƤ³wZd2eU2ÙJ-¥eÈ©ßnM—ÝçM¹ïáÀ>¬~…MPÞ¥|uê:‚7m. ˆ½ÖðqŽŸ?0_ž†Š6IâàßCfßÿë{‹ÅBë0 Üœ52ÜÎjÝ¡LC#?Ìgcc$,µÞD«àºãcÐBaént°ñU ¥2“ ÔEô^oE ßj…É6B¥P‚E‡P0¤„±õ™k9>1ób×5îEõסø:´IÔ¼Þ«ܵ¤\…{3¥î^+Ä’Ž5=1P=.8Œûm—%®'+¿#üþ÷ûÐÎ¥pœ€s¿4JÖÖÈy% ‰ïħ ¼V][BC˜„ŸðØQCéäÔ0e Z¯F ½î=æ5Æyg¸3Î|åÑÝ2³Z{B”ÜÔ%4¤”ÇÝæ"%Œ­m„ šc(¢ K)¥¡u zªìÐj#½änø/cÔúC¾ïßUw˜ö-'ËEE…Þ׊Ó_ÞÚí¦© "± 1G±{ãyäÂÛ4èÉÒ_O[Åk]# _†â ¡ÆV 9„_ Z|=ƒ/Û{åêb,ŽÕ÷l£¯ ¨gòý«YxOxƒœ¡à‰:¬mîËfÁu…@Ô©Oȵ(5éÏ´yR¹Â|ÊÒÚ×qlá… ï¯‚/\͜׼@ñ!žÝð+1°I* s,*$K£}4SÕ›1ܰ³” ñÀ0¹’ÄÑ ©f¨éÂ}‹U‡ J„`²!2‡Où²ƒAÁ¯cÏölI¥¢exðhƒtb¶\tC´´Ýa“x¢„ñc´fÇðTVk“0"ïDÈl ÆSx€$ íG+5w𡪨yX .«ž…Oðuªˆ 'Æ€˜!ZÙb«œÀÚå?:¸|ϾFQÂÉ} my‰z«Ì!2VɨSxÇS Qѳ¡ÇQ¶øÃÖULj€ÄMÇqÂ$'SÍŽ0H:±°ÉØG u]F]¬_ÀhžûŠK*m‚¨ÞLÞñÜ’­cãèåíÍmš¼f°_€ozSí{_¨,'S'÷f ûX~Éæùtt“}¡÷Š_©ùî¡órM‰pÎ?},{—’û¤K¶ð¹OšzÏ·k¸±>Õ„x¥íÄ¿î-ß«tõ–&ÒyD5g*8¾~ãˆzÃ+{LßÓnxÄÕ–K+›…8P¹Ä¾`;ј”X*Ö±ã©Qr2*™¹AeÐ %ý O.ïšõJzühBß½=ä×þQ·vá[î7cz·Û¾íʳ]:®ûumGö“.é*0"€$†¡ð(KýļÈÏr ’Ù·9à'-ÙSèfó,}ûbY.pÛ´T[°Å²!ÇñË…Üý‘°JMÓÁž , ŸêiM+nÑÔ5-h¤Ì% éþÍ­\^02 þäg.†˜X‚ÓÏêù’“žégy79Š2Ód?X,ÏóK´t,6_ô&Å…{%ó¶ç¶s{ý LÜ-“_Z#§O>ú|3‘wã*8(„&þ3ÜÛK‹¶l͉ü %<1 µ ½‹îàMÅEI(ò•s±S gPP½i†Š9âêÞ%›z¿ØÎϧ¤ é¨="R~h®n#ê±"¨ÿ˜N+c\ïG¯Q‡–û›\ cn(Á8n3œædS¾HÈÕÒÀ%Ñ ¿—Kùi:ÂŒ¿oÖ˜-„by`ÚrËoY?9â'Dµðªqï—BÙöû¾†.¢µäFÈ•¿tñ¸+”Q7³¢‚r° ¼$_"c“PÔŠXp—³IP¬Ü„qm¶²<ÕŠhþ´êc†–þËQ¯¼Sp‰Â6)o#ˆ†þ~haׯf’âñ :ÕàÉ¢#O\da^/þ÷±˜ëñÛ§–`uykñwšá„uäQ&#zzÔiP®Ä4׆ü£‹ä;&[Œ„Î{ñüíÞÆ÷{E‡°gHïÔ"Ì®âÜrÃâ9w¡9wáÃÍG:ðÙ¥pgq¯Ž H»µ)⸣ôÍCtöÁ Ptt¢`‘ëy²Š#!nꆄDQ' *ÚP…M>g‰qPýc§OšøÙ£Ž%"̽ÃÕJ[ilµÀùÂ)Q|ÔÒA Ü2+lËF,êIï™%y‹h{K=ë[˜2öÂè[îÉ<èñçPÞ'›ØQÜ¿œsh7bøH]v½"àu¾L±U>Tׯ½¦É^³+ü5ðÑ̾K´ Òóéwy‰ìÛ¦=`˜s™ˆ—à±ruQÄ RáQ‘¡¡4Ô¡7F˜ŸÑЊºÕÜÃHÞ¬1äœgMƒêEÜ^j­ÔE£oë» ú¨»YFus=~îÇpȬ‡í$ j^<­+N¾àöSBU*Od¹ 1 Z‰f ,#slšŠË9d±*t¦²#¼¶[;§@õ\5kÒÜòøožF_²¦1–·P8­Äë¥m·râ,W¢‹%½×!¢ f—r"Z/ÉMÇ®©‰Šñ©Ñ…{ù é†Ká‘øIŸå™!3§ŽŒ·©:TåÇHOü1ùñ‡Ïëóú¼>¯Ïëóú¼>¯Ïëóú¼>¯Ïëóú¼>¯Ïëó2¯ÿÿ!¨Ãodepkg-0.8.5/src/odepkg_auxiliary_functions.cc0000644000000000000000000004616012526637474017700 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ #include "config.h" #include "oct.h" #include "oct-map.h" #include "parse.h" #include "odepkg_auxiliary_functions.h" /* -*- texinfo -*- * @subsection Source file @file{odepkg_auxiliary_functions.cc} * * @deftypefn {Function} octave_value odepkg_auxiliary_getmapvalue (std::string vnam, octave_scalar_map vmap) * * Return the @code{octave_value} from the field that is identified by the string @var{vnam} of the @code{octave_scalar_map} that is given by @var{vmap}. The input arguments of this function are * * @itemize @minus * @item @var{vnam}: The name of the field whose value is returned * @item @var{vmap}: The map that is checked for the presence of the field * @end itemize * @end deftypefn */ /* -*- texinfo -*- * @deftypefn {Function} octave_idx_type odepkg_auxiliary_isvector (octave_value vval) * * Return the constant @code{true} if the value of the input argument @var{vval} is a valid numerical vector of @code{length > 1} or return the constant @code{false} otherwise. The input argument of this function is * * @itemize @minus * @item @var{vval}: The @code{octave_value} that is checked for being a valid numerical vector * @end itemize * @end deftypefn */ octave_idx_type odepkg_auxiliary_isvector (octave_value vval) { if (vval.is_numeric_type () && vval.ndims () == 2 && // ported from the is_vector.m file (vval.rows () == 1 || vval.columns () == 1)) return (true); else return (false); } /* -*- texinfo -*- * @deftypefn {Function} octave_value_list odepkg_auxiliary_evaleventfun (octave_value veve, octave_value vt, octave_value vy, octave_value_list vextarg, octave_idx_type vdeci) * * Return the values that come from the evaluation of the @code{Events} user function. The return arguments depend on the call to this function, ie. if @var{vdeci} is @code{0} then initilaization of the @code{Events} function is performed. If @var{vdeci} is @code{1} then a normal evaluation of the @code{Events} function is performed and the information from the @code{Events} evaluation is returned (cf. @file{odepkg_event_handle.m} for further details). If @var{vdeci} is @code{2} then cleanup of the @code{Events} function is performed and nothing is returned. The input arguments of this function are * @itemize @minus * @item @var{veve}: The @code{Events} function that is evaluated * @item @var{vt}: The time stamp at which the events function is called * @item @var{vy}: The solutions of the set of ODEs at time @var{vt} * @item @var{vextarg}: Extra arguments that are feed through to the @code{Events} function * @item @var{vdeci}: A decision flag that describes what evaluation should be done * @end itemize * @end deftypefn */ octave_value_list odepkg_auxiliary_evaleventfun (octave_value veve, octave_value vt, octave_value vy, octave_value_list vextarg, octave_idx_type vdeci) { // Set up the input arguments before the 'odepkg_event_handle' // function can be called from the file odepkg_event_handle.m octave_value_list varin; varin(0) = veve; varin(1) = vt; varin(2) = vy; // vy.print_with_name (octave_stdout, "vy", true); for (octave_idx_type vcnt = 0; vcnt < vextarg.length (); vcnt++) varin(vcnt+4) = vextarg(vcnt); octave_value_list varout; switch (vdeci) { case 0: varin(3) = "init"; feval ("odepkg_event_handle", varin, 0); break; case 1: varin(3) = ""; varout = feval ("odepkg_event_handle", varin, 1); break; case 2: varin(3) = "done"; feval ("odepkg_event_handle", varin, 0); break; default: break; } // varout(0).print_with_name (octave_stdout, "varout{0}", true); // varout(1).print_with_name (octave_stdout, "varout{1}", true); // varout(2).print_with_name (octave_stdout, "varout{2}", true); // varout(3).print_with_name (octave_stdout, "varout{3}", true); return (varout); } /* -*- texinfo -*- * @deftypefn {Function} octave_idx_type odepkg_auxiliary_evalplotfun (octave_value vplt, octave_value vsel, octave_value vt, octave_value vy, octave_value_list vextarg, octave_idx_type vdeci) * * Return a constant that comes from the evaluation of the @code{OutputFcn} function. The return argument depends on the call to this function, ie. if @var{vdeci} is @code{0} then initilaization of the @code{OutputFcn} function is performed and nothing is returned. If @var{vdeci} is @code{1} then a normal evaluation of the @code{OutputFcn} function is performed and either the constant @code{true} is returned if solving should be stopped or @code{false} is returned if solving should be continued (cf. @file{odeplot.m} for further details). If @var{vdeci} is @code{2} then cleanup of the @code{OutputFcn} function is performed and nothing is returned. The input arguments of this function are * @itemize @minus * @item @var{vplt}: The @code{OutputFcn} function that is evaluated * @item @var{vsel}: The output selection vector for which values should be treated * @item @var{vt}: The time stamp at which the events function is called * @item @var{vy}: The solutions of the set of ODEs at time @var{vt} * @item @var{vextarg}: Extra arguments that are feed through to the @code{OutputFcn} function * @item @var{vdeci}: A decision flag that describes what evaluation should be done * @end itemize * @end deftypefn */ octave_idx_type odepkg_auxiliary_evalplotfun (octave_value vplt, octave_value vsel, octave_value vt, octave_value vy, octave_value_list vextarg, octave_idx_type vdeci) { ColumnVector vresult (vy.vector_value ()); ColumnVector vreduced (vy.length ()); // Check if the user has set the option "OutputSel" then create a // reduced vector that stores the desired values. if (vsel.is_empty ()) { for (octave_idx_type vcnt = 0; vcnt < vresult.length (); vcnt++) vreduced(vcnt) = vresult(vcnt); } else { vreduced.resize (vsel.length ()); ColumnVector vselect (vsel.vector_value ()); for (octave_idx_type vcnt = 0; vcnt < vsel.length (); vcnt++) vreduced(vcnt) = vresult(static_cast (vselect(vcnt)-1)); } // Here we are setting up the list of input arguments before // evaluating the output function octave_value_list varin; varin(0) = vt; varin(1) = octave_value (vreduced); if (vdeci == 0) varin(2) = "init"; else if (vdeci == 1) varin(2) = ""; else if (vdeci == 2) varin(2) = "done"; for (octave_idx_type vcnt = 0; vcnt < vextarg.length (); vcnt++) varin(vcnt+3) = vextarg(vcnt); // Evaluate the output function and return the value of the output // function to the caller function if ((vdeci == 0) || (vdeci == 2)) { if (vplt.is_function_handle () || vplt.is_inline_function ()) feval (vplt.function_value (), varin, 0); else if (vplt.is_string ()) // String may be used from the caller feval (vplt.string_value (), varin, 0); return (true); } else if (vdeci == 1) { octave_value_list vout; if (vplt.is_function_handle () || vplt.is_inline_function ()) vout = feval (vplt.function_value (), varin, 1); else if (vplt.is_string ()) // String may be used if set automatically vout = feval (vplt.string_value (), varin, 1); return (vout(0).bool_value ()); } return (true); } /* -*- texinfo -*- * @deftypefn {Function} octave_value_list odepkg_auxiliary_evaljacide (octave_value vjac, octave_value vt, octave_value vy, octave_value vdy, octave_value_list vextarg) * * Return two matrices that come from the evaluation of the @code{Jacobian} function. The input arguments of this function are * @itemize @minus * @item @var{vjac}: The @code{Jacobian} function that is evaluated * @item @var{vt}: The time stamp at which the events function is called * @item @var{vy}: The solutions of the set of IDEs at time @var{vt} * @item @var{vdy}: The derivatives of the set of IDEs at time @var{vt} * @item @var{vextarg}: Extra arguments that are feed through to the @code{Jacobian} function * @end itemize * * @indent @b{Note:} This function can only be used for IDE problem solvers. * @end deftypefn */ octave_value_list odepkg_auxiliary_evaljacide (octave_value vjac, octave_value vt, octave_value vy, octave_value vdy, octave_value_list vextarg) { octave_value_list varout; // If vjac is a cell array then we expect that two matrices are // returned to the caller function, we can't check for this before if (vjac.is_cell () && (vjac.length () == 2)) { varout(0) = vjac.cell_value ()(0); varout(1) = vjac.cell_value ()(1); if (!varout(0).is_matrix_type () || !varout(1).is_matrix_type ()) { error_with_id ("OdePkg:InvalidArgument", "If Jacobian is a 2x1 cell array then both cells must be matrices"); } } // If vjac is a function_hanlde or an inline_function then evaluate // the function and return the results else if (vjac.is_function_handle () || vjac.is_inline_function ()) { octave_value_list varin; varin(0) = vt; // varin(0).print_with_name (octave_stdout, "vt", true); varin(1) = vy; // varin(1).print_with_name (octave_stdout, "vy", true); varin(2) = vdy; // varin(2).print_with_name (octave_stdout, "vdy", true); // Fill up RHS arguments with extra arguments that are given for (octave_idx_type vcnt = 0; vcnt < vextarg.length (); vcnt++) varin(vcnt+3) = vextarg(vcnt); // Evaluate the Jacobian function and return results varout = feval (vjac.function_value (), varin, 1); } // In principle this is not possible because odepkg_structure_check // should find all occurences that are not valid else { error_with_id ("OdePkg:InvalidArgument", "Jacobian must be a function handle or a cell array with length two"); } return (varout); } /* -*- texinfo -*- * @deftypefn {Function} octave_value odepkg_auxiliary_evaljacode (octave_value vjac, octave_value vt, octave_value vy, octave_value_list vextarg) * * Return a matrix that comes from the evaluation of the @code{Jacobian} function. The input arguments of this function are * @itemize @minus * @item @var{vjac}: The @code{Jacobian} function that is evaluated * @item @var{vt}: The time stamp at which the events function is called * @item @var{vy}: The solutions of the set of ODEs at time @var{vt} * @item @var{vextarg}: Extra arguments that are feed through to the @code{Jacobian} function * @end itemize * * @indent @b{Note:} This function can only be used for ODE and DAE problem solvers. * @end deftypefn */ octave_value odepkg_auxiliary_evaljacode (octave_value vjac, octave_value vt, octave_value vy, octave_value_list vextarg) { octave_value vret; // If vjac is a matrix then return its value to the caller function if (vjac.is_matrix_type ()) { vret = vjac; } // If vjac is a function_hanlde or an inline_function then evaluate // the function and return the results else if (vjac.is_function_handle () || vjac.is_inline_function ()) { octave_value_list varin; octave_value_list varout; varin(0) = vt; varin(1) = vy; // Fill up RHS arguments with extra arguments that are given for (octave_idx_type vcnt = 0; vcnt < vextarg.length (); vcnt++) varin(vcnt+2) = vextarg(vcnt); // Evaluate the Jacobian function and return results varout = feval (vjac.function_value (), varin, 1); vret = varout(0); } // In principle this is not possible because odepkg_structure_check // should find all occurences that are not valid else { error_with_id ("OdePkg:InvalidArgument", "Jacobian must be a function handle or a matrix"); } // vret.print (octave_stdout, true); return (vret); } /* -*- texinfo -*- * @deftypefn {Function} octave_value odepkg_auxiliary_evalmassode (octave_value vmass, octave_value vstate, octave_value vt, octave_value vy, octave_value_list vextarg) * * Return a matrix that comes from the evaluation of the @code{Mass} function. The input arguments of this function are * @itemize @minus * @item @var{vmass}: The @code{Mass} function that is evaluated * @item @var{vstate}: The state variable that either is the string @code{'none'}, @code{'weak'} or @code{'strong'} * @item @var{vt}: The time stamp at which the events function is called * @item @var{vy}: The solutions of the set of ODEs at time @var{vt} * @item @var{vextarg}: Extra arguments that are feed through to the @code{Mass} function * @end itemize * * @indent @b{Note:} This function can only be used for ODE and DAE problem solvers. * @end deftypefn */ octave_value odepkg_auxiliary_evalmassode (octave_value vmass, octave_value vstate, octave_value vt, octave_value vy, octave_value_list vextarg) { octave_value vret; // If vmass is a matrix then return its value to the caller function if (vmass.is_matrix_type ()) return (vmass); // If vmass is a function_hanlde or an inline_function then evaluate // the function and return the results else if (vmass.is_function_handle () || vmass.is_inline_function ()) { octave_value_list varin; octave_value_list varout; if (vstate.is_empty () || !vstate.is_string ()) error_with_id ("OdePkg:InvalidOption", "If \"Mass\" value is a handle then \"MStateDependence\" must be given"); else if (vstate.string_value ().compare ("none") == 0) { varin(0) = vt; for (octave_idx_type vcnt = 0; vcnt < vextarg.length (); vcnt++) varin(vcnt+1) = vextarg(vcnt); } else { // If "MStateDependence" is "weak" or "strong" varin(0) = vt; varin(1) = vy; // Fill up RHS arguments with extra arguments that are given for (octave_idx_type vcnt = 0; vcnt < vextarg.length (); vcnt++) varin(vcnt+2) = vextarg(vcnt); } // Evaluate the Mass function and return results varout = feval (vmass.function_value (), varin, 1); vret = varout(0); } // In principle the execution of the next line is not possible // because odepkg_structure_check should find all occurences that // are not valid else error_with_id ("OdePkg:InvalidArgument", "Mass must be a function handle or a matrix"); return (vret); } /* -*- texinfo -*- * @deftypefn {Function} octave_value odepkg_auxiliary_makestats (octave_value_list vstats, octave_idx_type vprnt) * * Return an @var{octave_value} that contains fields about performance informations of a finished solving process. The input arguments of this function are * @itemize @minus * @item @var{vstats}: The statistics informations list that has to be handled. The values that are treated have to be ordered as follows * @enumerate * @item Number of computed steps * @item Number of rejected steps * @item Number of function evaluations * @item Number of Jacobian evaluations * @item Number of LU decompositions * @item Number of forward backward substitutions * @end enumerate * @item @var{vprnt}: If @code{true} then the statistics information also is displayed on screen * @end itemize * @end deftypefn */ octave_value odepkg_auxiliary_makestats (octave_value_list vstats, octave_idx_type vprnt) { octave_scalar_map vretval; if (vstats.length () < 5) error_with_id ("OdePkg:InvalidArgument", "C++ function odepkg_auxiliary_makestats error"); else { vretval.assign ("nsteps", vstats(0)); vretval.assign ("nfailed", vstats(1)); vretval.assign ("nfevals", vstats(2)); vretval.assign ("npds", vstats(3)); vretval.assign ("ndecomps", vstats(4)); vretval.assign ("nlinsols", vstats(5)); } if (vprnt == true) { octave_stdout << "Number of function calls: " << vstats(0).int_value () << std::endl; octave_stdout << "Number of failed attempts: " << vstats(1).int_value () << std::endl; octave_stdout << "Number of function evals: " << vstats(2).int_value () << std::endl; octave_stdout << "Number of Jacobian evals: " << vstats(3).int_value () << std::endl; octave_stdout << "Number of LU decompositions: " << vstats(4).int_value () << std::endl; octave_stdout << "Number of fwd/backwd subst: " << vstats(5).int_value () << std::endl; } return (octave_value (vretval)); } /* -*- texinfo -*- * @deftypefn {Function} octave_idx_type odepkg_auxiliary_solstore (octave_value &vt, octave_value &vy, octave_idx_type vdeci) * * If @var{vdeci} is @code{0} (@var{vt} is a pointer to the initial time step and @var{vy} is a pointer to the initial values vector) then this function is initialized. Otherwise if @var{vdeci} is @code{1} (@var{vt} is a pointer to another time step and @var{vy} is a pointer to the solution vector) the values of @var{vt} and @var{vy} are added to the internal variable, if @var{vdeci} is @code{2} then the internal vectors are returned. The input arguments of this function are * @itemize @minus * @item @var{vt}: The time stamp at which the events function is called * @item @var{vy}: The solutions of the set of ODEs at time @var{vt} * @item @var{vdeci}: A decision flag that describes what evaluation should be done * @end itemize * @end deftypefn */ octave_idx_type odepkg_auxiliary_solstore (octave_value &vt, octave_value &vy, octave_idx_type vdeci) { // If the option "OutputSel" has been set then prepare a vector with // a reduced number of elements. The indexes of the values are given // in vsel if vdeci == (0 || 1). RowVector vrow; if (vdeci != 2) { vrow.resize (vy.length ()); vrow = RowVector (vy.vector_value ()); } // Now have a look at the vdeci variable and do 0..initialization, // 1..store other elements, 2..return stored elements to the caller // function, 3..delete the last line of the matrices static ColumnVector vtstore(1); static Matrix vystore; switch (vdeci) { case 0: // Keep the resize command here because otherwise we stack the // new values of t even if we have already started a new call to // the solver vtstore.resize(1); vtstore(0) = vt.double_value (); vystore = Matrix (vrow); break; case 1: vtstore = vtstore.stack (vt.column_vector_value ()); vystore = vystore.stack (Matrix (vrow)); break; case 2: vt = octave_value (vtstore); vy = octave_value (vystore); break; case 3: vtstore = vtstore.extract (0, vtstore.length () - 2); vystore = vystore.extract (0, 0, vtstore.rows () - 2, vtstore.cols () - 1); default: // This can be used for displaying all values at any time, // eg. if the code should be debuged or something like this vt = octave_value (vtstore); vy = octave_value (vystore); vt.print_with_name (octave_stdout, "vt"); vy.print_with_name (octave_stdout, "vy"); break; } return (true); } /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_auxiliary_functions.h0000644000000000000000000000367212526637474017543 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ #if !defined (odepkg_auxiliary_functions_h) #define odepkg_auxiliary_functions_h 1 octave_idx_type odepkg_auxiliary_isvector (octave_value vval); octave_value_list odepkg_auxiliary_evaleventfun (octave_value veve, octave_value vt, octave_value vy, octave_value_list vextarg, octave_idx_type vdeci); octave_idx_type odepkg_auxiliary_evalplotfun (octave_value vplt, octave_value vsel, octave_value vt, octave_value vy, octave_value_list vextarg, octave_idx_type vdeci); octave_value_list odepkg_auxiliary_evaljacide (octave_value vjac, octave_value vt, octave_value vy, octave_value vyd, octave_value_list vextarg); octave_value odepkg_auxiliary_evaljacode (octave_value vjac, octave_value vt, octave_value vy, octave_value_list vextarg); octave_value odepkg_auxiliary_evalmassode (octave_value vmass, octave_value vstate, octave_value vt, octave_value vy, octave_value_list vextarg); octave_value odepkg_auxiliary_makestats (octave_value_list vstats, octave_idx_type vprnt); octave_idx_type odepkg_auxiliary_solstore (octave_value &vt, octave_value &vy, octave_idx_type vdeci); #endif /* odepkg_auxiliary_functions_h */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_octsolver_ddaskr.cc0000644000000000000000000011412012526637474017141 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ /* Compile this file manually and run some tests with the following command bash:$ mkoctfile -v -W -Wall -Wshadow odepkg_octsolver_ddaskr.cc \ odepkg_auxiliary_functions.cc daskr/ddaskr.f daskr/dlinpk.f -o odekdi.oct octave> octave --quiet --eval "autoload ('odekdi', [pwd, '/odekdi.oct']); \ test 'odepkg_octsolver_ddaskr.cc'" If you have installed 'gfortran' or 'g95' on your computer then use the FFLAGS=-fno-automatic option because otherwise the solver function will be broken, eg. bash:$ FFLAGS="-fno-automatic ${FFLAGS}" mkoctfile -v -Wall -W \ -Wshadow odepkg_octsolver_ddaskr.cc odepkg_auxiliary_functions.cc \ cash/ddaskr.f -o odekdi.oct octave> octave --quiet --eval "autoload ('odekdi', [pwd, '/odekdi.oct']); \ test 'odepkg_octsolver_ddaskr.cc'" */ #include #include #include #include #include #include "odepkg_auxiliary_functions.h" typedef octave_idx_type (*odepkg_ddaskr_restype) (const double& T, const double* Y, const double* YPRIME, const double& CJ, double* DELTA, octave_idx_type& IRES, const double* RPAR, const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_ddaskr_jactype) (const double& T, const double* Y, const double* YPRIME, double* PD, const double& CJ, const double* RPAR, const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_ddaskr_psoltype) (const octave_idx_type& NEQ, const double& T, const double* Y, const double* YPRIME, const double* SAVR, const double* WK, const octave_idx_type& CJ, double* WGHT,const double* WP, const octave_idx_type* IWP, double* B, const octave_idx_type& EPLIN, octave_idx_type& IER, const double* RPAR, const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_ddaskr_rttype) (const octave_idx_type& NEQ, const double& T, const double* Y, const double* YP, const octave_idx_type& NRT, const double* RVAL, const double* RPAR, const octave_idx_type* IPAR); // typedef octave_idx_type (*odepkg_ddaskr_krylov_jactype) // (odepkg_ddaskr_restype, octave_idx_type& IRES, const octave_idx_type& NEQ, // const double& T, const double* Y, const double* YPRIME, // double* REWT, const double* SAVR, const double* WK, // const double& H, const double& CJ, const double* WP, // const octave_idx_type* IWP, octave_idx_type& IER, // const double* RPAR, const octave_idx_type* IPAR); // typedef octave_idx_type (*odepkg_ddaskr_krylov_psoltype) // (const octave_idx_type& NEQ, const double& T, const double* Y, // const double* YPRIME, const double* SAVR, const double* WK, // const octave_idx_type& CJ, double* WGHT,const double* WP, // const octave_idx_type* IWP, double* B, const octave_idx_type& EPLIN, // octave_idx_type& IER, const double* RPAR, const octave_idx_type* IPAR); extern "C" { F77_RET_T F77_FUNC (ddaskr, DDASKR) (odepkg_ddaskr_restype, const octave_idx_type& NEQ, const double& T, const double* Y, const double* YPRIME, const double& TOUT, const octave_idx_type* INFO, const double* RTOL, const double* ATOL, const octave_idx_type& IDID, const double* RWORK, const octave_idx_type& LRW, const octave_idx_type* IWORK, const octave_idx_type& LIW, const double* RPAR, const octave_idx_type* IPAR, odepkg_ddaskr_jactype, odepkg_ddaskr_psoltype, odepkg_ddaskr_rttype, const octave_idx_type& NRT, octave_idx_type* JROOT); } static octave_value_list vddaskrextarg; static octave_value vddaskrodefun; static octave_value vddaskrjacfun; static octave_idx_type vddaskrneqn; octave_idx_type odepkg_ddaskr_resfcn (const double& T, const double* Y, const double* YPRIME, GCC_ATTR_UNUSED const double& CJ, double* DELTA, GCC_ATTR_UNUSED octave_idx_type& IRES, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element wise, // otherwise Octave will crash if these variables will be freed ColumnVector A(vddaskrneqn), APRIME(vddaskrneqn); for (octave_idx_type vcnt = 0; vcnt < vddaskrneqn; vcnt++) { A(vcnt) = Y[vcnt]; APRIME(vcnt) = YPRIME[vcnt]; } // Fill the variable for the input arguments before evaluating the // function that keeps the set of implicit differential equations octave_value_list varin; varin(0) = T; varin(1) = A; varin(2) = APRIME; for (octave_idx_type vcnt = 0; vcnt < vddaskrextarg.length (); vcnt++) varin(vcnt+3) = vddaskrextarg(vcnt); octave_value_list vout = feval (vddaskrodefun.function_value (), varin, 1); // Return the results from the function evaluation to the Fortran // solver, again copy them and don't just create a Fortran vector ColumnVector vcol = vout(0).column_vector_value (); for (octave_idx_type vcnt = 0; vcnt < vddaskrneqn; vcnt++) DELTA[vcnt] = vcol(vcnt); return (true); } octave_idx_type odepkg_ddaskr_jacfcn (const double& T, const double* Y, const double* YPRIME, double* PD, const double& CJ, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(vddaskrneqn), APRIME(vddaskrneqn); for (octave_idx_type vcnt = 0; vcnt < vddaskrneqn; vcnt++) { A(vcnt) = Y[vcnt]; APRIME(vcnt) = YPRIME[vcnt]; } // Set the values that are needed as input arguments before calling // the Jacobian function octave_value vt = octave_value (T); octave_value vy = octave_value (A); octave_value vdy = octave_value (APRIME); octave_value_list vout = odepkg_auxiliary_evaljacide (vddaskrjacfun, vt, vy, vdy, vddaskrextarg); // Computes the NxN iteration matrix or partial derivatives for the // Fortran solver of the form PD=DG/DY+1/CON*(DG/DY') octave_value vbdov = vout(0) + CJ * vout(1); Matrix vbd = vbdov.matrix_value (); for (octave_idx_type vrow = 0; vrow < vddaskrneqn; vrow++) for (octave_idx_type vcol = 0; vcol < vddaskrneqn; vcol++) PD[vrow+vcol*vddaskrneqn] = vbd (vrow, vcol); // Don't know what my mistake is but the following code line never // worked for me (ie. the solver crashes Octave if it is used) // PD = vbd.fortran_vec (); return (true); } octave_idx_type odepkg_ddaskr_rtfcn // this is a dummy function (GCC_ATTR_UNUSED const octave_idx_type& NEQ, GCC_ATTR_UNUSED const double& T, GCC_ATTR_UNUSED const double* Y, GCC_ATTR_UNUSED const double* YP, GCC_ATTR_UNUSED const octave_idx_type& NRT, GCC_ATTR_UNUSED const double* RVAL, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { return (true); } octave_idx_type odepkg_ddaskr_psolfcn // this is a dummy function (GCC_ATTR_UNUSED const octave_idx_type& NEQ, GCC_ATTR_UNUSED const double& T, GCC_ATTR_UNUSED const double* Y, GCC_ATTR_UNUSED const double* YPRIME, GCC_ATTR_UNUSED const double* SAVR, GCC_ATTR_UNUSED const double* WK, GCC_ATTR_UNUSED const octave_idx_type& CJ, GCC_ATTR_UNUSED double* WGHT, GCC_ATTR_UNUSED const double* WP, GCC_ATTR_UNUSED const octave_idx_type* IWP, GCC_ATTR_UNUSED double* B, GCC_ATTR_UNUSED const octave_idx_type& EPLIN, GCC_ATTR_UNUSED octave_idx_type& IER, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { return (true); } octave_idx_type odepkg_ddaskr_error (octave_idx_type verr) { switch (verr) { case 0: break; // Everything is fine case -1: break; default: break; } return (true); } // PKG_ADD: autoload ("odekdi", "dldsolver.oct"); DEFUN_DLD (odekdi, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Command} {[@var{}] =} odekdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}])\n\ @deftypefnx {Command} {[@var{sol}] =} odekdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}])\n\ @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odekdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}])\n\ \n\ This function file can be used to solve a set of stiff implicit differential equations (IDEs). This function file is a wrapper file that uses the direct method (not the Krylov method) of Petzold's, Brown's, Hindmarsh's and Ulrich's Fortran solver @file{ddaskr.f}.\n\ \n\ If this function is called with no return argument then plot the solution over time in a figure window while solving the set of IDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{y0} is a double vector that defines the initial values of the states, @var{dy0} is a double vector that defines the initial values of the derivatives, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.\n\ \n\ If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of IDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.\n\ \n\ If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.\n\ \n\ For example,\n\ @example\n\ function res = odepkg_equations_ilorenz (t, y, yd)\n\ res = [10 * (y(2) - y(1)) - yd(1);\n\ y(1) * (28 - y(3)) - yd(2);\n\ y(1) * y(2) - 8/3 * y(3) - yd(3)];\n\ endfunction\n\ \n\ vopt = odeset (\"InitialStep\", 1e-3, \"MaxStep\", 1e-1, \\\n\ \"OutputFcn\", @@odephas3, \"Refine\", 5);\n\ odekdi (@@odepkg_equations_ilorenz, [0, 25], [3 15 1], \\\n\ [120 81 42.333333], vopt);\n\ @end example\n\ @end deftypefn\n\ \n\ @seealso{odepkg}") { octave_idx_type nargin = args.length (); // The number of input arguments octave_value_list vretval; // The cell array of return args octave_scalar_map vodeopt; // The OdePkg options structure // Check number and types of all the input arguments if (nargin < 4) { print_usage (); return (vretval); } // If args(0)==function_handle then set the vddaskrodefun variable // that has been defined "static" before if (!args(0).is_function_handle () && !args(0).is_inline_function ()) { error_with_id ("OdePkg:InvalidArgument", "First input argument must be a valid function handle"); return (vretval); } else // We store the args(0) argument in the static variable vddaskrodefun vddaskrodefun = args(0); // Check if the second input argument is a valid vector describing // the time window of solving, it may be of length 2 or longer if (args(1).is_scalar_type () || !odepkg_auxiliary_isvector (args(1))) { error_with_id ("OdePkg:InvalidArgument", "Second input argument must be a valid vector"); return (vretval); } // Check if the third input argument is a valid vector describing // the initial values of the variables of the IDEs if (!odepkg_auxiliary_isvector (args(2))) { error_with_id ("OdePkg:InvalidArgument", "Third input argument must be a valid vector"); return (vretval); } // Check if the fourth input argument is a valid vector describing // the initial values of the derivatives of the differential equations if (!odepkg_auxiliary_isvector (args(3))) { error_with_id ("OdePkg:InvalidArgument", "Fourth input argument must be a valid vector"); return (vretval); } // Check if the third and the fourth input argument (check for // vector already was successful before) have the same length if (args(2).length () != args(3).length ()) { error_with_id ("OdePkg:InvalidArgument", "Third and fourth input argument must have the same length"); return (vretval); } // Check if there are further input arguments ie. the options // structure and/or arguments that need to be passed to the // OutputFcn, Events and/or Jacobian etc. if (nargin >= 5) { // Fifth input argument != OdePkg option, need a default structure if (!args(4).is_map ()) { octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure for (octave_idx_type vcnt = 4; vcnt < nargin; vcnt++) vddaskrextarg(vcnt-4) = args(vcnt); // Save arguments in vddaskrextarg } // Fifth input argument == OdePkg option, extra input args given too else if (nargin > 5) { octave_value_list varin; varin(0) = args(4); varin(1) = "odekdi"; octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create structure of args(4) for (octave_idx_type vcnt = 5; vcnt < nargin; vcnt++) vddaskrextarg(vcnt-5) = args(vcnt); // Save extra arguments } // Fifth input argument == OdePkg option, no extra input args given else { octave_value_list varin; varin(0) = args(4); varin(1) = "odekdi"; // Check structure octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } } // if (nargin >= 5) else { // if nargin == 4, everything else has been checked before octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } /* Start PREPROCESSING, ie. check which options have been set and * print warnings if there are options that can't be handled by this * solver or have not been implemented yet *******************************************************************/ // Implementation of the option RelTol has been finished, this // option can be set by the user to another value than default value octave_value vreltol = vodeopt.contents ("RelTol"); if (vreltol.is_empty ()) { vreltol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"RelTol\" not set, new value 1e-6 is used"); } else if (!vreltol.is_scalar_type ()) { if (vreltol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"RelTol\" must be the same as the number of equations"); return (vretval); } } // Implementation of the option AbsTol has been finished, this // option can be set by the user to another value than default value octave_value vabstol = vodeopt.contents ("AbsTol"); if (vabstol.is_empty ()) { vabstol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"AbsTol\" not set, new value 1e-6 is used"); } else if (!vabstol.is_scalar_type ()) { if (vabstol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"AbsTol\" must be the same as the number of equations"); return (vretval); } } // Setting the tolerance type that depends on the types (scalar or // vector) of the options RelTol and AbsTol octave_idx_type vitol; if (vreltol.is_scalar_type () && vabstol.is_scalar_type ()) vitol = 0; else if (vreltol.length () == vabstol.length ()) vitol = 1; else { error_with_id ("OdePkg:InvalidOption", "Options \"RelTol\" and \"AbsTol\" must have same length"); return (vretval); } // The option NormControl will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnorm = vodeopt.contents ("NormControl"); if (!vnorm.is_empty ()) if (vnorm.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"NormControl\" will be ignored by this solver"); // The option NonNegative will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnneg = vodeopt.contents ("NonNegative"); if (!vnneg.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NonNegative\" will be ignored by this solver"); // Implementation of the option OutputFcn has been finished, this // option can be set by the user to another value than default value octave_value vplot = vodeopt.contents ("OutputFcn"); if (vplot.is_empty () && nargout == 0) vplot = "odeplot"; // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value octave_value voutsel = vodeopt.contents ("OutputSel"); // The option Refine will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vrefine = vodeopt.contents ("Refine"); if (vrefine.int_value () != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"Refine\" will be ignored by this solver"); // Implementation of the option Stats has been finished, this option // can be set by the user to another value than default value octave_value vstats = vodeopt.contents ("Stats"); // Implementation of the option InitialStep has been finished, this // option can be set by the user to another value than default value octave_value vinitstep = vodeopt.contents ("InitialStep"); if (args(1).length () > 2) { if (!vinitstep.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" will be ignored if fixed time stamps are given"); vinitstep = args(1).vector_value ()(1); } else if (vinitstep.is_empty ()) { vinitstep = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" not set, new value 1e-6 is used"); } // Implementation of the option MaxStep has been finished, this // option can be set by the user to another value than default value octave_value vmaxstep = vodeopt.contents ("MaxStep"); if (vmaxstep.is_empty () && args(1).length () == 2) { vmaxstep = (args(1).vector_value ()(1) - args(1).vector_value ()(0)) / 12.5; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxStep\" not set, new value %3.1e is used", vmaxstep.double_value ()); } // Implementation of the option Events has been finished, this // option can be set by the user to another value than default // value, odepkg_structure_check already checks for a valid value octave_value vevents = vodeopt.contents ("Events"); octave_value_list veveres; // We save the results of Events here // The options 'Jacobian', 'JPattern' and 'Vectorized' octave_value vjac = vodeopt.contents ("Jacobian"); if (!vjac.is_empty ()) vddaskrjacfun = vjac; // The option Mass will be ignored by this solver. We can't handle // Mass-matrix options with IDE problems octave_value vmass = vodeopt.contents ("Mass"); if (!vmass.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"Mass\" will be ignored by this solver"); // The option MStateDependence will be ignored by this solver. We // can't handle Mass-matrix options with IDE problems octave_value vmst = vodeopt.contents ("MStateDependence"); if (!vmst.is_empty ()) if (vmst.string_value ().compare ("weak") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MStateDependence\" will be ignored by this solver"); // The option MvPattern will be ignored by this solver. We // can't handle Mass-matrix options with IDE problems octave_value vmvpat = vodeopt.contents ("MvPattern"); if (!vmvpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MvPattern\" will be ignored by this solver"); // The option MvPattern will be ignored by this solver. We // can't handle Mass-matrix options with IDE problems octave_value vmsing = vodeopt.contents ("MassSingular"); if (!vmsing.is_empty ()) if (vmsing.string_value ().compare ("maybe") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MassSingular\" will be ignored by this solver"); // The option InitialSlope will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vinitslope = vodeopt.contents ("InitialSlope"); if (!vinitslope.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialSlope\" will be ignored by this solver"); // Implementation of the option MaxOrder has been finished, this // option can be set by the user to another value than default value octave_value vmaxder = vodeopt.contents ("MaxOrder"); if (vmaxder.is_empty ()) { vmaxder = 3; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" not set, new value 3 is used"); } else if (vmaxder.int_value () < 1) { vmaxder = 3; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" is zero, new value 3 is used"); } // The option BDF will be ignored because this is a BDF solver octave_value vbdf = vodeopt.contents ("BDF"); if (!vbdf.is_empty ()) if (vbdf.string_value () != "on") { vbdf = "on"; warning_with_id ("OdePkg:InvalidOption", "Option \"BDF\" set \"off\", new value \"on\" is used"); } // The option NewtonTol and MaxNewtonIterations will be ignored by // this solver, IT NEEDS TO BE CHECKED IF THE FORTRAN CORE SOLVER // CAN HANDLE THESE OPTIONS octave_value vntol = vodeopt.contents ("NewtonTol"); if (!vntol.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NewtonTol\" will be ignored by this solver"); octave_value vmaxnewton = vodeopt.contents ("MaxNewtonIterations"); if (!vmaxnewton.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" will be ignored by this solver"); /* Start MAINPROCESSING, set up all variables that are needed by this * solver and then initialize the solver function and get into the * main integration loop ********************************************************************/ NDArray vTIME = args(1).array_value (); NDArray vY = args(2).array_value (); NDArray vYPRIME = args(3).array_value (); NDArray vRTOL = vreltol.array_value (); NDArray vATOL = vabstol.array_value (); vddaskrneqn = args(2).length (); double T = vTIME(0); double TEND = vTIME(vTIME.length () - 1); double *Y = vY.fortran_vec (); double *YPRIME = vYPRIME.fortran_vec (); octave_idx_type IDID = 0; double *RTOL = vRTOL.fortran_vec (); double *ATOL = vATOL.fortran_vec (); double RPAR[1] = {0.0}; octave_idx_type IPAR[1] = {0}; octave_idx_type NRT = 0; OCTAVE_LOCAL_BUFFER (octave_idx_type, JROOT, NRT); for (octave_idx_type vcnt = 0; vcnt < NRT; vcnt++) JROOT[vcnt] = 0; octave_idx_type LRW = 60 + vddaskrneqn * (9 + vddaskrneqn) + 3 * NRT; OCTAVE_LOCAL_BUFFER (double, RWORK, LRW); for (octave_idx_type vcnt = 0; vcnt < LRW; vcnt++) RWORK[vcnt] = 0.0; octave_idx_type LIW = 40 + vddaskrneqn; OCTAVE_LOCAL_BUFFER (octave_idx_type, IWORK, LIW); for (octave_idx_type vcnt = 0; vcnt < LIW; vcnt++) IWORK[vcnt] = 0; octave_idx_type N = 20; OCTAVE_LOCAL_BUFFER (octave_idx_type, INFO, N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) INFO[vcnt] = 0; INFO[0] = 0; // Define that it is the initial first call INFO[1] = vitol; // RelTol/AbsTol are scalars or vectors INFO[2] = 1; // An intermediate output is wanted INFO[3] = 0; // Integrate behind TEND if (!vjac.is_empty ()) INFO[4] = 1; else INFO[4] = 0; // Internally calculate a Jacobian? 0..yes INFO[5] = 0; // Have a full Jacobian matrix? 0..yes INFO[6] = 1; // Use the value for maximum step size INFO[7] = 1; // The initial step size INFO[8] = 1; // Use MaxOrder 5 as default? 1..no INFO[11] = 0; // direct method (not krylov) RWORK[1] = vmaxstep.double_value (); // MaxStep value RWORK[2] = vinitstep.double_value (); // InitialStep value IWORK[2] = vmaxder.int_value (); // MaxOrder value // If the user has set an OutputFcn or an Events function then // initialize these IO-functions for further use octave_value vtim (T); octave_value vsol (vY); octave_value vyds (vYPRIME); odepkg_auxiliary_solstore (vtim, vsol, 0); if (!vplot.is_empty ()) odepkg_auxiliary_evalplotfun (vplot, voutsel, args(1), args(2), vddaskrextarg, 0); octave_value_list veveideargs; veveideargs(0) = vsol; veveideargs(1) = vyds; Cell veveidearg (veveideargs); if (!vevents.is_empty ()) odepkg_auxiliary_evaleventfun (vevents, vtim, veveidearg, vddaskrextarg, 0); // We are calling the core solver here to intialize all variables F77_XFCN (ddaskr, DDASKR, // Keep 5 arguments per line here (odepkg_ddaskr_resfcn, vddaskrneqn, T, Y, YPRIME, TEND, INFO, RTOL, ATOL, IDID, RWORK, LRW, IWORK, LIW, RPAR, IPAR, odepkg_ddaskr_jacfcn, odepkg_ddaskr_psolfcn, odepkg_ddaskr_rtfcn, NRT, JROOT)); if (IDID < 0) { odepkg_ddaskr_error (IDID); return (vretval); } // We need that variable in the following loop after calling the // core solver function and before calling the plot function ColumnVector vcres(vddaskrneqn); ColumnVector vydrs(vddaskrneqn); if (vTIME.length () == 2) { INFO[0] = 1; // Set this info variable ie. continue solving while (T < TEND) { F77_XFCN (ddaskr, DDASKR, // Keep 5 arguments per line here (odepkg_ddaskr_resfcn, vddaskrneqn, T, Y, YPRIME, TEND, INFO, RTOL, ATOL, IDID, RWORK, LRW, IWORK, LIW, RPAR, IPAR, odepkg_ddaskr_jacfcn, odepkg_ddaskr_psolfcn, odepkg_ddaskr_rtfcn, NRT, JROOT)); if (IDID < 0) { odepkg_ddaskr_error (IDID); return (vretval); } // This call of the Fortran solver has been successful so let us // plot the output and save the results for (octave_idx_type vcnt = 0; vcnt < vddaskrneqn; vcnt++) { vcres(vcnt) = Y[vcnt]; vydrs(vcnt) = YPRIME[vcnt]; } vsol = vcres; vyds = vydrs; vtim = T; if (!vevents.is_empty ()) { veveideargs(0) = vsol; veveideargs(1) = vyds; veveidearg = veveideargs; veveres = odepkg_auxiliary_evaleventfun (vevents, vtim, veveidearg, vddaskrextarg, 1); if (!veveres(0).cell_value ()(0).is_empty ()) if (veveres(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = veveres(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = veveres(0).cell_value ()(3).matrix_value (); vtim = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vsol = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); T = TEND; // let's get out here, the Events function told us to finish } } if (!vplot.is_empty ()) { if (odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vddaskrextarg, 1)) { error ("Missing error message implementation"); return (vretval); } } odepkg_auxiliary_solstore (vtim, vsol, 1); } } /* Start POSTPROCESSING, check how many arguments should be returned * to the caller and check which extra arguments have to be set *******************************************************************/ // Set up values that come from the last Fortran call and that are // needed to call the OdePkg output function a last time again for (octave_idx_type vcnt = 0; vcnt < vddaskrneqn; vcnt++) { vcres(vcnt) = Y[vcnt]; vydrs(vcnt) = YPRIME[vcnt]; } vsol = vcres; vyds = vydrs; vtim = T; veveideargs(0) = vsol; veveideargs(1) = vyds; veveidearg = veveideargs; if (!vevents.is_empty ()) odepkg_auxiliary_evaleventfun (vevents, vtim, vsol, vddaskrextarg, 2); if (!vplot.is_empty ()) odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vddaskrextarg, 2); // Return the results that have been stored in the // odepkg_auxiliary_solstore function octave_value vtres, vyres; odepkg_auxiliary_solstore (vtres, vyres, 2); // Get the stats information as an octave_scalar_map if the option 'Stats' // has been set with odeset // "nsteps", "nfailed", "nfevals", "npds", "ndecomps", "nlinsols" octave_value_list vstatinput; vstatinput(0) = IWORK[10]; vstatinput(1) = IWORK[14]; vstatinput(2) = IWORK[11]; vstatinput(3) = IWORK[12]; vstatinput(4) = 0; vstatinput(5) = IWORK[18]; octave_value vstatinfo; if ((vstats.string_value ().compare ("on") == 0) && (nargout == 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, false); else if ((vstats.string_value ().compare ("on") == 0) && (nargout != 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, true); // Set up output arguments that depend on how many output arguments // are desired -- check the nargout variable if (nargout == 1) { octave_scalar_map vretmap; vretmap.assign ("x", vtres); vretmap.assign ("y", vyres); vretmap.assign ("solver", "odekdi"); if (vstats.string_value ().compare ("on") == 0) vretmap.assign ("stats", vstatinfo); if (!vevents.is_empty ()) { vretmap.assign ("ie", veveres(0).cell_value ()(1)); vretmap.assign ("xe", veveres(0).cell_value ()(2)); vretmap.assign ("ye", veveres(0).cell_value ()(3)); } vretval(0) = octave_value (vretmap); } else if (nargout == 2) { vretval(0) = vtres; vretval(1) = vyres; } else if (nargout == 5) { Matrix vempty; // prepare an empty matrix vretval(0) = vtres; vretval(1) = vyres; vretval(2) = vempty; vretval(3) = vempty; vretval(4) = vempty; if (!vevents.is_empty ()) { vretval(2) = veveres(0).cell_value ()(2); vretval(3) = veveres(0).cell_value ()(3); vretval(4) = veveres(0).cell_value ()(1); } } return (vretval); } /* %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [vres] = fpol (vt, vy, vyd, varargin) %! vres = [vy(2) - vyd(1); %! (1 - vy(1)^2) * vy(2) - vy(1) - vyd(2)]; %!function [vjac, vyjc] = fjac (vt, vy, vyd, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %! vyjc = [-1, 0; 0, -1]; %!function [vjac, vyjc] = fjcc (vt, vy, vyd, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %! vyjc = sparse ([-1, 0; 0, -1]); %!function [vval, vtrm, vdir] = feve (vt, vy, vyd, varargin) %! vval = vyd; %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, vyd, varargin) %! vval = vyd; %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vref] = fref () %# The computed reference solut %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (size (vt) != [2, 1] && size (vt) != [1, 2]) %! error ('"fout" step "init"'); %! end %! elseif (isempty (vflag)) %! if (size (vt) ~= [1, 1]) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (size (vt) ~= [1, 1]) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! vsol = odekdi (1, [0, 2], [2; 0], [0; -2]); %!error %# input argument number two %! vsol = odekdi (@fpol, 1, [2; 0], [0; -2]); %!error %# input argument number three %! vsol = odekdi (@fpol, [0, 2], 1, [0; -2]); %!error %# input argument number four %! vsol = odekdi (@fpol, [0, 2], [2; 0], 1); %!test %# one output argument %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'odekdi'); %!test %# two output arguments %! [vt, vy] = odekdi (@fpol, [0, 2], [2; 0], [0; -2]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = odekdi (@fpol, [0, 2], [2; 0], [0; -2]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy,vyd) [vy(2)-vyd(1); (1-vy(1)^2)*vy(2)-vy(1)-vyd(2)]; %! vsol = odekdi (fvdb, [0, 2], [2; 0], [0; -2]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-4); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-4); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-1); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-7); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-3); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end-1)-vsol.x(end-2)], [1e-2], 1e-1); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = odekdi (@fpol, [0, 10], [2; 0], [0; -2], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'MaxStep', 0.1); %! vsol = odekdi (@fpol, [0, 10], [2; 0], [0; -2], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.49537, -0.82867, -2.67469], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'MaxStep', 0.1); %! [vt, vy, vxe, vye, vie] = odekdi (@fpol, [0, 10], [2; 0], [0; -2], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.49537, -0.82867, -2.67469], 1e-1); %! warning ('on', 'OdePkg:HideWarning'); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac, 'InitialStep', 1e-12); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for Mass option is missing %! %# test for MStateDependence option is missing %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %!test %# MaxOrder option %! vopt = odeset ('MaxOrder', 5); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# BDF option %! vopt = odeset ('BDF', 'on'); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set NewtonTol option to something else than default %! vopt = odeset ('NewtonTol', 1e-3); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set MaxNewtonIterations option to something else than default %! vopt = odeset ('MaxNewtonIterations', 2); %! vsol = odekdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! warning ('on', 'OdePkg:InvalidOption'); */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_octsolver_mebdfdae.cc0000644000000000000000000012227712526637474017434 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ /* Compile this file manually and run some tests with the following command bash:$ mkoctfile -v -Wall -W -Wshadow odepkg_octsolver_mebdfdae.cc \ odepkg_auxiliary_functions.cc cash/mebdfdae.f -o odebda.oct bash:$ octave --quiet --eval "autoload ('odebda', [pwd, '/odebda.oct']); \ test 'odepkg_octsolver_mebdfdae.cc'" If you have installed 'gfortran' or 'g95' on your computer then use the FFLAGS=-fno-automatic option because otherwise the solver function will be broken, eg. bash:$ FFLAGS="-fno-automatic ${FFLAGS}" mkoctfile -v -Wall -W \ -Wshadow odepkg_octsolver_mebdfdae.cc odepkg_auxiliary_functions.cc \ cash/mebdfdae.f -o odebda.oct octave> octave --quiet --eval "autoload ('odebda', [pwd, '/odebda.oct']); \ test 'odepkg_octsolver_mebdfdae.cc'" For an explanation about various parts of this source file cf. the source file odepkg_octsolver_odebdi.cc. The implementation of that file is very similiar to the implementation of this file. */ #include #include #include #include #include #include "odepkg_auxiliary_functions.h" typedef octave_idx_type (*odepkg_mebdfdae_usrtype) (const octave_idx_type& N, const double& T, const double* Y, double* YDOT, const octave_idx_type* IPAR, const double* RPAR, const octave_idx_type& IERR); typedef octave_idx_type (*odepkg_mebdfdae_jactype) (const double& T, const double* Y, double* PD, const octave_idx_type& N, const octave_idx_type& MEBAND, const octave_idx_type* IPAR, const double* RPAR, const octave_idx_type& IERR); typedef octave_idx_type (*odepkg_mebdfdae_masstype) (const octave_idx_type& N, double* AM, const octave_idx_type* MASBND, const double* RPAR, const octave_idx_type* IPAR, const octave_idx_type& IERR); extern "C" { F77_RET_T F77_FUNC (mebdf, MEBDF) // 3 arguments per line (const octave_idx_type& N, const double& T0, const double& HO, const double* Y0, const double& TOUT, const double& TEND, const octave_idx_type& MF, octave_idx_type& IDID, const octave_idx_type& LOUT, const octave_idx_type& LWORK, const double* WORK, const octave_idx_type& LIWORK, const octave_idx_type* IWORK, const octave_idx_type* MBND, const octave_idx_type* MASBND, const octave_idx_type& MAXDER, const octave_idx_type& ITOL, const double* RTOL, const double* ATOL, const double* RPAR, const octave_idx_type* IPAR, odepkg_mebdfdae_usrtype, odepkg_mebdfdae_jactype, odepkg_mebdfdae_masstype, octave_idx_type& IERR); } // extern "C" static octave_value_list vmebdfdaeextarg; static octave_value vmebdfdaeodefun; static octave_value vmebdfdaejacfun; static octave_value vmebdfdaemass; static octave_value vmebdfdaemassstate; octave_idx_type odepkg_mebdfdae_usrfcn (const octave_idx_type& N, const double& T, const double* Y, double* YDOT, GCC_ATTR_UNUSED const octave_idx_type* IPAR, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type& IERR) { // Copy the values that come from the Fortran function element wise, // otherwise Octave will crash if these variables will be freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Fill the variable for the input arguments before evaluating the // function that keeps the set of implicit differential equations octave_value_list varin; varin(0) = T; varin(1) = A; for (octave_idx_type vcnt = 0; vcnt < vmebdfdaeextarg.length (); vcnt++) varin(vcnt+2) = vmebdfdaeextarg(vcnt); octave_value_list vout = feval (vmebdfdaeodefun.function_value (), varin, 1); // Return the results from the function evaluation to the Fortran // solver, again copy them and don't just create a Fortran vector ColumnVector vcol = vout(0).column_vector_value (); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) YDOT[vcnt] = vcol(vcnt); return (true); } octave_idx_type odepkg_mebdfdae_jacfcn (const double& T, const double* Y, double* PD, const octave_idx_type& N, GCC_ATTR_UNUSED const octave_idx_type& MEBAND, GCC_ATTR_UNUSED const octave_idx_type* IPAR, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type& IERR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Jacobian function octave_value vt = octave_value (T); octave_value vy = octave_value (A); octave_value_list vout = odepkg_auxiliary_evaljacode (vmebdfdaejacfun, vt, vy, vmebdfdaeextarg); // Computes the NxN iteration matrix or partial derivatives for the // Fortran solver of the form PD=DG/DY+1/CON*(DG/DY') // octave_value vbdov = vout(0) + 1/CON * vout(1); Matrix vbd = vout(0).matrix_value (); for (octave_idx_type vrow = 0; vrow < N; vrow++) for (octave_idx_type vcol = 0; vcol < N; vcol++) PD[vrow+vcol*N] = vbd (vrow, vcol); // Don't know what my mistake is but the following code line never // worked for me (ie. the solver crashes Octave if it is used) // PD = vbd.fortran_vec (); return (true); } octave_idx_type odepkg_mebdfdae_massfcn (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* MASBND, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, GCC_ATTR_UNUSED const octave_idx_type& IERR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = 0.0; // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (0.0); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evalmassode (vmebdfdaemass, vmebdfdaemassstate, vt, vy, vmebdfdaeextarg); Matrix vam = vout.matrix_value (); for (octave_idx_type vrow = 0; vrow < N; vrow++) for (octave_idx_type vcol = 0; vcol < N; vcol++) { AM[vrow+vcol*N] = vam (vrow, vcol); } return (true); } octave_idx_type odepkg_mebdfdae_error (octave_idx_type verr) { switch (verr) { case 0: break; // Everything is fine case -1: error_with_id ("OdePkg:InternalError", "Integration was halted after failing to pass one error test (error \ occured in \"mebdfi\" core solver function with error number \"%d\")", verr); break; case -2: error_with_id ("OdePkg:InternalError", "Integration was halted after failing to pass a repeated error test \ after a successful initialisation step or because of an invalid option \ in RelTol or AbsTol (error occured in \"mebdfi\" core solver function with \ error number \"%d\")", verr); break; case -3: error_with_id ("OdePkg:InternalError", "Integration was halted after failing to achieve a corrector \ convergence even after reducing the step size h by a factor of 1e-10 \ (error occured in \"mebdfi\" core solver function with error number \ \"%d\")", verr); break; case -4: error_with_id ("OdePkg:InternalError", "Immediate halt because of illegal number or illegal values of input \ arguments (error occured in the \"mebdfi\" core solver function with \ error number \"%d\")", verr); break; case -5: error_with_id ("OdePkg:InternalError", "Idid was -1 on input (error occured in \"mebdfi\" core solver function \ with error number \"%d\")", verr); break; case -6: error_with_id ("OdePkg:InternalError", "Maximum number of allowed integration steps exceeded (error occured in \ \"mebdfi\" core solver function with error number \"%d\")", verr); break; case -7: error_with_id ("OdePkg:InternalError", "Stepsize grew too small (error occured in \"mebdfi\" core solver \ function with error number \"%d\")", verr); break; case -11: error_with_id ("OdePkg:InternalError", "Insufficient real workspace for the integration (error occured in \ \"mebdfi\" core solver function with error number \"%d\")", verr); break; case -12: error_with_id ("OdePkg:InternalError", "Insufficient integer workspace for the integration (error occured in \ \"mebdfi\" core solver function with error number \"%d\")", verr); break; default: error_with_id ("OdePkg:InternalError", "Unknown error (error occured in \"mebdfi\" core solver function with \ error number \"%d\")", verr); break; } return (true); } // PKG_ADD: autoload ("odebda", "dldsolver.oct"); DEFUN_DLD (odebda, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Command} {[@var{}] =} odebda (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{sol}] =} odebda (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odebda (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ \n\ This function file can be used to solve a set of stiff ordinary differential equations (ODEs) and stiff differential algebraic equations (DAEs). This function file is a wrapper file that uses Jeff Cash's Fortran solver @file{mebdfdae.f}.\n\ \n\ If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.\n\ \n\ If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.\n\ \n\ If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.\n\ \n\ For example,\n\ @example\n\ function y = odepkg_equations_lorenz (t, x)\n\ y = [10 * (x(2) - x(1));\n\ x(1) * (28 - x(3));\n\ x(1) * x(2) - 8/3 * x(3)];\n\ endfunction\n\ \n\ vopt = odeset (\"InitialStep\", 1e-3, \"MaxStep\", 1e-1, \\\n\ \"OutputFcn\", @@odephas3, \"Refine\", 5);\n\ odebda (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt);\n\ @end example\n\ @end deftypefn\n\ \n\ @seealso{odepkg}") { octave_idx_type nargin = args.length (); // The number of input arguments octave_value_list vretval; // The cell array of return args octave_scalar_map vodeopt; // The OdePkg options structure // Check number and types of all the input arguments if (nargin < 3) { print_usage (); return (vretval); } // If args(0)==function_handle is valid then set the vmebdfdaeodefun // variable that has been defined "static" before if (!args(0).is_function_handle () && !args(0).is_inline_function ()) { error_with_id ("OdePkg:InvalidArgument", "First input argument must be a valid function handle"); return (vretval); } else // We store the args(0) argument in the static variable vmebdfdaeodefun vmebdfdaeodefun = args(0); // Check if the second input argument is a valid vector describing // the time window of solving, it may be of length 2 or longer if (args(1).is_scalar_type () || !odepkg_auxiliary_isvector (args(1))) { error_with_id ("OdePkg:InvalidArgument", "Second input argument must be a valid vector"); return (vretval); } // Check if the third input argument is a valid vector describing // the initial values of the variables of the IDEs if (!odepkg_auxiliary_isvector (args(2))) { error_with_id ("OdePkg:InvalidArgument", "Third input argument must be a valid vector"); return (vretval); } // Check if there are further input arguments ie. the options // structure and/or arguments that need to be passed to the // OutputFcn, Events and/or Jacobian etc. if (nargin >= 4) { // Fourth input argument != OdePkg option, need a default structure if (!args(3).is_map ()) { octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure for (octave_idx_type vcnt = 3; vcnt < nargin; vcnt++) vmebdfdaeextarg(vcnt-3) = args(vcnt); // Save arguments in vmebdfdaeextarg } // Fourth input argument == OdePkg option, extra input args given too else if (nargin > 4) { octave_value_list varin; varin(0) = args(3); varin(1) = "odebda"; octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create structure from args(4) for (octave_idx_type vcnt = 4; vcnt < nargin; vcnt++) vmebdfdaeextarg(vcnt-4) = args(vcnt); // Save extra arguments } // Fourth input argument == OdePkg option, no extra input args given else { octave_value_list varin; varin(0) = args(3); varin(1) = "odebda"; // Check structure octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } } // if (nargin >= 4) else { // if nargin == 3, everything else has been checked before octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } /* Start PREPROCESSING, ie. check which options have been set and * print warnings if there are options that can't be handled by this * solver or have not been implemented yet *******************************************************************/ // Implementation of the option RelTol has been finished, this // option can be set by the user to another value than default value octave_value vreltol = vodeopt.contents ("RelTol"); if (vreltol.is_empty ()) { vreltol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"RelTol\" not set, new value %3.1e is used", vreltol.double_value ()); } // vreltol.print (octave_stdout, true); return (vretval); if (!vreltol.is_scalar_type ()) { if (vreltol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"RelTol\" must be the same as the number of equations"); return (vretval); } } // Implementation of the option AbsTol has been finished, this // option can be set by the user to another value than default value octave_value vabstol = vodeopt.contents ("AbsTol"); if (vabstol.is_empty ()) { vabstol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"AbsTol\" not set, new value %3.1e is used", vabstol.double_value ()); } // vabstol.print (octave_stdout, true); return (vretval); if (!vabstol.is_scalar_type ()) { if (vabstol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"AbsTol\" must be the same as the number of equations"); return (vretval); } } // Setting the tolerance type that depends on the types (scalar or // vector) of the options RelTol and AbsTol octave_idx_type vitol = 0; if (vreltol.is_scalar_type () && vabstol.is_scalar_type ()) vitol = 2; else if (vreltol.is_scalar_type () && !vabstol.is_scalar_type ()) vitol = 3; else if (!vreltol.is_scalar_type () && vabstol.is_scalar_type ()) vitol = 4; else if (!vreltol.is_scalar_type () && !vabstol.is_scalar_type ()) vitol = 5; // The option NormControl will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnorm = vodeopt.contents ("NormControl"); if (!vnorm.is_empty ()) if (vnorm.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"NormControl\" will be ignored by this solver"); // The option NonNegative will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnneg = vodeopt.contents ("NonNegative"); if (!vnneg.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NonNegative\" will be ignored by this solver"); // Implementation of the option OutputFcn has been finished, this // option can be set by the user to another value than default value octave_value vplot = vodeopt.contents ("OutputFcn"); if (vplot.is_empty () && nargout == 0) vplot = "odeplot"; // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value octave_value voutsel = vodeopt.contents ("OutputSel"); // The option Refine will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vrefine = vodeopt.contents ("Refine"); if (vrefine.int_value () != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"Refine\" will be ignored by this solver"); // Implementation of the option Stats has been finished, this option // can be set by the user to another value than default value octave_value vstats = vodeopt.contents ("Stats"); // Implementation of the option InitialStep has been finished, this // option can be set by the user to another value than default value octave_value vinitstep = vodeopt.contents ("InitialStep"); if (args(1).length () > 2) { if (!vinitstep.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" will be ignored if fixed time stamps are given"); vinitstep = args(1).vector_value ()(1); } if (vinitstep.is_empty ()) { vinitstep = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" not set, new value %3.1e is used", vinitstep.double_value ()); } // Implementation of the option MaxStep has been finished, this // option can be set by the user to another value than default value octave_value vmaxstep = vodeopt.contents ("MaxStep"); if (vmaxstep.is_empty () && args(1).length () == 2) { vmaxstep = (args(1).vector_value ()(1) - args(1).vector_value ()(0)) / 12.5; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxStep\" not set, new value %3.1e is used", vmaxstep.double_value ()); } // Implementation of the option Events has been finished, this // option can be set by the user to another value than default // value, odepkg_structure_check already checks for a valid value octave_value vevents = vodeopt.contents ("Events"); octave_value_list veveres; // We save the results of Events here // The options 'Jacobian', 'JPattern' and 'Vectorized' octave_value vjac = vodeopt.contents ("Jacobian"); octave_idx_type vmebdfdaejac = 22; // We need to set this if no Jac available if (!vjac.is_empty ()) { vmebdfdaejacfun = vjac; vmebdfdaejac = 21; } // Implementation of the option 'Mass' has been finished, these // options can be set by the user to another value than default vmebdfdaemass = vodeopt.contents ("Mass"); octave_idx_type vmebdfdaemas = 0; if (!vmebdfdaemass.is_empty ()) { vmebdfdaemas = 1; if (vmebdfdaemass.is_function_handle () || vmebdfdaemass.is_inline_function ()) warning_with_id ("OdePkg:InvalidOption", "Option \"Mass\" only supports constant mass matrices M() and not M(t,y)"); } // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option vmebdfdaemassstate = vodeopt.contents ("MStateDependence"); if (!vmebdfdaemassstate.is_empty ()) if (vmebdfdaemassstate.string_value ().compare ("weak") != 0) // 'weak' is default warning_with_id ("OdePkg:InvalidOption", "Option \"MStateDependence\" will be ignored by this solver"); // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmvpat = vodeopt.contents ("MvPattern"); if (!vmvpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MvPattern\" will be ignored by this solver"); // The option MassSingular will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmsing = vodeopt.contents ("MassSingular"); if (!vmsing.is_empty ()) if (vmsing.string_value ().compare ("maybe") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MassSingular\" will be ignored by this solver"); // The option InitialSlope will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vinitslope = vodeopt.contents ("InitialSlope"); if (!vinitslope.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialSlope\" will be ignored by this solver"); // Implementation of the option MaxOrder has been finished, this // option can be set by the user to another value than default value octave_value vmaxder = vodeopt.contents ("MaxOrder"); if (vmaxder.is_empty ()) { vmaxder = 3; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" not set, new value %1d is used", vmaxder.int_value ()); } if (vmaxder.int_value () < 1) { vmaxder = 3; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" is zero, new value %1d is used", vmaxder.int_value ()); } // The option BDF will be ignored because this is a BDF solver octave_value vbdf = vodeopt.contents ("BDF"); if (vbdf.is_string ()) if (vbdf.string_value () != "on") { vbdf = "on"; warning_with_id ("OdePkg:InvalidOption", "Option \"BDF\" set \"off\", new value \"on\" is used"); } // The option NewtonTol and MaxNewtonIterations will be ignored by // this solver, IT NEEDS TO BE CHECKED IF THE FORTRAN CORE SOLVER // CAN HANDLE THESE OPTIONS octave_value vntol = vodeopt.contents ("NewtonTol"); if (!vntol.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NewtonTol\" will be ignored by this solver"); octave_value vmaxnewton = vodeopt.contents ("MaxNewtonIterations"); if (!vmaxnewton.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" will be ignored by this solver"); /* Start MAINPROCESSING, set up all variables that are needed by this * solver and then initialize the solver function and get into the * main integration loop ********************************************************************/ ColumnVector vTIME (args(1).vector_value ()); NDArray vRTOL = vreltol.array_value (); NDArray vATOL = vabstol.array_value (); NDArray vY0 = args(2).array_value (); octave_idx_type N = args(2).length (); double T0 = vTIME(0); double HO = vinitstep.double_value (); double *Y0 = vY0.fortran_vec (); double TOUT = T0 + vinitstep.double_value (); double TEND = vTIME(vTIME.length ()-1); octave_idx_type MF = vmebdfdaejac; octave_idx_type IDID = 1; octave_idx_type LOUT = 6; // Logical output channel "not opened" octave_idx_type LWORK = 42*N+3*N*N+4; OCTAVE_LOCAL_BUFFER (double, WORK, LWORK); for (octave_idx_type vcnt = 0; vcnt < LWORK; vcnt++) WORK[vcnt] = 0.0; octave_idx_type LIWORK = N+14; OCTAVE_LOCAL_BUFFER (octave_idx_type, IWORK, LIWORK); for (octave_idx_type vcnt = 0; vcnt < LIWORK; vcnt++) IWORK[vcnt] = 0; octave_idx_type MBND[4] = {N, N, N, N}; octave_idx_type MASBND[4] = {0, N, 0, N}; if (!vmebdfdaemass.is_empty ()) MASBND[0] = 1; octave_idx_type MAXDER = vmaxder.int_value (); octave_idx_type ITOL = vitol; double *RTOL = vRTOL.fortran_vec (); double *ATOL = vATOL.fortran_vec (); double RPAR[1] = {0.0}; octave_idx_type IPAR[1] = {0}; octave_idx_type IERR = 0; IWORK[0] = N; // Number of variables of index 1 IWORK[1] = 0; // Number of variables of index 2 IWORK[2] = 0; // Number of variables of index 3 IWORK[13] = 1000000; // The maximum number of steps allowed // Check if the user has set some of the options "OutputFcn", "Events" // etc. and initialize the plot, events and the solstore functions octave_value vtim (T0); octave_value vsol (vY0); odepkg_auxiliary_solstore (vtim, vsol, 0); if (!vplot.is_empty ()) odepkg_auxiliary_evalplotfun (vplot, voutsel, args(1), args(2), vmebdfdaeextarg, 0); if (!vevents.is_empty ()) odepkg_auxiliary_evaleventfun (vevents, vtim, args(2), vmebdfdaeextarg, 0); // We are calling the core solver here to intialize all variables F77_XFCN (mebdf, MEBDF, // Keep 5 arguments per line here (N, T0, HO, Y0, TOUT, TEND, MF, IDID, LOUT, LWORK, WORK, LIWORK, IWORK, MBND, MASBND, MAXDER, ITOL, RTOL, ATOL, RPAR, IPAR, odepkg_mebdfdae_usrfcn, odepkg_mebdfdae_jacfcn, odepkg_mebdfdae_massfcn, IERR)); if (IDID < 0) { odepkg_mebdfdae_error (IDID); return (vretval); } // We need that variable in the following loop after calling the // core solver function and before calling the plot function ColumnVector vcres(N); if (vTIME.length () == 2) { // Before we are entering the solver loop replace the first time // stamp value with FirstStep = (InitTime - InitStep) TOUT = TOUT - vinitstep.double_value (); while (TOUT < TEND) { // Calculate the next time stamp for that an solution is required TOUT = TOUT + vmaxstep.double_value (); TOUT = (TOUT > TEND ? TEND : TOUT); // Call the core Fortran solver again and again and check if an // exception is encountered, set IDID = 2 every time to hit // every point of time that is required exactly IDID = 2; F77_XFCN (mebdf, MEBDF, // Keep 5 arguments per line here (N, T0, HO, Y0, TOUT, TEND, MF, IDID, LOUT, LWORK, WORK, LIWORK, IWORK, MBND, MASBND, MAXDER, ITOL, RTOL, ATOL, RPAR, IPAR, odepkg_mebdfdae_usrfcn, odepkg_mebdfdae_jacfcn, odepkg_mebdfdae_massfcn, IERR)); if (IDID < 0) { odepkg_mebdfdae_error (IDID); return (vretval); } // This call of the Fortran solver has been successful so let us // plot the output and save the results for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) vcres(vcnt) = Y0[vcnt]; vsol = vcres; vtim = TOUT; if (!vevents.is_empty ()) { veveres = odepkg_auxiliary_evaleventfun (vevents, vtim, vsol, vmebdfdaeextarg, 1); if (!veveres(0).cell_value ()(0).is_empty ()) if (veveres(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = veveres(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = veveres(0).cell_value ()(3).matrix_value (); vtim = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vsol = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); TOUT = TEND; // let's get out here, the Events function told us to finish } } if (!vplot.is_empty ()) { if (odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vmebdfdaeextarg, 1)) { error ("Missing error message implementation"); return (vretval); } } odepkg_auxiliary_solstore (vtim, vsol, 1); } } else { // if (vTIME.length () > 2) we have all the time values needed volatile octave_idx_type vtimecnt = 1; octave_idx_type vtimelen = vTIME.length (); while (vtimecnt < vtimelen) { vtimecnt++; TOUT = vTIME(vtimecnt-1); // Call the core Fortran solver again and again and check if an // exception is encountered, set IDID = 2 every time to hit // every point of time that is required exactly IDID = 2; F77_XFCN (mebdf, MEBDF, // Keep 5 arguments per line here (N, T0, HO, Y0, TOUT, TEND, MF, IDID, LOUT, LWORK, WORK, LIWORK, IWORK, MBND, MASBND, MAXDER, ITOL, RTOL, ATOL, RPAR, IPAR, odepkg_mebdfdae_usrfcn, odepkg_mebdfdae_jacfcn, odepkg_mebdfdae_massfcn, IERR)); if (IDID < 0) { odepkg_mebdfdae_error (IDID); return (vretval); } // The last call of the Fortran solver has been successful so // let us plot the output and save the results for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) vcres(vcnt) = Y0[vcnt]; vsol = vcres; vtim = TOUT; if (!vevents.is_empty ()) { veveres = odepkg_auxiliary_evaleventfun (vevents, vtim, vsol, vmebdfdaeextarg, 1); if (!veveres(0).cell_value ()(0).is_empty ()) if (veveres(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = veveres(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = veveres(0).cell_value ()(3).matrix_value (); vtim = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vsol = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); vtimecnt = vtimelen; // let's get out here, the Events function told us to finish } } if (!vplot.is_empty ()) { if (odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vmebdfdaeextarg, 1)) { error ("Missing error message implementation"); return (vretval); } } odepkg_auxiliary_solstore (vtim, vsol, 1); } } /* Start POSTPROCESSING, check how many arguments should be returned * to the caller and check which extra arguments have to be set *******************************************************************/ // Set up values that come from the last Fortran call and that are // needed to call the OdePkg output function one last time again for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) vcres(vcnt) = Y0[vcnt]; vsol = vcres; vtim = TOUT; if (!vevents.is_empty ()) odepkg_auxiliary_evaleventfun (vevents, vtim, vsol, vmebdfdaeextarg, 2); if (!vplot.is_empty ()) odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vmebdfdaeextarg, 2); // Return the results that have been stored in the // odepkg_auxiliary_solstore function octave_value vtres, vyres; odepkg_auxiliary_solstore (vtres, vyres, 2); // odepkg_auxiliary_solstore (vtres, vyres, voutsel, 100); // Get the stats information as an octave_scalar_map if the option 'Stats' // has been set with odeset // "nsteps", "nfailed", "nfevals", "npds", "ndecomps", "nlinsols" octave_value_list vstatinput; vstatinput(0) = IWORK[4]; vstatinput(1) = IWORK[5]; vstatinput(2) = IWORK[6]; vstatinput(3) = IWORK[7]; vstatinput(4) = IWORK[8]; vstatinput(5) = IWORK[9]; octave_value vstatinfo; if (vstats.string_value () == "on" && (nargout == 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, false); else if (vstats.string_value () == "on" && (nargout != 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, true); // Set up output arguments that depends on how many output arguments // are desired by the caller if (nargout == 1) { octave_scalar_map vretmap; vretmap.assign ("x", vtres); vretmap.assign ("y", vyres); vretmap.assign ("solver", "odebda"); if (vstats.string_value () == "on") vretmap.assign ("stats", vstatinfo); if (!vevents.is_empty ()) { vretmap.assign ("ie", veveres(0).cell_value ()(1)); vretmap.assign ("xe", veveres(0).cell_value ()(2)); vretmap.assign ("ye", veveres(0).cell_value ()(3)); } vretval(0) = octave_value (vretmap); } else if (nargout == 2) { vretval(0) = vtres; vretval(1) = vyres; } else if (nargout == 5) { Matrix vempty; // prepare an empty matrix vretval(0) = vtres; vretval(1) = vyres; vretval(2) = vempty; vretval(3) = vempty; vretval(4) = vempty; if (!vevents.is_empty ()) { vretval(2) = veveres(0).cell_value ()(2); vretval(3) = veveres(0).cell_value ()(3); vretval(4) = veveres(0).cell_value ()(1); } } return (vretval); } /* %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference solut %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (size (vt) != [2, 1] && size (vt) != [1, 2]) %! error ('"fout" step "init"'); %! end %! elseif (isempty (vflag)) %! if (size (vt) ~= [1, 1]) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (size (vt) ~= [1, 1]) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = odebda (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = odebda (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = odebda (@flor, [0 25], 1); %!test %# one output argument %! vsol = odebda (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'odebda'); %!test %# two output arguments %! [vt, vy] = odebda (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = odebda (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = odebda (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = odebda (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = odebda (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-5); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-5); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = odebda (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = odebda (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = odebda (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'RelTol', 1e-10); %! vsol = odebda (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.63352, -0.98874, -1.93801], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = odebda (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.63352, -0.98874, -1.93801], 1e-1); %! warning ('on', 'OdePkg:HideWarning'); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %!test %# BDF option set "off" %! vopt = odeset ('BDF', 'off'); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set NewtonTol option to something else than default %! vopt = odeset ('NewtonTol', 1e-3); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set MaxNewtonIterations option to something else than default %! vopt = odeset ('MaxNewtonIterations', 2); %! vsol = odebda (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! warning ('on', 'OdePkg:InvalidOption'); */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_octsolver_mebdfi.cc0000644000000000000000000013575712526637474017142 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ /* Compile this file manually and run some tests with the following command bash:$ mkoctfile -v -Wall -W -Wshadow odepkg_octsolver_mebdfi.cc \ odepkg_auxiliary_functions.cc cash/mebdfi.f -o odebdi.oct octave> octave --quiet --eval "autoload ('odebdi', [pwd, '/odebdi.oct']); \ test 'odepkg_octsolver_mebdfi.cc'" If you have installed 'gfortran' or 'g95' on your computer then use the FFLAGS=-fno-automatic option because otherwise the solver function will be broken, eg. bash:$ FFLAGS="-fno-automatic ${FFLAGS}" mkoctfile -v -Wall -W \ -Wshadow odepkg_octsolver_mebdfi.cc odepkg_auxiliary_functions.cc \ cash/mebdfi.f -o odebdi.oct octave> octave --quiet --eval "autoload ('odebdi', [pwd, '/odebdi.oct']); \ test 'odepkg_octsolver_mebdfi.cc'" */ #include #include #include #include #include #include "odepkg_auxiliary_functions.h" /* -*- texinfo -*- * @subsection Source File @file{odepkg_octsolver_mebdfi.cc} * * @deftp {Typedef} {octave_idx_type (*odepkg_mebdfi_usrtype)} * This @code{typedef} is used to define the input and output arguments of the user function for the IDE problem that is further needed by the Fortran core solver @code{mebdfi}. The implementation of this @code{typedef} is * * @example * typedef octave_idx_type (*odepkg_mebdfi_usrtype) * (const octave_idx_type& N, const double& T, const double* Y, * double* DELTA, const double* YPRIME, const octave_idx_type* IPAR, * const double* RPAR, const octave_idx_type& IERR); * @end example * @end deftp */ typedef octave_idx_type (*odepkg_mebdfi_usrtype) (const octave_idx_type& N, const double& T, const double* Y, double* DELTA, const double* YPRIME, const octave_idx_type* IPAR, const double* RPAR, const octave_idx_type& IERR); /* -*- texinfo -*- * @deftp {Typedef} {octave_idx_type (*odepkg_mebdfi_jactype)} * * This @code{typedef} is used to define the input and output arguments of the @code{Jacobian} function for the IDE problem that is further needed by the Fortran core solver @code{mebdfi}. The implementation of this @code{typedef} is * * @example * typedef octave_idx_type (*odepkg_mebdfi_jactype) * (const double& T, const double* Y, double* PD, * const octave_idx_type& N, const double* YPRIME, * const octave_idx_type* MBND, const double& CON, * const octave_idx_type* IPAR, const double* RPAR, * const octave_idx_type& IERR); * @end example * @end deftp */ typedef octave_idx_type (*odepkg_mebdfi_jactype) // 3 arguments per line (const double& T, const double* Y, double* PD, const octave_idx_type& N, const double* YPRIME, const octave_idx_type* MBND, const double& CON, const octave_idx_type* IPAR, const double* RPAR, const octave_idx_type& IERR); extern "C" { /* -*- texinfo -*- * @deftp {Prototype} {F77_RET_T F77_FUNC (mebdfi, MEBDFI)} (const octave_idx_type& N, const double& T0, const double& HO, const double* Y0, const double* YPRIME, const double& TOUT, const double& TEND, const octave_idx_type& MF, octave_idx_type& IDID, const octave_idx_type& LOUT, const octave_idx_type& LWORK, const double* WORK, const octave_idx_type& LIWORK, const octave_idx_type* IWORK, const octave_idx_type* MBND, const octave_idx_type& MAXDER, const octave_idx_type& ITOL, const double* RTOL, const double* ATOL, const double* RPAR, const octave_idx_type* IPAR, odepkg_mebdfi_jactype, odepkg_mebdfi_usrtype, octave_idx_type& IERR); * * The prototype @code{F77_FUNC (mebdfi, MEBDFI)} is used to represent the information about the Fortran core solver @code{mebdfi} that is defined in the Fortran source file @file{mebdfi.f} (cf. the Fortran source file @file{mebdfi.f} for further details). * @end deftp */ F77_RET_T F77_FUNC (mebdfi, MEBDFI) // 3 arguments per line (const octave_idx_type& N, const double& T0, const double& HO, const double* Y0, const double* YPRIME, const double& TOUT, const double& TEND, const octave_idx_type& MF, octave_idx_type& IDID, const octave_idx_type& LOUT, const octave_idx_type& LWORK, const double* WORK, const octave_idx_type& LIWORK, const octave_idx_type* IWORK, const octave_idx_type* MBND, const octave_idx_type& MAXDER, const octave_idx_type& ITOL, const double* RTOL, const double* ATOL, const double* RPAR, const octave_idx_type* IPAR, odepkg_mebdfi_jactype, odepkg_mebdfi_usrtype, octave_idx_type& IERR); } /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value_list} {vmebdfiextarg} * * This static variable is used to store the extra arguments that are needed by some or by all of the @code{OutputFcn}, the @code{Jacobian} function and the @code{Events} function while solving the IDE problem. * @end deftypevr */ static octave_value_list vmebdfiextarg; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {*vmebdfiodefun} * * This static variable is used to store the value for the user function that defines the set of IDEs. * @end deftypevr */ static octave_value vmebdfiodefun; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vmebdfijacfun} * * This static variable is used to store the value for the @code{Jacobian} function or the @code{Jacobian} matrix that is needed if Jacobian evaluation should be performed. * @end deftypevr */ static octave_value vmebdfijacfun; /* -*- texinfo -*- * @deftypefn {Function} {octave_idx_type} {odepkg_mebdfi_usrfcn} (const octave_idx_type& N, const double& T, const double* Y, double* DELTA, const double* YPRIME, GCC_ATTR_UNUSED const octave_idx_type* IPAR, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type& IERR) * * Return @code{true} if the evaluation of the user function was successful, return @code{false} otherwise. This function is directly called from the Fortran core solver @code{mebdfi}. The input arguments of this function are * * @itemize @minus * @item @var{N}: The number of equations that are defined for the IDE--problem * @item @var{T}: The actual time stamp for the current function evaluation * @item @var{Y}: The function values from the last successful integration step of length @var{N} * @item @var{DELTA}: The residual vector that needs to be calculated of length @var{N} * @item @var{YPRIME}: The derivative values from the last successful integration step of length @var{N} * @item @var{IPAR}: The integer parameters that are passed to the user function (unused) * @item @var{RPAR}: The real parameters that are passed to the user function (unused) * @item @var{IERR}: The error flag that can be set on each evaluation (unused) * @end itemize * @end deftypefn */ octave_idx_type odepkg_mebdfi_usrfcn (const octave_idx_type& N, const double& T, const double* Y, double* DELTA, const double* YPRIME, GCC_ATTR_UNUSED const octave_idx_type* IPAR, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type& IERR) { // Copy the values that come from the Fortran function element wise, // otherwise Octave will crash if these variables will be freed ColumnVector A(N), APRIME(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { A(vcnt) = Y[vcnt]; APRIME(vcnt) = YPRIME[vcnt]; } // Fill the variable for the input arguments before evaluating the // function that keeps the set of implicit differential equations octave_value_list varin; varin(0) = T; varin(1) = A; varin(2) = APRIME; for (octave_idx_type vcnt = 0; vcnt < vmebdfiextarg.length (); vcnt++) varin(vcnt+3) = vmebdfiextarg(vcnt); octave_value_list vout = feval (vmebdfiodefun.function_value (), varin, 1); // Return the results from the function evaluation to the Fortran // solver, again copy them and don't just create a Fortran vector ColumnVector vcol = vout(0).column_vector_value (); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) DELTA[vcnt] = vcol(vcnt); return (true); } /* -*- texinfo -*- * @deftypefn {Function} {octave_idx_type} {odepkg_mebdfi_jacfcn} (const double& T, const double* Y, double* PD, const octave_idx_type& N, const double* YPRIME, GCC_ATTR_UNUSED const octave_idx_type* MBND, const double& CON, GCC_ATTR_UNUSED const octave_idx_type* IPAR, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type& IERR) * * Return @code{true} if the evaluation of the Jacobian function (that is defined for a special IDE problem in Octave) was successful, otherwise return @code{false}. This function is directly called from the Fortran core solver @code{mebdfi}. The input arguments of this function are * * @itemize @minus * @item @var{T}: The actual time stamp for the current function evaluation * @item @var{Y}: The function values from the last successful integration step of length @var{N} * @item @var{PD}: The values of partial derivatives of the Jacobian matrix of size @var{N} * @item @var{N}: The number of equations that are defined for the IDE--problem * @item @var{YPRIME}: The derivative values from the last successful integration step of length @var{N} * @item @var{MBND}: A vector of size 4 describing the sizes of a banded Jacobian (unused) * @item @var{CON}: A constant value that is set before the evaluation of the Jacobian function * @item @var{IPAR}: The integer parameters that are passed to the user function (unused) * @item @var{RPAR}: The real parameters that are passed to the user function (unused) * @item @var{IERR}: The error flag that can be set on each evaluation (unused) * @end itemize * @end deftypefn */ octave_idx_type odepkg_mebdfi_jacfcn (const double& T, const double* Y, double* PD, const octave_idx_type& N, const double* YPRIME, GCC_ATTR_UNUSED const octave_idx_type* MBND, const double& CON, GCC_ATTR_UNUSED const octave_idx_type* IPAR, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type& IERR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N), APRIME(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { A(vcnt) = Y[vcnt]; APRIME(vcnt) = YPRIME[vcnt]; } // Set the values that are needed as input arguments before calling // the Jacobian function octave_value vt = octave_value (T); octave_value vy = octave_value (A); octave_value vdy = octave_value (APRIME); octave_value_list vout = odepkg_auxiliary_evaljacide (vmebdfijacfun, vt, vy, vdy, vmebdfiextarg); // Computes the NxN iteration matrix or partial derivatives for the // Fortran solver of the form PD=DG/DY+1/CON*(DG/DY') octave_value vbdov = vout(0) + 1/CON * vout(1); Matrix vbd = vbdov.matrix_value (); for (octave_idx_type vrow = 0; vrow < N; vrow++) for (octave_idx_type vcol = 0; vcol < N; vcol++) PD[vrow+vcol*N] = vbd (vrow, vcol); // Don't know what my mistake is but the following code line never // worked for me (ie. the solver crashes Octave if it is used) // PD = vbd.fortran_vec (); return (true); } /* -*- texinfo -*- * @deftypefn {Function} octave_idx_type odepkg_mebdfi_error (octave_idx_type verr) * TODO * @end deftypefn */ octave_idx_type odepkg_mebdfi_error (octave_idx_type verr) { switch (verr) { case 0: break; // Everything is fine case -1: error_with_id ("OdePkg:InternalError", "Integration was halted after failing to pass one error test (error \ occured in \"mebdfi\" core solver function with error number \"%d\")", verr); break; case -2: error_with_id ("OdePkg:InternalError", "Integration was halted after failing to pass a repeated error test \ after a successful initialisation step or because of an invalid option \ in RelTol or AbsTol (error occured in \"mebdfi\" core solver function with \ error number \"%d\")", verr); break; case -3: error_with_id ("OdePkg:InternalError", "Integration was halted after failing to achieve a corrector \ convergence even after reducing the step size h by a factor of 1e-10 \ (error occured in \"mebdfi\" core solver function with error number \ \"%d\")", verr); break; case -4: error_with_id ("OdePkg:InternalError", "Immediate halt because of illegal number or illegal values of input \ arguments (error occured in the \"mebdfi\" core solver function with \ error number \"%d\")", verr); break; case -5: error_with_id ("OdePkg:InternalError", "Idid was -1 on input (error occured in \"mebdfi\" core solver function \ with error number \"%d\")", verr); break; case -6: error_with_id ("OdePkg:InternalError", "Maximum number of allowed integration steps exceeded (error occured in \ \"mebdfi\" core solver function with error number \"%d\")", verr); break; case -7: error_with_id ("OdePkg:InternalError", "Stepsize grew too small (error occured in \"mebdfi\" core solver \ function with error number \"%d\")", verr); break; case -11: error_with_id ("OdePkg:InternalError", "Insufficient real workspace for the integration (error occured in \ \"mebdfi\" core solver function with error number \"%d\")", verr); break; case -12: error_with_id ("OdePkg:InternalError", "Insufficient integer workspace for the integration (error occured in \ \"mebdfi\" core solver function with error number \"%d\")", verr); break; default: error_with_id ("OdePkg:InternalError", "Unknown error (error occured in \"mebdfi\" core solver function with \ error number \"%d\")", verr); break; } return (true); } /* -*- texinfo -*- * @deftp {Function} {DEFUN_DLD} (odebdi, args, nargout, 'help string') * @findex odebdi * * Return the results of the solving process of the IDE problem from the Fortran core solver @code{mebdfi} to the caller function (cf. @command{help odebdi} within Octave for further details about this function). the Argument @var{odebdi} is the name of the function that can be used in Octave and @var{'help string'} is the help text that is displayed if the command @command{help odebdi} is called from Octave. The input arguments of this function are * @itemize @minus * @item @var{args}: The input arguments in form of an @code{octave_value_list} * @item @var{nargout}: The number of output arguments that are required * @end itemize * @end deftp */ // PKG_ADD: autoload ("odebdi", "dldsolver.oct"); DEFUN_DLD (odebdi, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Command} {[@var{}] =} odebdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}])\n\ @deftypefnx {Command} {[@var{sol}] =} odebdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}])\n\ @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odebdi (@var{@@fun}, @var{slot}, @var{y0}, @var{dy0}, [@var{opt}], [@var{P1}, @var{P2}, @dots{}])\n\ \n\ This function file can be used to solve a set of stiff implicit differential equations (IDEs). This function file is a wrapper file that uses Jeff Cash's Fortran solver @file{mebdfi.f}.\n\ \n\ If this function is called with no return argument then plot the solution over time in a figure window while solving the set of IDEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{y0} is a double vector that defines the initial values of the states, @var{dy0} is a double vector that defines the initial values of the derivatives, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.\n\ \n\ If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of IDEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.\n\ \n\ If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.\n\ \n\ For example,\n\ @example\n\ function res = odepkg_equations_ilorenz (t, y, yd)\n\ res = [10 * (y(2) - y(1)) - yd(1);\n\ y(1) * (28 - y(3)) - yd(2);\n\ y(1) * y(2) - 8/3 * y(3) - yd(3)];\n\ endfunction\n\ \n\ vopt = odeset (\"InitialStep\", 1e-3, \"MaxStep\", 1e-1, \\\n\ \"OutputFcn\", @@odephas3, \"Refine\", 5);\n\ odebdi (@@odepkg_equations_ilorenz, [0, 25], [3 15 1], \\\n\ [120 81 42.333333], vopt);\n\ @end example\n\ @end deftypefn\n\ \n\ @seealso{odepkg}") { octave_idx_type nargin = args.length (); // The number of input arguments octave_value_list vretval; // The cell array of return args octave_scalar_map vodeopt; // The OdePkg options structure // Check number and types of all the input arguments if (nargin < 4) { print_usage (); return (vretval); } // If args(0)==function_handle is valid then set the vmebdfiodefun // variable that has been defined "static" before if (!args(0).is_function_handle () && !args(0).is_inline_function ()) { error_with_id ("OdePkg:InvalidArgument", "First input argument must be a valid function handle"); return (vretval); } else // We store the args(0) argument in the static variable vmebdfiodefun vmebdfiodefun = args(0); // Check if the second input argument is a valid vector describing // the time window of solving, it may be of length 2 or longer if (args(1).is_scalar_type () || !odepkg_auxiliary_isvector (args(1))) { error_with_id ("OdePkg:InvalidArgument", "Second input argument must be a valid vector"); return (vretval); } // Check if the third input argument is a valid vector describing // the initial values of the variables of the IDEs if (!odepkg_auxiliary_isvector (args(2))) { error_with_id ("OdePkg:InvalidArgument", "Third input argument must be a valid vector"); return (vretval); } // Check if the fourth input argument is a valid vector describing // the initial values of the derivatives of the differential equations if (!odepkg_auxiliary_isvector (args(3))) { error_with_id ("OdePkg:InvalidArgument", "Fourth input argument must be a valid vector"); return (vretval); } // Check if the third and the fourth input argument (check for // vector already was successful before) have the same length if (args(2).length () != args(3).length ()) { error_with_id ("OdePkg:InvalidArgument", "Third and fourth input argument must have the same length"); return (vretval); } // Check if there are further input arguments ie. the options // structure and/or arguments that need to be passed to the // OutputFcn, Events and/or Jacobian etc. if (nargin >= 5) { // Fifth input argument != OdePkg option, need a default structure if (!args(4).is_map ()) { octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure for (octave_idx_type vcnt = 4; vcnt < nargin; vcnt++) vmebdfiextarg(vcnt-4) = args(vcnt); // Save arguments in vmebdfiextarg } // Fifth input argument == OdePkg option, extra input args given too else if (nargin > 5) { octave_value_list varin; varin(0) = args(4); varin(1) = "odebdi"; octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create structure of args(4) for (octave_idx_type vcnt = 5; vcnt < nargin; vcnt++) vmebdfiextarg(vcnt-5) = args(vcnt); // Save extra arguments } // Fifth input argument == OdePkg option, no extra input args given else { octave_value_list varin; varin(0) = args(4); varin(1) = "odebdi"; // Check structure octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } } // if (nargin >= 5) else { // if nargin == 4, everything else has been checked before octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } /* Start PREPROCESSING, ie. check which options have been set and * print warnings if there are options that can't be handled by this * solver or have not been implemented yet *******************************************************************/ // Implementation of the option RelTol has been finished, this // option can be set by the user to another value than default value octave_value vreltol = vodeopt.contents ("RelTol"); if (vreltol.is_empty ()) { vreltol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"RelTol\" not set, new value %3.1e is used", vreltol.double_value ()); } // vreltol.print (octave_stdout, true); return (vretval); if (!vreltol.is_scalar_type ()) { if (vreltol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"RelTol\" must be the same as the number of equations"); return (vretval); } } // Implementation of the option AbsTol has been finished, this // option can be set by the user to another value than default value octave_value vabstol = vodeopt.contents ("AbsTol"); if (vabstol.is_empty ()) { vabstol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"AbsTol\" not set, new value %3.1e is used", vabstol.double_value ()); } // vabstol.print (octave_stdout, true); return (vretval); if (!vabstol.is_scalar_type ()) { if (vabstol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"AbsTol\" must be the same as the number of equations"); return (vretval); } } // Setting the tolerance type that depends on the types (scalar or // vector) of the options RelTol and AbsTol octave_idx_type vitol = 0; if (vreltol.is_scalar_type () && vabstol.is_scalar_type ()) vitol = 2; else if (vreltol.is_scalar_type () && !vabstol.is_scalar_type ()) vitol = 3; else if (!vreltol.is_scalar_type () && vabstol.is_scalar_type ()) vitol = 4; else if (!vreltol.is_scalar_type () && !vabstol.is_scalar_type ()) vitol = 5; // The option NormControl will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnorm = vodeopt.contents ("NormControl"); if (!vnorm.is_empty ()) if (vnorm.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"NormControl\" will be ignored by this solver"); // The option NonNegative will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnneg = vodeopt.contents ("NonNegative"); if (!vnneg.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NonNegative\" will be ignored by this solver"); // Implementation of the option OutputFcn has been finished, this // option can be set by the user to another value than default value octave_value vplot = vodeopt.contents ("OutputFcn"); if (vplot.is_empty () && nargout == 0) vplot = "odeplot"; // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value octave_value voutsel = vodeopt.contents ("OutputSel"); // The option Refine will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vrefine = vodeopt.contents ("Refine"); if (vrefine.int_value () != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"Refine\" will be ignored by this solver"); // Implementation of the option Stats has been finished, this option // can be set by the user to another value than default value octave_value vstats = vodeopt.contents ("Stats"); // Implementation of the option InitialStep has been finished, this // option can be set by the user to another value than default value octave_value vinitstep = vodeopt.contents ("InitialStep"); if (args(1).length () > 2) { if (!vinitstep.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" will be ignored if fixed time stamps are given"); vinitstep = args(1).vector_value ()(1); } if (vinitstep.is_empty ()) { vinitstep = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" not set, new value %3.1e is used", vinitstep.double_value ()); } // Implementation of the option MaxStep has been finished, this // option can be set by the user to another value than default value octave_value vmaxstep = vodeopt.contents ("MaxStep"); if (vmaxstep.is_empty () && args(1).length () == 2) { vmaxstep = (args(1).vector_value ()(1) - args(1).vector_value ()(0)) / 12.5; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxStep\" not set, new value %3.1e is used", vmaxstep.double_value ()); } // Implementation of the option Events has been finished, this // option can be set by the user to another value than default // value, odepkg_structure_check already checks for a valid value octave_value vevents = vodeopt.contents ("Events"); octave_value_list veveres; // We save the results of Events here // The options 'Jacobian', 'JPattern' and 'Vectorized' octave_value vjac = vodeopt.contents ("Jacobian"); octave_idx_type vmebdfijac = 22; // We need to set this if no Jac available if (!vjac.is_empty ()) { vmebdfijacfun = vjac; vmebdfijac = 21; } // The option Mass will be ignored by this solver. We can't handle // Mass-matrix options with IDE problems octave_value vmass = vodeopt.contents ("Mass"); if (!vmass.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"Mass\" will be ignored by this solver"); // The option MStateDependence will be ignored by this solver. We // can't handle Mass-matrix options with IDE problems octave_value vmst = vodeopt.contents ("MStateDependence"); if (!vmst.is_empty ()) if (vmst.string_value ().compare ("weak") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MStateDependence\" will be ignored by this solver"); // The option MvPattern will be ignored by this solver. We // can't handle Mass-matrix options with IDE problems octave_value vmvpat = vodeopt.contents ("MvPattern"); if (!vmvpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MvPattern\" will be ignored by this solver"); // The option MvPattern will be ignored by this solver. We // can't handle Mass-matrix options with IDE problems octave_value vmsing = vodeopt.contents ("MassSingular"); if (!vmsing.is_empty ()) if (vmsing.string_value ().compare ("maybe") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MassSingular\" will be ignored by this solver"); // The option InitialSlope will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vinitslope = vodeopt.contents ("InitialSlope"); if (!vinitslope.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialSlope\" will be ignored by this solver"); // Implementation of the option MaxOrder has been finished, this // option can be set by the user to another value than default value octave_value vmaxder = vodeopt.contents ("MaxOrder"); if (vmaxder.is_empty ()) { vmaxder = 3; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" not set, new value %1d is used", vmaxder.int_value ()); } if (vmaxder.int_value () < 1) { vmaxder = 3; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" is zero, new value %1d is used", vmaxder.int_value ()); } // The option BDF will be ignored because this is a BDF solver octave_value vbdf = vodeopt.contents ("BDF"); if (vbdf.is_string ()) if (vbdf.string_value () != "on") { vbdf = "on"; warning_with_id ("OdePkg:InvalidOption", "Option \"BDF\" set \"off\", new value \"on\" is used"); } // The option NewtonTol and MaxNewtonIterations will be ignored by // this solver, IT NEEDS TO BE CHECKED IF THE FORTRAN CORE SOLVER // CAN HANDLE THESE OPTIONS octave_value vntol = vodeopt.contents ("NewtonTol"); if (!vntol.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NewtonTol\" will be ignored by this solver"); octave_value vmaxnewton = vodeopt.contents ("MaxNewtonIterations"); if (!vmaxnewton.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" will be ignored by this solver"); /* Start MAINPROCESSING, set up all variables that are needed by this * solver and then initialize the solver function and get into the * main integration loop ********************************************************************/ ColumnVector vTIME (args(1).vector_value ()); NDArray vRTOL = vreltol.array_value (); NDArray vATOL = vabstol.array_value (); NDArray vY0 = args(2).array_value (); NDArray vYPRIME = args(3).array_value (); octave_idx_type N = args(2).length (); double T0 = vTIME(0); double HO = vinitstep.double_value (); double *Y0 = vY0.fortran_vec (); double *YPRIME = vYPRIME.fortran_vec (); double TOUT = T0 + vinitstep.double_value (); double TEND = vTIME(vTIME.length ()-1); octave_idx_type MF = vmebdfijac; octave_idx_type IDID = 1; octave_idx_type LOUT = 6; // Logical output channel "not opened" octave_idx_type LWORK = 32*N+2*N*N+3; OCTAVE_LOCAL_BUFFER (double, WORK, LWORK); for (octave_idx_type vcnt = 0; vcnt < LWORK; vcnt++) WORK[vcnt] = 0.0; octave_idx_type LIWORK = N+14; OCTAVE_LOCAL_BUFFER (octave_idx_type, IWORK, LIWORK); for (octave_idx_type vcnt = 0; vcnt < LIWORK; vcnt++) IWORK[vcnt] = 0; octave_idx_type MBND[4] = {N, N, N, N}; octave_idx_type MAXDER = vmaxder.int_value (); octave_idx_type ITOL = vitol; double *RTOL = vRTOL.fortran_vec (); double *ATOL = vATOL.fortran_vec (); double RPAR[1] = {0.0}; octave_idx_type IPAR[1] = {0}; octave_idx_type IERR = 0; IWORK[13] = 1000000; // The maximum number of steps allowed // Check if the user has set some of the options "OutputFcn", "Events" // etc. and initialize the plot, events and the solstore functions octave_value vtim (T0); octave_value vsol (vY0); octave_value vyds (vYPRIME); odepkg_auxiliary_solstore (vtim, vsol, 0); if (!vplot.is_empty ()) odepkg_auxiliary_evalplotfun (vplot, voutsel, args(1), args(2), vmebdfiextarg, 0); octave_value_list veveideargs; veveideargs(0) = vsol; veveideargs(1) = vyds; Cell veveidearg (veveideargs); if (!vevents.is_empty ()) odepkg_auxiliary_evaleventfun (vevents, vtim, veveidearg, vmebdfiextarg, 0); // We are calling the core solver here to intialize all variables F77_XFCN (mebdfi, MEBDFI, // Keep 5 arguments per line here (N, T0, HO, Y0, YPRIME, TOUT, TEND, MF, IDID, LOUT, LWORK, WORK, LIWORK, IWORK, MBND, MAXDER, ITOL, RTOL, ATOL, RPAR, IPAR, odepkg_mebdfi_jacfcn, odepkg_mebdfi_usrfcn, IERR)); if (IDID < 0) { odepkg_mebdfi_error (IDID); return (vretval); } // We need that variable in the following loop after calling the // core solver function and before calling the plot function ColumnVector vcres(N); ColumnVector vydrs(N); if (vTIME.length () == 2) { // Before we are entering the solver loop replace the first time // stamp value with FirstStep = (InitTime - InitStep) TOUT = TOUT - vinitstep.double_value (); while (TOUT < TEND) { // Calculate the next time stamp for that an solution is required TOUT = TOUT + vmaxstep.double_value (); TOUT = (TOUT > TEND ? TEND : TOUT); // Call the core Fortran solver again and again and check if an // exception is encountered, set IDID = 2 every time to hit // every point of time that is required exactly IDID = 2; F77_XFCN (mebdfi, MEBDFI, // Keep 5 arguments per line here (N, T0, HO, Y0, YPRIME, TOUT, TEND, MF, IDID, LOUT, LWORK, WORK, LIWORK, IWORK, MBND, MAXDER, ITOL, RTOL, ATOL, RPAR, IPAR, odepkg_mebdfi_jacfcn, odepkg_mebdfi_usrfcn, IERR)); if (IDID < 0) { odepkg_mebdfi_error (IDID); return (vretval); } // This call of the Fortran solver has been successful so let us // plot the output and save the results for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { vcres(vcnt) = Y0[vcnt]; vydrs(vcnt) = YPRIME[vcnt]; } vsol = vcres; vyds = vydrs; vtim = TOUT; if (!vevents.is_empty ()) { veveideargs(0) = vsol; veveideargs(1) = vyds; veveidearg = veveideargs; veveres = odepkg_auxiliary_evaleventfun (vevents, vtim, veveidearg, vmebdfiextarg, 1); if (!veveres(0).cell_value ()(0).is_empty ()) if (veveres(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = veveres(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = veveres(0).cell_value ()(3).matrix_value (); vtim = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vsol = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); TOUT = TEND; // let's get out here, the Events function told us to finish } } if (!vplot.is_empty ()) { if (odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vmebdfiextarg, 1)) { error ("Missing error message implementation"); return (vretval); } } odepkg_auxiliary_solstore (vtim, vsol, 1); } } else { // if (vTIME.length () > 2) we have all the time values needed volatile octave_idx_type vtimecnt = 1; octave_idx_type vtimelen = vTIME.length () - 1; while (vtimecnt < vtimelen) { vtimecnt++; TOUT = vTIME(vtimecnt); // Call the core Fortran solver again and again and check if an // exception is encountered, set IDID = 2 every time to hit // every point of time that is required exactly IDID = 2; F77_XFCN (mebdfi, MEBDFI, // Keep 5 arguments per line here (N, T0, HO, Y0, YPRIME, TOUT, TEND, MF, IDID, LOUT, LWORK, WORK, LIWORK, IWORK, MBND, MAXDER, ITOL, RTOL, ATOL, RPAR, IPAR, odepkg_mebdfi_jacfcn, odepkg_mebdfi_usrfcn, IERR)); if (IDID < 0) { odepkg_mebdfi_error (IDID); return (vretval); } // The last call of the Fortran solver has been successful so // let us plot the output and save the results for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { vcres(vcnt) = Y0[vcnt]; vydrs(vcnt) = YPRIME[vcnt]; } vsol = vcres; vyds = vydrs; vtim = TOUT; if (!vevents.is_empty ()) { veveideargs(0) = vsol; veveideargs(1) = vyds; veveidearg = veveideargs; veveres = odepkg_auxiliary_evaleventfun (vevents, vtim, veveidearg, vmebdfiextarg, 1); if (!veveres(0).cell_value ()(0).is_empty ()) if (veveres(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = veveres(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = veveres(0).cell_value ()(3).matrix_value (); vtim = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vsol = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); vtimecnt = vtimelen; // let's get out here, the Events function told us to finish } } if (!vplot.is_empty ()) { if (odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vmebdfiextarg, 1)) { error ("Missing error message implementation"); return (vretval); } } odepkg_auxiliary_solstore (vtim, vsol, 1); } } /* Start POSTPROCESSING, check how many arguments should be returned * to the caller and check which extra arguments have to be set *******************************************************************/ // Set up values that come from the last Fortran call and that are // needed to call the OdePkg output function one last time again for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { vcres(vcnt) = Y0[vcnt]; vydrs(vcnt) = YPRIME[vcnt]; } vsol = vcres; vyds = vydrs; vtim = TOUT; veveideargs(0) = vsol; veveideargs(1) = vyds; veveidearg = veveideargs; if (!vevents.is_empty ()) odepkg_auxiliary_evaleventfun (vevents, vtim, veveidearg, vmebdfiextarg, 2); if (!vplot.is_empty ()) odepkg_auxiliary_evalplotfun (vplot, voutsel, vtim, vsol, vmebdfiextarg, 2); // Return the results that have been stored in the // odepkg_auxiliary_solstore function octave_value vtres, vyres; odepkg_auxiliary_solstore (vtres, vyres, 2); // odepkg_auxiliary_solstore (vtres, vyres, voutsel, 100); // Get the stats information as an octave_scalar_map if the option 'Stats' // has been set with odeset octave_value_list vstatinput; vstatinput(0) = IWORK[4]; vstatinput(1) = IWORK[5]; vstatinput(2) = IWORK[6]; vstatinput(3) = IWORK[7]; vstatinput(4) = IWORK[8]; vstatinput(5) = IWORK[9]; octave_value vstatinfo; if (vstats.string_value () == "on" && (nargout == 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, false); else if (vstats.string_value () == "on" && (nargout != 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, true); // Set up output arguments that depends on how many output arguments // are desired by the caller if (nargout == 1) { octave_scalar_map vretmap; vretmap.assign ("x", vtres); vretmap.assign ("y", vyres); vretmap.assign ("solver", "odebdi"); if (vstats.string_value () == "on") vretmap.assign ("stats", vstatinfo); if (!vevents.is_empty ()) { vretmap.assign ("ie", veveres(0).cell_value ()(1)); vretmap.assign ("xe", veveres(0).cell_value ()(2)); vretmap.assign ("ye", veveres(0).cell_value ()(3)); } vretval(0) = octave_value (vretmap); } else if (nargout == 2) { vretval(0) = vtres; vretval(1) = vyres; } else if (nargout == 5) { Matrix vempty; // prepare an empty matrix vretval(0) = vtres; vretval(1) = vyres; vretval(2) = vempty; vretval(3) = vempty; vretval(4) = vempty; if (!vevents.is_empty ()) { vretval(2) = veveres(0).cell_value ()(2); vretval(3) = veveres(0).cell_value ()(3); vretval(4) = veveres(0).cell_value ()(1); } } return (vretval); } /* %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [vres] = fpol (vt, vy, vyd, varargin) %! vres = [vy(2) - vyd(1); %! (1 - vy(1)^2) * vy(2) - vy(1) - vyd(2)]; %!function [vjac, vyjc] = fjac (vt, vy, vyd, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %! vyjc = [-1, 0; 0, -1]; %!function [vjac, vyjc] = fjcc (vt, vy, vyd, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %! vyjc = sparse ([-1, 0; 0, -1]); %!function [vval, vtrm, vdir] = feve (vt, vy, vyd, varargin) %! vval = vyd; %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, vyd, varargin) %! vval = vyd; %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vref] = fref () %# The computed reference solut %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (size (vt) != [2, 1] && size (vt) != [1, 2]) %! error ('"fout" step "init"'); %! end %! elseif (isempty (vflag)) %! if (size (vt) ~= [1, 1]) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (size (vt) ~= [1, 1]) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! vsol = odebdi (1, [0, 2], [2; 0], [0; -2]); %!error %# input argument number two %! vsol = odebdi (@fpol, 1, [2; 0], [0; -2]); %!error %# input argument number three %! vsol = odebdi (@fpol, [0, 2], 1, [0; -2]); %!error %# input argument number four %! vsol = odebdi (@fpol, [0, 2], [2; 0], 1); %!test %# one output argument %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'odebdi'); %!test %# two output arguments %! [vt, vy] = odebdi (@fpol, [0, 2], [2; 0], [0; -2]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = odebdi (@fpol, [0, 2], [2; 0], [0; -2]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy,vyd) [vy(2)-vyd(1); (1-vy(1)^2)*vy(2)-vy(1)-vyd(2)]; %! vsol = odebdi (fvdb, [0, 2], [2; 0], [0; -2]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-5); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-1); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-3); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = odebdi (@fpol, [0, 10], [2; 0], [0; -2], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'MaxStep', 0.1); %! vsol = odebdi (@fpol, [0, 10], [2; 0], [0; -2], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.49537, -0.82867, -2.67469], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'MaxStep', 0.1); %! [vt, vy, vxe, vye, vie] = odebdi (@fpol, [0, 10], [2; 0], [0; -2], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.49537, -0.82867, -2.67469], 1e-1); %! warning ('on', 'OdePkg:HideWarning'); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %# test for Mass option is missing %! %# test for MStateDependence option is missing %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %!test %# MaxOrder option %! vopt = odeset ('MaxOrder', 3); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# BDF option %! vopt = odeset ('BDF', 'off'); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set NewtonTol option to something else than default %! vopt = odeset ('NewtonTol', 1e-3); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set MaxNewtonIterations option to something else than default %! vopt = odeset ('MaxNewtonIterations', 2); %! vsol = odebdi (@fpol, [0, 2], [2; 0], [0; -2], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! warning ('on', 'OdePkg:InvalidOption'); */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_octsolver_radau.cc0000644000000000000000000011350712526637474016775 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ /* Compile this file manually and run some tests with the following command bash:~$ mkoctfile -v -Wall -W -Wshadow odepkg_octsolver_radau.cc \ odepkg_auxiliary_functions.cc hairer/radau.f hairer/dc_decsol.f \ hairer/decsol.f -o ode2r.oct octave> octave --quiet --eval "autoload ('ode2r', [pwd, '/ode2r.oct']); \ test 'odepkg_octsolver_radau.cc'" For an explanation about various parts of this source file cf. the source file odepkg_octsolver_rodas.cc. The implementation of that file is very similiar to the implementation of this file. */ #include #include #include #include #include #include "odepkg_auxiliary_functions.h" typedef octave_idx_type (*odepkg_radau_usrtype) (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_radau_jactype) (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_radau_masstype) (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_radau_soltype) (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* CONT, const octave_idx_type* LRC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN); extern "C" { F77_RET_T F77_FUNC (radau, RADAU) (const octave_idx_type& N, odepkg_radau_usrtype, const double& X, const double* Y, const double& XEND, const double& H, const double* RTOL, const double* ATOL, const octave_idx_type& ITOL, odepkg_radau_jactype, const octave_idx_type& IJAC, const octave_idx_type& MLJAC, const octave_idx_type& MUJAC, odepkg_radau_masstype, const octave_idx_type& IMAS, const octave_idx_type& MLMAS, const octave_idx_type& MUMAS, odepkg_radau_soltype, const octave_idx_type& IOUT, const double* WORK, const octave_idx_type& LWORK, const octave_idx_type* IWORK, const octave_idx_type& LIWORK, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, const octave_idx_type& IDID); double F77_FUNC (contra, CONTRA) (const octave_idx_type& I, const double& S, const double* CONT, const octave_idx_type* LRC); } // extern "C" static octave_value_list vradauextarg; static octave_value vradauodefun; static octave_value vradaujacfun; static octave_value vradauevefun; static octave_value_list vradauevesol; static octave_value vradaupltfun; static octave_value vradauoutsel; static octave_value vradaurefine; static octave_value vradaumass; static octave_value vradaumassstate; octave_idx_type odepkg_radau_usrfcn (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element wise, // otherwise Octave will crash if these variables will be freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { A(vcnt) = Y[vcnt]; // octave_stdout << "I am here Y[" << vcnt << "] " << Y[vcnt] << std::endl; // octave_stdout << "I am here T " << X << std::endl; } // Fill the variable for the input arguments before evaluating the // function that keeps the set of differential algebraic equations octave_value_list varin; varin(0) = X; varin(1) = A; for (octave_idx_type vcnt = 0; vcnt < vradauextarg.length (); vcnt++) varin(vcnt+2) = vradauextarg(vcnt); octave_value_list vout = feval (vradauodefun.function_value (), varin, 1); // Return the results from the function evaluation to the Fortran // solver, again copy them and don't just create a Fortran vector ColumnVector vcol = vout(0).column_vector_value (); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) F[vcnt] = vcol(vcnt); return (true); } octave_idx_type odepkg_radau_jacfcn (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (X); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evaljacode (vradaujacfun, vt, vy, vradauextarg); Matrix vdfy = vout.matrix_value (); for (octave_idx_type vcol = 0; vcol < N; vcol++) for (octave_idx_type vrow = 0; vrow < N; vrow++) DFY[vrow+vcol*N] = vdfy (vrow, vcol); return (true); } F77_RET_T odepkg_radau_massfcn (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = 0.0; // warning_with_id ("OdePkg:InvalidFunctionCall", // "Radau can only handle M()=const Mass matrices"); // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (0.0); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evalmassode (vradaumass, vradaumassstate, vt, vy, vradauextarg); Matrix vam = vout.matrix_value (); for (octave_idx_type vrow = 0; vrow < N; vrow++) for (octave_idx_type vcol = 0; vcol < N; vcol++) AM[vrow+vcol*N] = vam (vrow, vcol); return (true); } octave_idx_type odepkg_radau_solfcn (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* CONT, const octave_idx_type* LRC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Output function, the solstore function or the Events function octave_value vt = octave_value (X); octave_value vy = octave_value (A); // Check if an 'Events' function has been set by the user if (!vradauevefun.is_empty ()) { vradauevesol = odepkg_auxiliary_evaleventfun (vradauevefun, vt, vy, vradauextarg, 1); if (!vradauevesol(0).cell_value ()(0).is_empty ()) if (vradauevesol(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = vradauevesol(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = vradauevesol(0).cell_value ()(3).matrix_value (); vt = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vy = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); IRTRN = (vradauevesol(0).cell_value ()(0).int_value () ? -1 : 0); } } // Save the solutions that come from the Fortran core solver if this // is not the initial first call to this function if (NR > 1) odepkg_auxiliary_solstore (vt, vy, 1); // Check if an 'OutputFcn' has been set by the user (including the // values of the options for 'OutputSel' and 'Refine') if (!vradaupltfun.is_empty ()) { if (vradaurefine.int_value () > 0) { ColumnVector B(N); double vtb = 0.0; for (octave_idx_type vcnt = 1; vcnt < vradaurefine.int_value (); vcnt++) { // Calculate time stamps between XOLD and X and get the // results at these time stamps vtb = (X - XOLD) * vcnt / vradaurefine.int_value () + XOLD; for (octave_idx_type vcou = 0; vcou < N; vcou++) B(vcou) = F77_FUNC (contra, CONTRA) (vcou+1, vtb, CONT, LRC); // Evaluate the 'OutputFcn' with the approximated values from // the F77_FUNC before the output of the results is done octave_value vyr = octave_value (B); octave_value vtr = octave_value (vtb); odepkg_auxiliary_evalplotfun (vradaupltfun, vradauoutsel, vtr, vyr, vradauextarg, 1); } } // Evaluate the 'OutputFcn' with the results from the solver, if // the OutputFcn returns true then set a negative value in IRTRN IRTRN = - odepkg_auxiliary_evalplotfun (vradaupltfun, vradauoutsel, vt, vy, vradauextarg, 1); } return (true); } // PKG_ADD: autoload ("ode2r", "dldsolver.oct"); DEFUN_DLD (ode2r, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Command} {[@var{}] =} ode2r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{sol}] =} ode2r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode2r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ \n\ This function file can be used to solve a set of stiff ordinary differential equations (ODEs) and stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{radau.f}.\n\ \n\ If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.\n\ \n\ If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.\n\ \n\ If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.\n\ \n\ For example,\n\ @example\n\ function y = odepkg_equations_lorenz (t, x)\n\ y = [10 * (x(2) - x(1));\n\ x(1) * (28 - x(3));\n\ x(1) * x(2) - 8/3 * x(3)];\n\ endfunction\n\ \n\ vopt = odeset (\"InitialStep\", 1e-3, \"MaxStep\", 1e-1, \\\n\ \"OutputFcn\", @@odephas3, \"Refine\", 5);\n\ ode2r (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt);\n\ @end example\n\ @end deftypefn\n\ \n\ @seealso{odepkg}") { octave_idx_type nargin = args.length (); // The number of input arguments octave_value_list vretval; // The cell array of return args octave_scalar_map vodeopt; // The OdePkg options structure // Check number and types of all input arguments if (nargin < 3) { print_usage (); return (vretval); } // If args(0)==function_handle is valid then set the vradauodefun // variable that has been defined "static" before if (!args(0).is_function_handle () && !args(0).is_inline_function ()) { error_with_id ("OdePkg:InvalidArgument", "First input argument must be a valid function handle"); return (vretval); } else // We store the args(0) argument in the static variable vradauodefun vradauodefun = args(0); // Check if the second input argument is a valid vector describing // the time window that should be solved, it may be of length 2 ONLY if (args(1).is_scalar_type () || !odepkg_auxiliary_isvector (args(1))) { error_with_id ("OdePkg:InvalidArgument", "Second input argument must be a valid vector"); return (vretval); } // Check if the thirt input argument is a valid vector describing // the initial values of the variables of the differential equations if (!odepkg_auxiliary_isvector (args(2))) { error_with_id ("OdePkg:InvalidArgument", "Third input argument must be a valid vector"); return (vretval); } // Check if there are further input arguments ie. the options // structure and/or arguments that need to be passed to the // OutputFcn, Events and/or Jacobian etc. if (nargin >= 4) { // Fourth input argument != OdePkg option, need a default structure if (!args(3).is_map ()) { octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure for (octave_idx_type vcnt = 3; vcnt < nargin; vcnt++) vradauextarg(vcnt-3) = args(vcnt); // Save arguments in vradauextarg } // Fourth input argument == OdePkg option, extra input args given too else if (nargin > 4) { octave_value_list varin; varin(0) = args(3); varin(1) = "ode2r"; octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create structure from args(4) for (octave_idx_type vcnt = 4; vcnt < nargin; vcnt++) vradauextarg(vcnt-4) = args(vcnt); // Save extra arguments } // Fourth input argument == OdePkg option, no extra input args given else { octave_value_list varin; varin(0) = args(3); varin(1) = "ode2r"; // Check structure octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } } // if (nargin >= 4) else { // if nargin == 3, everything else has been checked before octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } /* Start PREPROCESSING, ie. check which options have been set and * print warnings if there are options that can't be handled by this * solver or have not been implemented yet *******************************************************************/ // Implementation of the option RelTol has been finished, this // option can be set by the user to another value than default value octave_value vreltol = vodeopt.contents ("RelTol"); if (vreltol.is_empty ()) { vreltol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"RelTol\" not set, new value %3.1e is used", vreltol.double_value ()); } // vreltol.print (octave_stdout, true); return (vretval); if (!vreltol.is_scalar_type ()) { if (vreltol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"RelTol\" must be the same as the number of equations"); return (vretval); } } // Implementation of the option AbsTol has been finished, this // option can be set by the user to another value than default value octave_value vabstol = vodeopt.contents ("AbsTol"); if (vabstol.is_empty ()) { vabstol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"AbsTol\" not set, new value %3.1e is used", vabstol.double_value ()); } // vabstol.print (octave_stdout, true); return (vretval); if (!vabstol.is_scalar_type ()) { if (vabstol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"AbsTol\" must be the same as the number of equations"); return (vretval); } } // Setting the tolerance type that depends on the types (scalar or // vector) of the options RelTol and AbsTol octave_idx_type vitol = 0; if (vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 0; else if (!vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 1; else { error_with_id ("OdePkg:InvalidOption", "Values of \"RelTol\" and \"AbsTol\" must have same length"); return (vretval); } // The option NormControl will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnorm = vodeopt.contents ("NormControl"); if (!vnorm.is_empty ()) if (vnorm.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"NormControl\" will be ignored by this solver"); // The option NonNegative will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnneg = vodeopt.contents ("NonNegative"); if (!vnneg.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NonNegative\" will be ignored by this solver"); // Implementation of the option OutputFcn has been finished, this // option can be set by the user to another value than default value vradaupltfun = vodeopt.contents ("OutputFcn"); if (vradaupltfun.is_empty () && nargout == 0) vradaupltfun = "odeplot"; // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vradauoutsel = vodeopt.contents ("OutputSel"); // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vradaurefine = vodeopt.contents ("Refine"); // Implementation of the option Stats has been finished, this option // can be set by the user to another value than default value octave_value vstats = vodeopt.contents ("Stats"); // Implementation of the option InitialStep has been finished, this // option can be set by the user to another value than default value octave_value vinitstep = vodeopt.contents ("InitialStep"); if (args(1).length () > 2) { error_with_id ("OdePkg:InvalidOption", "Fixed time stamps are not supported by this solver"); } if (vinitstep.is_empty ()) { vinitstep = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" not set, new value %3.1e is used", vinitstep.double_value ()); } // Implementation of the option MaxStep has been finished, this // option can be set by the user to another value than default value octave_value vmaxstep = vodeopt.contents ("MaxStep"); if (vmaxstep.is_empty () && args(1).length () == 2) { vmaxstep = (args(1).vector_value ()(1) - args(1).vector_value ()(0)) / 12.5; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxStep\" not set, new value %3.1e is used", vmaxstep.double_value ()); } // Implementation of the option Events has been finished, this // option can be set by the user to another value than default // value, odepkg_structure_check already checks for a valid value vradauevefun = vodeopt.contents ("Events"); // Implementation of the option 'Jacobian' has been finished, these // options can be set by the user to another value than default vradaujacfun = vodeopt.contents ("Jacobian"); octave_idx_type vradaujac = 0; // We need to set this if no Jac available if (!vradaujacfun.is_empty ()) vradaujac = 1; // The option JPattern will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vradaujacpat = vodeopt.contents ("JPattern"); if (!vradaujacpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"JPattern\" will be ignored by this solver"); // The option Vectorized will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vradauvectorize = vodeopt.contents ("Vectorized"); if (!vradauvectorize.is_empty ()) if (vradauvectorize.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"Vectorized\" will be ignored by this solver"); // Implementation of the option 'Mass' has been finished, these // options can be set by the user to another value than default vradaumass = vodeopt.contents ("Mass"); octave_idx_type vradaumas = 0; if (!vradaumass.is_empty ()) { vradaumas = 1; if (vradaumass.is_function_handle () || vradaumass.is_inline_function ()) warning_with_id ("OdePkg:InvalidOption", "Option \"Mass\" only supports constant mass matrices M() and not M(t,y)"); } // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option vradaumassstate = vodeopt.contents ("MStateDependence"); if (!vradaumassstate.is_empty ()) if (vradaumassstate.string_value ().compare ("weak") != 0) // 'weak' is default warning_with_id ("OdePkg:InvalidOption", "Option \"MStateDependence\" will be ignored by this solver"); // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmvpat = vodeopt.contents ("MvPattern"); if (!vmvpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MvPattern\" will be ignored by this solver"); // The option MassSingular will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmsing = vodeopt.contents ("MassSingular"); if (!vmsing.is_empty ()) if (vmsing.string_value ().compare ("maybe") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MassSingular\" will be ignored by this solver"); // The option InitialSlope will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vinitslope = vodeopt.contents ("InitialSlope"); if (!vinitslope.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialSlope\" will be ignored by this solver"); // The option MaxOrder will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vmaxder = vodeopt.contents ("MaxOrder"); if (!vmaxder.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" will be ignored by this solver"); // The option BDF will be ignored by this solver, the core Fortran // solver doesn't support this option octave_value vbdf = vodeopt.contents ("BDF"); if (!vbdf.is_empty ()) if (vbdf.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"BDF\" will be ignored by this solver"); // The option NewtonTol and MaxNewtonIterations will be ignored by // this solver, IT NEEDS TO BE CHECKED IF THE FORTRAN CORE SOLVER // CAN HANDLE THESE OPTIONS octave_value vntol = vodeopt.contents ("NewtonTol"); if (!vntol.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NewtonTol\" will be ignored by this solver"); octave_value vmaxnewton = vodeopt.contents ("MaxNewtonIterations"); if (!vmaxnewton.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" will be ignored by this solver"); /* Start MAINPROCESSING, set up all variables that are needed by this * solver and then initialize the solver function and get into the * main integration loop ********************************************************************/ NDArray vY0 = args(2).array_value (); NDArray vRTOL = vreltol.array_value (); NDArray vATOL = vabstol.array_value (); octave_idx_type N = args(2).length (); double X = args(1).vector_value ()(0); double* Y = vY0.fortran_vec (); double XEND = args(1).vector_value ()(1); double H = vinitstep.double_value (); double *RTOL = vRTOL.fortran_vec (); double *ATOL = vATOL.fortran_vec (); octave_idx_type ITOL = vitol; octave_idx_type IJAC = vradaujac; octave_idx_type MLJAC=N; octave_idx_type MUJAC=N; octave_idx_type IMAS=vradaumas; octave_idx_type MLMAS=N; octave_idx_type MUMAS=N; octave_idx_type IOUT = 1; // The SOLOUT function will always be called octave_idx_type LWORK = N*(N+N+7*N+3*7+3)+20; OCTAVE_LOCAL_BUFFER (double, WORK, LWORK); for (octave_idx_type vcnt = 0; vcnt < LWORK; vcnt++) WORK[vcnt] = 0.0; octave_idx_type LIWORK = (2+(7-1)/2)*N+20; OCTAVE_LOCAL_BUFFER (octave_idx_type, IWORK, LIWORK); for (octave_idx_type vcnt = 0; vcnt < LIWORK; vcnt++) IWORK[vcnt] = 0; double RPAR[1] = {0.0}; octave_idx_type IPAR[1] = {0}; octave_idx_type IDID = 0; IWORK[0] = 1; // Switch for transformation of Jacobian into Hessenberg form WORK[2] = -1; // Recompute Jacobian after every succesful step WORK[6] = vmaxstep.double_value (); // Set the maximum step size // Check if the user has set some of the options "OutputFcn", "Events" // etc. and initialize the plot, events and the solstore functions octave_value vtim = args(1).vector_value ()(0); octave_value vsol = args(2); odepkg_auxiliary_solstore (vtim, vsol, 0); if (!vradaupltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vradaupltfun, vradauoutsel, args(1), args(2), vradauextarg, 0); if (!vradauevefun.is_empty ()) odepkg_auxiliary_evaleventfun (vradauevefun, vtim, args(2), vradauextarg, 0); // We are calling the core solver and solve the set of ODEs or DAEs F77_XFCN (radau, RADAU, // Keep 5 arguments per line here (N, odepkg_radau_usrfcn, X, Y, XEND, H, RTOL, ATOL, ITOL, odepkg_radau_jacfcn, IJAC, MLJAC, MUJAC, odepkg_radau_massfcn, IMAS, MLMAS, MUMAS, odepkg_radau_solfcn, IOUT, WORK, LWORK, IWORK, LIWORK, RPAR, IPAR, IDID)); if (IDID < 0) { // odepkg_auxiliary_mebdfanalysis (IDID); error_with_id ("hugh:hugh", "missing implementation"); vretval(0) = 0.0; return (vretval); } /* Start POSTPROCESSING, check how many arguments should be returned * to the caller and check which extra arguments have to be set *******************************************************************/ // Return the results that have been stored in the // odepkg_auxiliary_solstore function octave_value vtres, vyres; odepkg_auxiliary_solstore (vtres, vyres, 2); // Set up variables to make it possible to call the cleanup // functions of 'OutputFcn' and 'Events' if any Matrix vlastline; vlastline = vyres.matrix_value (); vlastline = vlastline.extract (vlastline.rows () - 1, 0, vlastline.rows () - 1, vlastline.cols () - 1); octave_value vted = octave_value (XEND); octave_value vfin = octave_value (vlastline); if (!vradaupltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vradaupltfun, vradauoutsel, vted, vfin, vradauextarg, 2); if (!vradauevefun.is_empty ()) odepkg_auxiliary_evaleventfun (vradauevefun, vted, vfin, vradauextarg, 2); // Get the stats information as an octave_scalar_map if the option 'Stats' // has been set with odeset // "nsteps", "nfailed", "nfevals", "npds", "ndecomps", "nlinsols" octave_value_list vstatinput; vstatinput(0) = IWORK[16]; vstatinput(1) = IWORK[17]; vstatinput(2) = IWORK[13]; vstatinput(3) = IWORK[14]; vstatinput(4) = IWORK[18]; vstatinput(5) = IWORK[19]; octave_value vstatinfo; if ((vstats.string_value ().compare ("on") == 0) && (nargout == 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, false); else if ((vstats.string_value ().compare ("on") == 0) && (nargout != 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, true); // Set up output arguments that depend on how many output arguments // are desired from the caller if (nargout == 1) { octave_scalar_map vretmap; vretmap.assign ("x", vtres); vretmap.assign ("y", vyres); vretmap.assign ("solver", "ode2r"); if (!vstatinfo.is_empty ()) // Event implementation vretmap.assign ("stats", vstatinfo); if (!vradauevefun.is_empty ()) { vretmap.assign ("ie", vradauevesol(0).cell_value ()(1)); vretmap.assign ("xe", vradauevesol(0).cell_value ()(2)); vretmap.assign ("ye", vradauevesol(0).cell_value ()(3)); } vretval(0) = octave_value (vretmap); } else if (nargout == 2) { vretval(0) = vtres; vretval(1) = vyres; } else if (nargout == 5) { Matrix vempty; // prepare an empty matrix vretval(0) = vtres; vretval(1) = vyres; vretval(2) = vempty; vretval(3) = vempty; vretval(4) = vempty; if (!vradauevefun.is_empty ()) { vretval(2) = vradauevesol(0).cell_value ()(2); vretval(3) = vradauevesol(0).cell_value ()(3); vretval(4) = vradauevesol(0).cell_value ()(1); } } return (vretval); } /* %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference solut %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (size (vt) != [2, 1] && size (vt) != [1, 2]) %! error ('"fout" step "init"'); %! end %! elseif (isempty (vflag)) %! if (size (vt) ~= [1, 1]) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (size (vt) ~= [1, 1]) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = ode2r (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = ode2r (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = ode2r (@fpol, [0 25], 1); %!error %# fixed step sizes not supported %! B = ode2r (@fpol, [0:0.1:2], [2 0]); %!test %# one output argument %! vsol = ode2r (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode2r'); %!test %# two output arguments %! [vt, vy] = ode2r (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode2r (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = ode2r (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = ode2r (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode2r (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode2r (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode2r (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode2r (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode2r (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode2r (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %! warning ('on', 'OdePkg:HideWarning'); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set NewtonTol option to something else than default %! vopt = odeset ('NewtonTol', 1e-3); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set MaxNewtonIterations option to something else than default %! vopt = odeset ('MaxNewtonIterations', 2); %! vsol = ode2r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! warning ('on', 'OdePkg:InvalidOption'); */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_octsolver_radau5.cc0000644000000000000000000011471712526637474017066 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ /* Compile this file manually and run some tests with the following command bash:~$ mkoctfile -v -Wall -W -Wshadow odepkg_octsolver_radau5.cc \ odepkg_auxiliary_functions.cc hairer/radau5.f hairer/dc_decsol.f \ hairer/decsol.f -o ode5r.oct octave> octave --quiet --eval "autoload ('ode5r', [pwd, '/ode5r.oct']); \ test 'odepkg_octsolver_radau5.cc'" For an explanation about various parts of this source file cf. the source file odepkg_octsolver_rodas.cc. The implementation of that file is very similiar to the implementation of this file. */ #include #include #include #include #include #include "odepkg_auxiliary_functions.h" typedef octave_idx_type (*odepkg_radau5_usrtype) (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_radau5_jactype) (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_radau5_masstype) (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_radau5_soltype) (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* CONT, const octave_idx_type* LRC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN); extern "C" { F77_RET_T F77_FUNC (radau5, RADAU5) (const octave_idx_type& N, odepkg_radau5_usrtype, const double& X, const double* Y, const double& XEND, const double& H, const double* RTOL, const double* ATOL, const octave_idx_type& ITOL, odepkg_radau5_jactype, const octave_idx_type& IJAC, const octave_idx_type& MLJAC, const octave_idx_type& MUJAC, odepkg_radau5_masstype, const octave_idx_type& IMAS, const octave_idx_type& MLMAS, const octave_idx_type& MUMAS, odepkg_radau5_soltype, const octave_idx_type& IOUT, const double* WORK, const octave_idx_type& LWORK, const octave_idx_type* IWORK, const octave_idx_type& LIWORK, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, const octave_idx_type& IDID); double F77_FUNC (contr5, CONTR5) (const octave_idx_type& I, const double& S, const double* CONT, const octave_idx_type* LRC); } // extern "C" static octave_value_list vradau5extarg; static octave_value vradau5odefun; static octave_value vradau5jacfun; static octave_value vradau5evefun; static octave_value_list vradau5evesol; static octave_value vradau5pltfun; static octave_value vradau5outsel; static octave_value vradau5refine; static octave_value vradau5mass; static octave_value vradau5massstate; octave_idx_type odepkg_radau5_usrfcn (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element wise, // otherwise Octave will crash if these variables will be freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { A(vcnt) = Y[vcnt]; // octave_stdout << "I am here Y[" << vcnt << "] " << Y[vcnt] << std::endl; // octave_stdout << "I am here T " << X << std::endl; } // Fill the variable for the input arguments before evaluating the // function that keeps the set of differential algebraic equations octave_value_list varin; varin(0) = X; varin(1) = A; for (octave_idx_type vcnt = 0; vcnt < vradau5extarg.length (); vcnt++) varin(vcnt+2) = vradau5extarg(vcnt); octave_value_list vout = feval (vradau5odefun.function_value (), varin, 1); // Return the results from the function evaluation to the Fortran // solver, again copy them and don't just create a Fortran vector ColumnVector vcol = vout(0).column_vector_value (); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) F[vcnt] = vcol(vcnt); return (true); } octave_idx_type odepkg_radau5_jacfcn (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (X); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evaljacode (vradau5jacfun, vt, vy, vradau5extarg); Matrix vdfy = vout.matrix_value (); for (octave_idx_type vcol = 0; vcol < N; vcol++) for (octave_idx_type vrow = 0; vrow < N; vrow++) DFY[vrow+vcol*N] = vdfy (vrow, vcol); return (true); } F77_RET_T odepkg_radau5_massfcn (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = 0.0; // warning_with_id ("OdePkg:InvalidFunctionCall", // "Radau5 can only handle M()=const Mass matrices"); // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (0.0); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evalmassode (vradau5mass, vradau5massstate, vt, vy, vradau5extarg); Matrix vam = vout.matrix_value (); for (octave_idx_type vrow = 0; vrow < N; vrow++) for (octave_idx_type vcol = 0; vcol < N; vcol++) AM[vrow+vcol*N] = vam (vrow, vcol); return (true); } octave_idx_type odepkg_radau5_solfcn (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* CONT, const octave_idx_type* LRC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Output function, the solstore function or the Events function octave_value vt = octave_value (X); octave_value vy = octave_value (A); // Check if an 'Events' function has been set by the user if (!vradau5evefun.is_empty ()) { vradau5evesol = odepkg_auxiliary_evaleventfun (vradau5evefun, vt, vy, vradau5extarg, 1); if (!vradau5evesol(0).cell_value ()(0).is_empty ()) if (vradau5evesol(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = vradau5evesol(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = vradau5evesol(0).cell_value ()(3).matrix_value (); vt = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vy = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); IRTRN = (vradau5evesol(0).cell_value ()(0).int_value () ? -1 : 0); } } // Save the solutions that come from the Fortran core solver if this // is not the initial first call to this function if (NR > 1) odepkg_auxiliary_solstore (vt, vy, 1); // Check if an 'OutputFcn' has been set by the user (including the // values of the options for 'OutputSel' and 'Refine') if (!vradau5pltfun.is_empty ()) { if (vradau5refine.int_value () > 0) { ColumnVector B(N); double vtb = 0.0; for (octave_idx_type vcnt = 1; vcnt < vradau5refine.int_value (); vcnt++) { // Calculate time stamps between XOLD and X and get the // results at these time stamps vtb = (X - XOLD) * vcnt / vradau5refine.int_value () + XOLD; for (octave_idx_type vcou = 0; vcou < N; vcou++) B(vcou) = F77_FUNC (contr5, CONTR5) (vcou+1, vtb, CONT, LRC); // Evaluate the 'OutputFcn' with the approximated values from // the F77_FUNC before the output of the results is done octave_value vyr = octave_value (B); octave_value vtr = octave_value (vtb); odepkg_auxiliary_evalplotfun (vradau5pltfun, vradau5outsel, vtr, vyr, vradau5extarg, 1); } } // Evaluate the 'OutputFcn' with the results from the solver, if // the OutputFcn returns true then set a negative value in IRTRN IRTRN = - odepkg_auxiliary_evalplotfun (vradau5pltfun, vradau5outsel, vt, vy, vradau5extarg, 1); } return (true); } // PKG_ADD: autoload ("ode5r", "dldsolver.oct"); DEFUN_DLD (ode5r, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Command} {[@var{}] =} ode5r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{sol}] =} ode5r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode5r (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ \n\ This function file can be used to solve a set of stiff ordinary differential equations (ODEs) and stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{radau5.f}.\n\ \n\ If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.\n\ \n\ If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.\n\ \n\ If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.\n\ \n\ For example,\n\ @example\n\ function y = odepkg_equations_lorenz (t, x)\n\ y = [10 * (x(2) - x(1));\n\ x(1) * (28 - x(3));\n\ x(1) * x(2) - 8/3 * x(3)];\n\ endfunction\n\ \n\ vopt = odeset (\"InitialStep\", 1e-3, \"MaxStep\", 1e-1, \\\n\ \"OutputFcn\", @@odephas3, \"Refine\", 5);\n\ ode5r (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt);\n\ @end example\n\ @end deftypefn\n\ \n\ @seealso{odepkg}") { octave_idx_type nargin = args.length (); // The number of input arguments octave_value_list vretval; // The cell array of return args octave_scalar_map vodeopt; // The OdePkg options structure // Check number and types of all input arguments if (nargin < 3) { print_usage (); return (vretval); } // If args(0)==function_handle is valid then set the vradau5odefun // variable that has been defined "static" before if (!args(0).is_function_handle () && !args(0).is_inline_function ()) { error_with_id ("OdePkg:InvalidArgument", "First input argument must be a valid function handle"); return (vretval); } else // We store the args(0) argument in the static variable vradau5odefun vradau5odefun = args(0); // Check if the second input argument is a valid vector describing // the time window that should be solved, it may be of length 2 ONLY if (args(1).is_scalar_type () || !odepkg_auxiliary_isvector (args(1))) { error_with_id ("OdePkg:InvalidArgument", "Second input argument must be a valid vector"); return (vretval); } // Check if the thirt input argument is a valid vector describing // the initial values of the variables of the differential equations if (!odepkg_auxiliary_isvector (args(2))) { error_with_id ("OdePkg:InvalidArgument", "Third input argument must be a valid vector"); return (vretval); } // Check if there are further input arguments ie. the options // structure and/or arguments that need to be passed to the // OutputFcn, Events and/or Jacobian etc. if (nargin >= 4) { // Fourth input argument != OdePkg option, need a default structure if (!args(3).is_map ()) { octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure for (octave_idx_type vcnt = 3; vcnt < nargin; vcnt++) vradau5extarg(vcnt-3) = args(vcnt); // Save arguments in vradau5extarg } // Fourth input argument == OdePkg option, extra input args given too else if (nargin > 4) { octave_value_list varin; varin(0) = args(3); varin(1) = "ode5r"; octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create structure from args(4) for (octave_idx_type vcnt = 4; vcnt < nargin; vcnt++) vradau5extarg(vcnt-4) = args(vcnt); // Save extra arguments } // Fourth input argument == OdePkg option, no extra input args given else { octave_value_list varin; varin(0) = args(3); varin(1) = "ode5r"; // Check structure octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } } // if (nargin >= 4) else { // if nargin == 3, everything else has been checked before octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } /* Start PREPROCESSING, ie. check which options have been set and * print warnings if there are options that can't be handled by this * solver or have not been implemented yet *******************************************************************/ // Implementation of the option RelTol has been finished, this // option can be set by the user to another value than default value octave_value vreltol = vodeopt.contents ("RelTol"); if (vreltol.is_empty ()) { vreltol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"RelTol\" not set, new value %3.1e is used", vreltol.double_value ()); } // vreltol.print (octave_stdout, true); return (vretval); if (!vreltol.is_scalar_type ()) { if (vreltol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"RelTol\" must be the same as the number of equations"); return (vretval); } } // Implementation of the option AbsTol has been finished, this // option can be set by the user to another value than default value octave_value vabstol = vodeopt.contents ("AbsTol"); if (vabstol.is_empty ()) { vabstol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"AbsTol\" not set, new value %3.1e is used", vabstol.double_value ()); } // vabstol.print (octave_stdout, true); return (vretval); if (!vabstol.is_scalar_type ()) { if (vabstol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"AbsTol\" must be the same as the number of equations"); return (vretval); } } // Setting the tolerance type that depends on the types (scalar or // vector) of the options RelTol and AbsTol octave_idx_type vitol = 0; if (vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 0; else if (!vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 1; else { error_with_id ("OdePkg:InvalidOption", "Values of \"RelTol\" and \"AbsTol\" must have same length"); return (vretval); } // The option NormControl will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnorm = vodeopt.contents ("NormControl"); if (!vnorm.is_empty ()) if (vnorm.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"NormControl\" will be ignored by this solver"); // The option NonNegative will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnneg = vodeopt.contents ("NonNegative"); if (!vnneg.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NonNegative\" will be ignored by this solver"); // Implementation of the option OutputFcn has been finished, this // option can be set by the user to another value than default value vradau5pltfun = vodeopt.contents ("OutputFcn"); if (vradau5pltfun.is_empty () && nargout == 0) vradau5pltfun = "odeplot"; // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vradau5outsel = vodeopt.contents ("OutputSel"); // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vradau5refine = vodeopt.contents ("Refine"); // Implementation of the option Stats has been finished, this option // can be set by the user to another value than default value octave_value vstats = vodeopt.contents ("Stats"); // Implementation of the option InitialStep has been finished, this // option can be set by the user to another value than default value octave_value vinitstep = vodeopt.contents ("InitialStep"); if (args(1).length () > 2) { error_with_id ("OdePkg:InvalidOption", "Fixed time stamps are not supported by this solver"); } if (vinitstep.is_empty ()) { vinitstep = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" not set, new value %3.1e is used", vinitstep.double_value ()); } // Implementation of the option MaxStep has been finished, this // option can be set by the user to another value than default value octave_value vmaxstep = vodeopt.contents ("MaxStep"); if (vmaxstep.is_empty () && args(1).length () == 2) { vmaxstep = (args(1).vector_value ()(1) - args(1).vector_value ()(0)) / 12.5; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxStep\" not set, new value %3.1e is used", vmaxstep.double_value ()); } // Implementation of the option Events has been finished, this // option can be set by the user to another value than default // value, odepkg_structure_check already checks for a valid value vradau5evefun = vodeopt.contents ("Events"); // Implementation of the option 'Jacobian' has been finished, these // options can be set by the user to another value than default vradau5jacfun = vodeopt.contents ("Jacobian"); octave_idx_type vradau5jac = 0; // We need to set this if no Jac available if (!vradau5jacfun.is_empty ()) vradau5jac = 1; // The option JPattern will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vradau5jacpat = vodeopt.contents ("JPattern"); if (!vradau5jacpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"JPattern\" will be ignored by this solver"); // The option Vectorized will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vradau5vectorize = vodeopt.contents ("Vectorized"); if (!vradau5vectorize.is_empty ()) if (vradau5vectorize.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"Vectorized\" will be ignored by this solver"); // Implementation of the option 'Mass' has been finished, these // options can be set by the user to another value than default vradau5mass = vodeopt.contents ("Mass"); octave_idx_type vradau5mas = 0; if (!vradau5mass.is_empty ()) { vradau5mas = 1; if (vradau5mass.is_function_handle () || vradau5mass.is_inline_function ()) warning_with_id ("OdePkg:InvalidOption", "Option \"Mass\" only supports constant mass matrices M() and not M(t,y)"); } // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option vradau5massstate = vodeopt.contents ("MStateDependence"); if (!vradau5massstate.is_empty ()) if (vradau5massstate.string_value ().compare ("weak") != 0) // 'weak' is default warning_with_id ("OdePkg:InvalidOption", "Option \"MStateDependence\" will be ignored by this solver"); // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmvpat = vodeopt.contents ("MvPattern"); if (!vmvpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MvPattern\" will be ignored by this solver"); // The option MassSingular will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmsing = vodeopt.contents ("MassSingular"); if (!vmsing.is_empty ()) if (vmsing.string_value ().compare ("maybe") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MassSingular\" will be ignored by this solver"); // The option InitialSlope will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vinitslope = vodeopt.contents ("InitialSlope"); if (!vinitslope.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialSlope\" will be ignored by this solver"); // The option MaxOrder will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vmaxder = vodeopt.contents ("MaxOrder"); if (!vmaxder.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" will be ignored by this solver"); // The option BDF will be ignored by this solver, the core Fortran // solver doesn't support this option octave_value vbdf = vodeopt.contents ("BDF"); if (!vbdf.is_empty ()) if (vbdf.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"BDF\" will be ignored by this solver"); // Implementation of the option NewtonTol has been finished, this // option can be set by the user to another value than default value octave_value vNTOL = vodeopt.contents ("NewtonTol"); if (vNTOL.is_empty ()) { vNTOL = 0; warning_with_id ("OdePkg:InvalidOption", "Option \"NewtonTol\" not set, default value is used"); } // Implementation of the option MaxNewtonIterations has been finished, this // option can be set by the user to another value than default value octave_value vmaxnit = vodeopt.contents ("MaxNewtonIterations"); if (vmaxnit.is_empty ()) { vmaxnit = 7; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" not set, default value 7 is used"); } else if (vmaxnit.int_value () < 1) { vmaxnit = 7; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" is zero, default value 7 is used"); } /* Start MAINPROCESSING, set up all variables that are needed by this * solver and then initialize the solver function and get into the * main integration loop ********************************************************************/ NDArray vY0 = args(2).array_value (); NDArray vRTOL = vreltol.array_value (); NDArray vATOL = vabstol.array_value (); octave_idx_type N = args(2).length (); double X = args(1).vector_value ()(0); double* Y = vY0.fortran_vec (); double XEND = args(1).vector_value ()(1); double H = vinitstep.double_value (); double *RTOL = vRTOL.fortran_vec (); double *ATOL = vATOL.fortran_vec (); octave_idx_type ITOL = vitol; octave_idx_type IJAC = vradau5jac; octave_idx_type MLJAC=N; octave_idx_type MUJAC=N; octave_idx_type IMAS=vradau5mas; octave_idx_type MLMAS=N; octave_idx_type MUMAS=N; octave_idx_type IOUT = 1; // The SOLOUT function will always be called octave_idx_type LWORK = N*(N+N+7*N+3*7+3)+20; OCTAVE_LOCAL_BUFFER (double, WORK, LWORK); for (octave_idx_type vcnt = 0; vcnt < LWORK; vcnt++) WORK[vcnt] = 0.0; octave_idx_type LIWORK = (2+(7-1)/2)*N+20; OCTAVE_LOCAL_BUFFER (octave_idx_type, IWORK, LIWORK); for (octave_idx_type vcnt = 0; vcnt < LIWORK; vcnt++) IWORK[vcnt] = 0; double RPAR[1] = {0.0}; octave_idx_type IPAR[1] = {0}; octave_idx_type IDID = 0; IWORK[0] = 1; // Switch for transformation of Jacobian into Hessenberg form IWORK[2] = vmaxnit.int_value (); // Maximum number of Newton iterations WORK[2] = -1; // Recompute Jacobian after every succesful step WORK[3] = vNTOL.double_value (); // Tolerance of Newton iteration WORK[6] = vmaxstep.double_value (); // Set the maximum step size // Check if the user has set some of the options "OutputFcn", "Events" // etc. and initialize the plot, events and the solstore functions octave_value vtim = args(1).vector_value ()(0); octave_value vsol = args(2); odepkg_auxiliary_solstore (vtim, vsol, 0); if (!vradau5pltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vradau5pltfun, vradau5outsel, args(1), args(2), vradau5extarg, 0); if (!vradau5evefun.is_empty ()) odepkg_auxiliary_evaleventfun (vradau5evefun, vtim, args(2), vradau5extarg, 0); // octave_stdout << "X VALUE=" << X << XEND << std::endl; // We are calling the core solver and solve the set of ODEs or DAEs F77_XFCN (radau5, RADAU5, // Keep 5 arguments per line here (N, odepkg_radau5_usrfcn, X, Y, XEND, H, RTOL, ATOL, ITOL, odepkg_radau5_jacfcn, IJAC, MLJAC, MUJAC, odepkg_radau5_massfcn, IMAS, MLMAS, MUMAS, odepkg_radau5_solfcn, IOUT, WORK, LWORK, IWORK, LIWORK, RPAR, IPAR, IDID)); if (IDID < 0) { // odepkg_auxiliary_mebdfanalysis (IDID); error_with_id ("hugh:hugh", "missing implementation"); vretval(0) = 0.0; return (vretval); } /* Start POSTPROCESSING, check how many arguments should be returned * to the caller and check which extra arguments have to be set *******************************************************************/ // Return the results that have been stored in the // odepkg_auxiliary_solstore function octave_value vtres, vyres; odepkg_auxiliary_solstore (vtres, vyres, 2); // Set up variables to make it possible to call the cleanup // functions of 'OutputFcn' and 'Events' if any Matrix vlastline; vlastline = vyres.matrix_value (); vlastline = vlastline.extract (vlastline.rows () - 1, 0, vlastline.rows () - 1, vlastline.cols () - 1); octave_value vted = octave_value (XEND); octave_value vfin = octave_value (vlastline); if (!vradau5pltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vradau5pltfun, vradau5outsel, vted, vfin, vradau5extarg, 2); if (!vradau5evefun.is_empty ()) odepkg_auxiliary_evaleventfun (vradau5evefun, vted, vfin, vradau5extarg, 2); // Get the stats information as an octave_scalar_map if the option 'Stats' // has been set with odeset // "nsteps", "nfailed", "nfevals", "npds", "ndecomps", "nlinsols" octave_value_list vstatinput; vstatinput(0) = IWORK[16]; vstatinput(1) = IWORK[17]; vstatinput(2) = IWORK[13]; vstatinput(3) = IWORK[14]; vstatinput(4) = IWORK[18]; vstatinput(5) = IWORK[19]; octave_value vstatinfo; if ((vstats.string_value ().compare ("on") == 0) && (nargout == 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, false); else if ((vstats.string_value ().compare ("on") == 0) && (nargout != 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, true); // Set up output arguments that depend on how many output arguments // are desired from the caller if (nargout == 1) { octave_scalar_map vretmap; vretmap.assign ("x", vtres); vretmap.assign ("y", vyres); vretmap.assign ("solver", "ode5r"); if (!vstatinfo.is_empty ()) // Event implementation vretmap.assign ("stats", vstatinfo); if (!vradau5evefun.is_empty ()) { vretmap.assign ("ie", vradau5evesol(0).cell_value ()(1)); vretmap.assign ("xe", vradau5evesol(0).cell_value ()(2)); vretmap.assign ("ye", vradau5evesol(0).cell_value ()(3)); } vretval(0) = octave_value (vretmap); } else if (nargout == 2) { vretval(0) = vtres; vretval(1) = vyres; } else if (nargout == 5) { Matrix vempty; // prepare an empty matrix vretval(0) = vtres; vretval(1) = vyres; vretval(2) = vempty; vretval(3) = vempty; vretval(4) = vempty; if (!vradau5evefun.is_empty ()) { vretval(2) = vradau5evesol(0).cell_value ()(2); vretval(3) = vradau5evesol(0).cell_value ()(3); vretval(4) = vradau5evesol(0).cell_value ()(1); } } return (vretval); } /* %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference solut %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (size (vt) != [2, 1] && size (vt) != [1, 2]) %! error ('"fout" step "init"'); %! end %! elseif (isempty (vflag)) %! if (size (vt) ~= [1, 1]) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (size (vt) ~= [1, 1]) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = ode5r (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = ode5r (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = ode5r (@flor, [0 25], 1); %!error %# fixed step sizes not supported %! B = ode5r (@fpol, [0:0.1:2], [2 0]); %!test %# one output argument %! vsol = ode5r (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'ode5r'); %!test %# two output arguments %! [vt, vy] = ode5r (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = ode5r (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = ode5r (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = ode5r (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = ode5r (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = ode5r (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = ode5r (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = ode5r (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = ode5r (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = ode5r (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %! warning ('on', 'OdePkg:HideWarning'); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set NewtonTol option to something else than default %! vopt = odeset ('NewtonTol', 1e-3); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set MaxNewtonIterations option to something else than default %! vopt = odeset ('MaxNewtonIterations', 2); %! vsol = ode5r (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! warning ('on', 'OdePkg:InvalidOption'); */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_octsolver_rodas.cc0000644000000000000000000014104112526637474017003 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ /* Compile this file manually and run some tests with the following command bash:~$ mkoctfile -v -Wall -W -Wshadow odepkg_octsolver_rodas.cc \ odepkg_auxiliary_functions.cc hairer/rodas.f hairer/dc_decsol.f \ hairer/decsol.f -o oders.oct octave> octave --quiet --eval "autoload ('oders', [pwd, '/oders.oct']); \ test 'odepkg_octsolver_rodas.cc'" */ #include #include #include #include #include #include "odepkg_auxiliary_functions.h" /* -*- texinfo -*- * @subsection Source File @file{odepkg_octsolver_rodas.cc} * * @deftp {Typedef} {octave_idx_type (*odepkg_rodas_usrtype)} * This @code{typedef} is used to define the input and output arguments of the user function for the DAE problem that is further needed by the Fortran core solver @code{rodas}. The implementation of this @code{typedef} is * * @example * typedef octave_idx_type (*odepkg_rodas_usrtype) * (const octave_idx_type& N, const double& X, const double* Y, * double* F, GCC_ATTR_UNUSED const double* RPAR, * GCC_ATTR_UNUSED const octave_idx_type* IPAR); * @end example * @end deftp */ typedef octave_idx_type (*odepkg_rodas_usrtype) (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); /* -*- texinfo -*- * @deftp {Typedef} {octave_idx_type (*odepkg_rodas_jactype)} * * This @code{typedef} is used to define the input and output arguments of the @code{Jacobian} function for the DAE problem that is further needed by the Fortran core solver @code{rodas}. The implementation of this @code{typedef} is * * @example * typedef octave_idx_type (*odepkg_rodas_jactype) * (const octave_idx_type& N, const double& X, const double* Y, * double* DFY, GCC_ATTR_UNUSED const octave_idx_type* LDFY, * GCC_ATTR_UNUSED const double* RPAR, * GCC_ATTR_UNUSED const octave_idx_type* IPAR); * @end example * @end deftp */ typedef octave_idx_type (*odepkg_rodas_jactype) (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); /* -*- texinfo -*- * @deftp {Typedef} {octave_idx_type (*odepkg_rodas_masstype)} * * This @code{typedef} is used to define the input and output arguments of the @code{Mass} function for the DAE problem that is further needed by the Fortran core solver @code{rodas}. The implementation of this @code{typedef} is * * @example * typedef octave_idx_type (*odepkg_rodas_masstype) * (const octave_idx_type& N, double* AM, * GCC_ATTR_UNUSED const octave_idx_type* LMAS, * GCC_ATTR_UNUSED const double* RPAR, * GCC_ATTR_UNUSED const octave_idx_type* IPAR); * @end example * @end deftp */ typedef octave_idx_type (*odepkg_rodas_masstype) (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); /* -*- texinfo -*- * @deftp {Typedef} {octave_idx_type (*odepkg_rodas_soltype)} * * This @code{typedef} is used to define the input and output arguments of the @code{Solution} function for the DAE problem that is further needed by the Fortran core solver @code{rodas}. The implementation of this @code{typedef} is * * @example * typedef octave_idx_type (*odepkg_rodas_soltype) * (const octave_idx_type& NR, const double& XOLD, const double& X, * const double* Y, const double* CONT, const octave_idx_type* LRC, * const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, * GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN); * @end example * @end deftp */ typedef octave_idx_type (*odepkg_rodas_soltype) (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* CONT, const octave_idx_type* LRC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN); /* -*- texinfo -*- * @deftp {Typedef} {octave_idx_type (*odepkg_rodas_dfxtype)} * * This @code{typedef} is used to define the input and output arguments of the @code{DFX} function for the DAE problem that is further needed by the Fortran core solver @code{rodas}. The implementation of this @code{typedef} is * * @example * typedef octave_idx_type (*odepkg_rodas_dfxtype) * (GCC_ATTR_UNUSED const octave_idx_type& N, * GCC_ATTR_UNUSED const double& X, * GCC_ATTR_UNUSED const double* Y, * GCC_ATTR_UNUSED const double* FX, * GCC_ATTR_UNUSED const double* RPAR, * GCC_ATTR_UNUSED const octave_idx_type* IPAR); * @end example * @end deftp */ typedef octave_idx_type (*odepkg_rodas_dfxtype) (GCC_ATTR_UNUSED const octave_idx_type& N, GCC_ATTR_UNUSED const double& X, GCC_ATTR_UNUSED const double* Y, GCC_ATTR_UNUSED const double* FX, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); extern "C" { /* -*- texinfo -*- * @deftp {Prototype} {F77_RET_T F77_FUNC (rodas, RODAS)} (const octave_idx_type& N, odepkg_rodas_usrtype, const octave_idx_type& IFCN, const octave_idx_type& X, const double* Y, const double& XEND, const double& H, const double* RTOL, const double* ATOL, const octave_idx_type& ITOL, odepkg_rodas_jactype, const octave_idx_type& IJAC, const octave_idx_type& MLJAC, const octave_idx_type& MUJAC, odepkg_rodas_dfxtype, const octave_idx_type& IDFX, odepkg_rodas_masstype, const octave_idx_type& IMAS, const octave_idx_type& MLMAS, const octave_idx_type& MUMAS, odepkg_rodas_soltype, const octave_idx_type& IOUT, const double* WORK, const octave_idx_type& LWORK, const octave_idx_type* IWORK, const octave_idx_type& LIWORK, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, const octave_idx_type& IDID); * * The prototype @code{F77_FUNC (rodas, RODAS)} is used to represent the information about the Fortran core solver @code{rodas} that is defined in the Fortran source file @file{rodas.f} (cf. the Fortran source file @file{rodas.f} for further details). * @end deftp */ F77_RET_T F77_FUNC (rodas, RODAS) (const octave_idx_type& N, odepkg_rodas_usrtype, const octave_idx_type& IFCN, const double& X, const double* Y, const double& XEND, const double& H, const double* RTOL, const double* ATOL, const octave_idx_type& ITOL, odepkg_rodas_jactype, const octave_idx_type& IJAC, const octave_idx_type& MLJAC, const octave_idx_type& MUJAC, odepkg_rodas_dfxtype, const octave_idx_type& IDFX, odepkg_rodas_masstype, const octave_idx_type& IMAS, const octave_idx_type& MLMAS, const octave_idx_type& MUMAS, odepkg_rodas_soltype, const octave_idx_type& IOUT, const double* WORK, const octave_idx_type& LWORK, const octave_idx_type* IWORK, const octave_idx_type& LIWORK, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, const octave_idx_type& IDID); /* -*- texinfo -*- * @deftp {Prototype} {double F77_FUNC (contro, CONTRO)} (const octave_idx_type& I, const double* S, const double* CONT, const octave_idx_type* LRC); * * The prototype @code{F77_FUNC (contro, CONTRO)} is used to represent the information about a continous output calculation for the @code{rodas} algorithm that is defined in the Fortran source file @file{rodas.f} (cf. the Fortran source file @file{rodas.f} for further details). * @end deftp */ double F77_FUNC (contro, CONTRO) (const octave_idx_type& I, const double& S, const double* CONT, const octave_idx_type* LRC); /* -*- texinfo -*- * @deftp {Prototype} {F77_RET_T F77_FUNC (dfx, DFX)} (GCC_ATTR_UNUSED const octave_idx_type& N, GCC_ATTR_UNUSED const double& X, GCC_ATTR_UNUSED const double* Y, GCC_ATTR_UNUSED const double* FX, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); * * The prototype @code{F77_RET_T F77_FUNC (dfx, DFX)} is used to represent the information about a DFX dummy function (that will never be called because it is not supported) for the @code{rodas} algorithm that is defined in the Fortran source file @file{rodas.f} (cf. the Fortran source file @file{rodas.f} for further details). * @end deftp */ F77_RET_T F77_FUNC (dfx, DFX) (GCC_ATTR_UNUSED const octave_idx_type& N, GCC_ATTR_UNUSED const double& X, GCC_ATTR_UNUSED const double* Y, GCC_ATTR_UNUSED const double* FX, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); } // extern "C" /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value_list} {vrodasextarg} * * This static variable is used to store the extra arguments that are needed by some or by all of the @code{OutputFcn}, the @code{Jacobian} function, the @code{Mass} function and the @code{Events} function while solving the DAE problem. * @end deftypevr */ static octave_value_list vrodasextarg; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasodefun} * * This static variable is used to store the value for the user function that defines the set of DAEs. * @end deftypevr */ static octave_value vrodasodefun; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasjacfun} * * This static variable is used to store the value for the @code{Jacobian} function that may be needed from the Fortran core solver while solving. * @end deftypevr */ static octave_value vrodasjacfun; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasevefun} * * This static variable is used to store the value for the @code{Events} function that may be needed from the Fortran core solver while solving. * @end deftypevr */ static octave_value vrodasevefun; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasevesol} * * This static variable is used to store the results that come from the @code{Events} function while solving. * @end deftypevr */ static octave_value_list vrodasevesol; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodaspltfun} * * This static variable is used to store the value for the @code{OutputFcn} function if any. * @end deftypevr */ static octave_value vrodaspltfun; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasoutsel} * * This static variable is used to store the value for the @code{OutputSel} vector if any. * @end deftypevr */ static octave_value vrodasoutsel; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasrefine} * * This static variable is used to store the value for the @code{Refine} option if any. * @end deftypevr */ static octave_value vrodasrefine; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasmass} * * This static variable is used to store the value for the @code{Mass} function while solving. * @end deftypevr */ static octave_value vrodasmass; /* -*- texinfo -*- * @deftypevr {Variable} {static octave_value} {vrodasmassstate} * * This static variable is used to store the value for the @code{MStateDependence} string if any. * @end deftypevr */ static octave_value vrodasmassstate; /* -*- texinfo -*- * @deftypefn {Function} {octave_idx_type} {odepkg_rodas_usrfcn} (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) * * Return @code{true} if the evaluation of the user type function (that is defined for a special DAE problem in Octave) was successful, otherwise return @code{false}. This function is directly called from the Fortran core solver @code{rodas}. The input arguments of this function are * * @itemize @minus * @item @var{N}: The number of equations that are defined for the DAE--problem * @item @var{X}: The actual time stamp for the current function evaluation * @item @var{Y}: The function values from the last successful integration step of length @var{N} * @item @var{F}: The solution vector that needs to be calculated of length @var{N} * @item @var{RPAR}: The real parameters that are passed to the user function (unused) * @item @var{IPAR}: The integer parameters that are passed to the user function (unused) * @end itemize * @end deftypefn */ octave_idx_type odepkg_rodas_usrfcn (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element wise, // otherwise Octave will crash if these variables will be freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { A(vcnt) = Y[vcnt]; // octave_stdout << "I am here Y[" << vcnt << "] " << Y[vcnt] << std::endl; // octave_stdout << "I am here T " << X << std::endl; } // Fill the variable for the input arguments before evaluating the // function that keeps the set of differential algebraic equations octave_value_list varin; varin(0) = X; varin(1) = A; for (octave_idx_type vcnt = 0; vcnt < vrodasextarg.length (); vcnt++) varin(vcnt+2) = vrodasextarg(vcnt); octave_value_list vout = feval (vrodasodefun.function_value (), varin, 1); // Return the results from the function evaluation to the Fortran // solver, again copy them and don't just create a Fortran vector ColumnVector vcol = vout(0).column_vector_value (); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) F[vcnt] = vcol(vcnt); return (true); } /* -*- texinfo -*- * @deftypefn {Function} {octave_idx_type} {odepkg_mebdfi_jacfcn} (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) * * Return @code{true} if the evaluation of the @code{Jacobian} function (that is defined for a special DAE problem in Octave) was successful, otherwise return @code{false}. This function is directly called from the Fortran core solver @code{rodas}. The input arguments of this function are * * @itemize @minus * @item @var{N}: The number of equations that are defined for the DAE--problem * @item @var{X}: The actual time stamp for the current function evaluation * @item @var{Y}: The function values from the last successful integration step of length @var{N} * @item @var{DFY}: The values of partial derivatives of the Jacobian matrix of size @var{N} * @item @var{LDFY}: * @item @var{RPAR}: The real parameters that are passed to the user function (unused) * @item @var{IPAR}: The integer parameters that are passed to the user function (unused) * @end itemize * @end deftypefn */ octave_idx_type odepkg_rodas_jacfcn (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (X); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evaljacode (vrodasjacfun, vt, vy, vrodasextarg); Matrix vdfy = vout.matrix_value (); for (octave_idx_type vcol = 0; vcol < N; vcol++) for (octave_idx_type vrow = 0; vrow < N; vrow++) DFY[vrow+vcol*N] = vdfy (vrow, vcol); return (true); } /* -*- texinfo -*- * odepkg_rodas_massfcn - TODO */ F77_RET_T odepkg_rodas_massfcn (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = 0.0; // warning_with_id ("OdePkg:InvalidFunctionCall", // "Rodas can only handle M()=const Mass matrices"); // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (0.0); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evalmassode (vrodasmass, vrodasmassstate, vt, vy, vrodasextarg); Matrix vam = vout.matrix_value (); for (octave_idx_type vrow = 0; vrow < N; vrow++) for (octave_idx_type vcol = 0; vcol < N; vcol++) AM[vrow+vcol*N] = vam (vrow, vcol); return (true); } /* -*- texinfo -*- * odepkg_rodas_solfcn - TODO */ octave_idx_type odepkg_rodas_solfcn (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* CONT, const octave_idx_type* LRC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Output function, the solstore function or the Events function octave_value vt = octave_value (X); octave_value vy = octave_value (A); // Check if an 'Events' function has been set by the user if (!vrodasevefun.is_empty ()) { vrodasevesol = odepkg_auxiliary_evaleventfun (vrodasevefun, vt, vy, vrodasextarg, 1); if (!vrodasevesol(0).cell_value ()(0).is_empty ()) if (vrodasevesol(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = vrodasevesol(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = vrodasevesol(0).cell_value ()(3).matrix_value (); vt = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vy = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); IRTRN = (vrodasevesol(0).cell_value ()(0).int_value () ? -1 : 0); } } // Save the solutions that come from the Fortran core solver if this // is not the initial first call to this function if (NR > 1) odepkg_auxiliary_solstore (vt, vy, 1); // Check if an 'OutputFcn' has been set by the user (including the // values of the options for 'OutputSel' and 'Refine') if (!vrodaspltfun.is_empty ()) { if (vrodasrefine.int_value () > 0) { ColumnVector B(N); double vtb = 0.0; for (octave_idx_type vcnt = 1; vcnt < vrodasrefine.int_value (); vcnt++) { // Calculate time stamps between XOLD and X and get the // results at these time stamps vtb = (X - XOLD) * vcnt / vrodasrefine.int_value () + XOLD; for (octave_idx_type vcou = 0; vcou < N; vcou++) B(vcou) = F77_FUNC (contro, CONTRO) (vcou+1, vtb, CONT, LRC); // Evaluate the 'OutputFcn' with the approximated values from // the F77_FUNC before the output of the results is done octave_value vyr = octave_value (B); octave_value vtr = octave_value (vtb); odepkg_auxiliary_evalplotfun (vrodaspltfun, vrodasoutsel, vtr, vyr, vrodasextarg, 1); } } // Evaluate the 'OutputFcn' with the results from the solver, if // the OutputFcn returns true then set a negative value in IRTRN IRTRN = - odepkg_auxiliary_evalplotfun (vrodaspltfun, vrodasoutsel, vt, vy, vrodasextarg, 1); } return (true); } /* -*- texinfo -*- * odepkg_rodas_solfcn - TODO dummy function */ octave_idx_type odepkg_rodas_dfxfcn (GCC_ATTR_UNUSED const octave_idx_type& N, GCC_ATTR_UNUSED const double& X, GCC_ATTR_UNUSED const double* Y, GCC_ATTR_UNUSED const double* FX, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { warning_with_id ("OdePkg:InvalidFunctionCall", "function odepkg_rodas_dfxfcn: This warning message should never appear"); return (true); } // PKG_ADD: autoload ("oders", "dldsolver.oct"); DEFUN_DLD (oders, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Loadable Function} {[@var{}] =} oders (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{sol}] =} oders (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} oders (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ \n\ This function file can be used to solve a set of stiff ordinary differential equations (ODEs) and stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{rodas.f}.\n\ \n\ If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.\n\ \n\ If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.\n\ \n\ If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.\n\ \n\ For example,\n\ @example\n\ function y = odepkg_equations_lorenz (t, x)\n\ y = [10 * (x(2) - x(1));\n\ x(1) * (28 - x(3));\n\ x(1) * x(2) - 8/3 * x(3)];\n\ endfunction\n\ \n\ vopt = odeset (\"InitialStep\", 1e-3, \"MaxStep\", 1e-1, \\\n\ \"OutputFcn\", @@odephas3, \"Refine\", 5);\n\ oders (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt);\n\ @end example\n\ @end deftypefn\n\ \n\ @seealso{odepkg}") { octave_idx_type nargin = args.length (); // The number of input arguments octave_value_list vretval; // The cell array of return args octave_scalar_map vodeopt; // The OdePkg options structure // Check number and types of all input arguments if (nargin < 3) { print_usage (); return (vretval); } // If args(0)==function_handle is valid then set the vrodasodefun // variable that has been defined "static" before if (!args(0).is_function_handle () && !args(0).is_inline_function ()) { error_with_id ("OdePkg:InvalidArgument", "First input argument must be a valid function handle"); return (vretval); } else // We store the args(0) argument in the static variable vrodasodefun vrodasodefun = args(0); // Check if the second input argument is a valid vector describing // the time window that should be solved, it may be of length 2 ONLY if (args(1).is_scalar_type () || !odepkg_auxiliary_isvector (args(1))) { error_with_id ("OdePkg:InvalidArgument", "Second input argument must be a valid vector"); return (vretval); } // Check if the thirt input argument is a valid vector describing // the initial values of the variables of the differential equations if (!odepkg_auxiliary_isvector (args(2))) { error_with_id ("OdePkg:InvalidArgument", "Third input argument must be a valid vector"); return (vretval); } // Check if there are further input arguments ie. the options // structure and/or arguments that need to be passed to the // OutputFcn, Events and/or Jacobian etc. if (nargin >= 4) { // Fourth input argument != OdePkg option, need a default structure if (!args(3).is_map ()) { octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure for (octave_idx_type vcnt = 3; vcnt < nargin; vcnt++) vrodasextarg(vcnt-3) = args(vcnt); // Save arguments in vrodasextarg } // Fourth input argument == OdePkg option, extra input args given too else if (nargin > 4) { octave_value_list varin; varin(0) = args(3); varin(1) = "oders"; octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create structure from args(4) for (octave_idx_type vcnt = 4; vcnt < nargin; vcnt++) vrodasextarg(vcnt-4) = args(vcnt); // Save extra arguments } // Fourth input argument == OdePkg option, no extra input args given else { octave_value_list varin; varin(0) = args(3); varin(1) = "oders"; // Check structure octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } } // if (nargin >= 4) else { // if nargin == 3, everything else has been checked before octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } /* Start PREPROCESSING, ie. check which options have been set and * print warnings if there are options that can't be handled by this * solver or have not been implemented yet *******************************************************************/ // Implementation of the option RelTol has been finished, this // option can be set by the user to another value than default value octave_value vreltol = vodeopt.contents ("RelTol"); if (vreltol.is_empty ()) { vreltol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"RelTol\" not set, new value %3.1e is used", vreltol.double_value ()); } // vreltol.print (octave_stdout, true); return (vretval); if (!vreltol.is_scalar_type ()) { if (vreltol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"RelTol\" must be the same as the number of equations"); return (vretval); } } // Implementation of the option AbsTol has been finished, this // option can be set by the user to another value than default value octave_value vabstol = vodeopt.contents ("AbsTol"); if (vabstol.is_empty ()) { vabstol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"AbsTol\" not set, new value %3.1e is used", vabstol.double_value ()); } // vabstol.print (octave_stdout, true); return (vretval); if (!vabstol.is_scalar_type ()) { if (vabstol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"AbsTol\" must be the same as the number of equations"); return (vretval); } } // Setting the tolerance type that depends on the types (scalar or // vector) of the options RelTol and AbsTol octave_idx_type vitol = 0; if (vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 0; else if (!vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 1; else { error_with_id ("OdePkg:InvalidOption", "Values of \"RelTol\" and \"AbsTol\" must have same length"); return (vretval); } // The option NormControl will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnorm = vodeopt.contents ("NormControl"); if (!vnorm.is_empty ()) if (vnorm.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"NormControl\" will be ignored by this solver"); // The option NonNegative will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnneg = vodeopt.contents ("NonNegative"); if (!vnneg.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NonNegative\" will be ignored by this solver"); // Implementation of the option OutputFcn has been finished, this // option can be set by the user to another value than default value vrodaspltfun = vodeopt.contents ("OutputFcn"); if (vrodaspltfun.is_empty () && nargout == 0) vrodaspltfun = "odeplot"; // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vrodasoutsel = vodeopt.contents ("OutputSel"); // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vrodasrefine = vodeopt.contents ("Refine"); // Implementation of the option Stats has been finished, this option // can be set by the user to another value than default value octave_value vstats = vodeopt.contents ("Stats"); // Implementation of the option InitialStep has been finished, this // option can be set by the user to another value than default value octave_value vinitstep = vodeopt.contents ("InitialStep"); if (args(1).length () > 2) { error_with_id ("OdePkg:InvalidOption", "Fixed time stamps are not supported by this solver"); } if (vinitstep.is_empty ()) { vinitstep = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" not set, new value %3.1e is used", vinitstep.double_value ()); } // Implementation of the option MaxStep has been finished, this // option can be set by the user to another value than default value octave_value vmaxstep = vodeopt.contents ("MaxStep"); if (vmaxstep.is_empty () && args(1).length () == 2) { vmaxstep = (args(1).vector_value ()(1) - args(1).vector_value ()(0)) / 12.5; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxStep\" not set, new value %3.1e is used", vmaxstep.double_value ()); } // Implementation of the option Events has been finished, this // option can be set by the user to another value than default // value, odepkg_structure_check already checks for a valid value vrodasevefun = vodeopt.contents ("Events"); // Implementation of the option 'Jacobian' has been finished, these // options can be set by the user to another value than default vrodasjacfun = vodeopt.contents ("Jacobian"); octave_idx_type vrodasjac = 0; // We need to set this if no Jac available if (!vrodasjacfun.is_empty ()) vrodasjac = 1; // The option JPattern will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vrodasjacpat = vodeopt.contents ("JPattern"); if (!vrodasjacpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"JPattern\" will be ignored by this solver"); // The option Vectorized will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vrodasvectorize = vodeopt.contents ("Vectorized"); if (!vrodasvectorize.is_empty ()) if (vrodasvectorize.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"Vectorized\" will be ignored by this solver"); // Implementation of the option 'Mass' has been finished, these // options can be set by the user to another value than default vrodasmass = vodeopt.contents ("Mass"); octave_idx_type vrodasmas = 0; if (!vrodasmass.is_empty ()) { vrodasmas = 1; if (vrodasmass.is_function_handle () || vrodasmass.is_inline_function ()) warning_with_id ("OdePkg:InvalidOption", "Option \"Mass\" only supports constant mass matrices M() and not M(t,y)"); } // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option vrodasmassstate = vodeopt.contents ("MStateDependence"); if (!vrodasmassstate.is_empty ()) if (vrodasmassstate.string_value ().compare ("weak") != 0) // 'weak' is default warning_with_id ("OdePkg:InvalidOption", "Option \"MStateDependence\" will be ignored by this solver"); // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmvpat = vodeopt.contents ("MvPattern"); if (!vmvpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MvPattern\" will be ignored by this solver"); // The option MassSingular will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmsing = vodeopt.contents ("MassSingular"); if (!vmsing.is_empty ()) if (vmsing.string_value ().compare ("maybe") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MassSingular\" will be ignored by this solver"); // The option InitialSlope will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vinitslope = vodeopt.contents ("InitialSlope"); if (!vinitslope.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialSlope\" will be ignored by this solver"); // The option MaxOrder will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vmaxder = vodeopt.contents ("MaxOrder"); if (!vmaxder.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" will be ignored by this solver"); // The option BDF will be ignored by this solver, the core Fortran // solver doesn't support this option octave_value vbdf = vodeopt.contents ("BDF"); if (!vbdf.is_empty ()) if (vbdf.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"BDF\" will be ignored by this solver"); // The option NewtonTol and MaxNewtonIterations will be ignored by // this solver, IT NEEDS TO BE CHECKED IF THE FORTRAN CORE SOLVER // CAN HANDLE THESE OPTIONS octave_value vntol = vodeopt.contents ("NewtonTol"); if (!vntol.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NewtonTol\" will be ignored by this solver"); octave_value vmaxnewton = vodeopt.contents ("MaxNewtonIterations"); if (!vmaxnewton.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" will be ignored by this solver"); /* Start MAINPROCESSING, set up all variables that are needed by this * solver and then initialize the solver function and get into the * main integration loop ********************************************************************/ NDArray vY0 = args(2).array_value (); NDArray vRTOL = vreltol.array_value (); NDArray vATOL = vabstol.array_value (); octave_idx_type N = args(2).length (); octave_idx_type IFCN = 1; double X = args(1).vector_value ()(0); double* Y = vY0.fortran_vec (); double XEND = args(1).vector_value ()(1); double H = vinitstep.double_value (); double *RTOL = vRTOL.fortran_vec (); double *ATOL = vATOL.fortran_vec (); octave_idx_type ITOL = vitol; octave_idx_type IJAC = vrodasjac; octave_idx_type MLJAC=N; octave_idx_type MUJAC=N; octave_idx_type IDFX=0; octave_idx_type IMAS=vrodasmas; octave_idx_type MLMAS=N; octave_idx_type MUMAS=N; octave_idx_type IOUT = 1; // The SOLOUT function will always be called octave_idx_type LWORK = N*(N+N+N+14)+20; OCTAVE_LOCAL_BUFFER (double, WORK, LWORK); for (octave_idx_type vcnt = 0; vcnt < LWORK; vcnt++) WORK[vcnt] = 0.0; octave_idx_type LIWORK = N+20; OCTAVE_LOCAL_BUFFER (octave_idx_type, IWORK, LIWORK); for (octave_idx_type vcnt = 0; vcnt < LIWORK; vcnt++) IWORK[vcnt] = 0; double RPAR[1] = {0.0}; octave_idx_type IPAR[1] = {0}; octave_idx_type IDID = 0; WORK[1] = vmaxstep.double_value (); // Set the maximum step size // Check if the user has set some of the options "OutputFcn", "Events" // etc. and initialize the plot, events and the solstore functions octave_value vtim = args(1).vector_value ()(0); octave_value vsol = args(2); odepkg_auxiliary_solstore (vtim, vsol, 0); if (!vrodaspltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vrodaspltfun, vrodasoutsel, args(1), args(2), vrodasextarg, 0); if (!vrodasevefun.is_empty ()) odepkg_auxiliary_evaleventfun (vrodasevefun, vtim, args(2), vrodasextarg, 0); // We are calling the core solver and solve the set of ODEs or DAEs F77_XFCN (rodas, RODAS, // Keep 5 arguments per line here (N, odepkg_rodas_usrfcn, IFCN, X, Y, XEND, H, RTOL, ATOL, ITOL, odepkg_rodas_jacfcn, IJAC, MLJAC, MUJAC, odepkg_rodas_dfxfcn, IDFX, odepkg_rodas_massfcn, IMAS, MLMAS, MUMAS, odepkg_rodas_solfcn, IOUT, WORK, LWORK, IWORK, LIWORK, RPAR, IPAR, IDID)); if (IDID < 0) { // odepkg_auxiliary_mebdfanalysis (IDID); error_with_id ("hugh:hugh", "missing implementation"); vretval(0) = 0.0; return (vretval); } /* Start POSTPROCESSING, check how many arguments should be returned * to the caller and check which extra arguments have to be set *******************************************************************/ // Return the results that have been stored in the // odepkg_auxiliary_solstore function octave_value vtres, vyres; odepkg_auxiliary_solstore (vtres, vyres, 2); // Set up variables to make it possible to call the cleanup // functions of 'OutputFcn' and 'Events' if any Matrix vlastline; vlastline = vyres.matrix_value (); vlastline = vlastline.extract (vlastline.rows () - 1, 0, vlastline.rows () - 1, vlastline.cols () - 1); octave_value vted = octave_value (XEND); octave_value vfin = octave_value (vlastline); if (!vrodaspltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vrodaspltfun, vrodasoutsel, vted, vfin, vrodasextarg, 2); if (!vrodasevefun.is_empty ()) odepkg_auxiliary_evaleventfun (vrodasevefun, vted, vfin, vrodasextarg, 2); // Get the stats information as an octave_scalar_map if the option 'Stats' // has been set with odeset octave_value_list vstatinput; vstatinput(0) = IWORK[16]; vstatinput(1) = IWORK[17]; vstatinput(2) = IWORK[13]; vstatinput(3) = IWORK[14]; vstatinput(4) = IWORK[18]; vstatinput(5) = IWORK[19]; octave_value vstatinfo; if ((vstats.string_value ().compare ("on") == 0) && (nargout == 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, false); else if ((vstats.string_value ().compare ("on") == 0) && (nargout != 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, true); // Set up output arguments that depend on how many output arguments // are desired from the caller if (nargout == 1) { octave_scalar_map vretmap; vretmap.assign ("x", vtres); vretmap.assign ("y", vyres); vretmap.assign ("solver", "oders"); if (!vstatinfo.is_empty ()) // Event implementation vretmap.assign ("stats", vstatinfo); if (!vrodasevefun.is_empty ()) { vretmap.assign ("ie", vrodasevesol(0).cell_value ()(1)); vretmap.assign ("xe", vrodasevesol(0).cell_value ()(2)); vretmap.assign ("ye", vrodasevesol(0).cell_value ()(3)); } vretval(0) = octave_value (vretmap); } else if (nargout == 2) { vretval(0) = vtres; vretval(1) = vyres; } else if (nargout == 5) { Matrix vempty; // prepare an empty matrix vretval(0) = vtres; vretval(1) = vyres; vretval(2) = vempty; vretval(3) = vempty; vretval(4) = vempty; if (!vrodasevefun.is_empty ()) { vretval(2) = vrodasevesol(0).cell_value ()(2); vretval(3) = vrodasevesol(0).cell_value ()(3); vretval(4) = vrodasevesol(0).cell_value ()(1); } } return (vretval); } /* %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference solut %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (size (vt) != [2, 1] && size (vt) != [1, 2]) %! error ('"fout" step "init"'); %! end %! elseif (isempty (vflag)) %! if (size (vt) ~= [1, 1]) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (size (vt) ~= [1, 1]) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = oders (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = oders (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = oders (@fpol, [0 25], 1); %!error %# fixed step sizes not supported %! B = oders (@fpol, [0:0.1:2], [2 0]); %!test %# one output argument %! vsol = oders (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'oders'); %!test %# two output arguments %! [vt, vy] = oders (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = oders (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = oders (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = oders (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = oders (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-4); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-4); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = oders (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = oders (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = oders (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = oders (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = oders (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 1e-1); %! warning ('on', 'OdePkg:HideWarning'); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set NewtonTol option to something else than default %! vopt = odeset ('NewtonTol', 1e-3); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set MaxNewtonIterations option to something else than default %! vopt = odeset ('MaxNewtonIterations', 2); %! vsol = oders (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! warning ('on', 'OdePkg:InvalidOption'); */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */ odepkg-0.8.5/src/odepkg_octsolver_seulex.cc0000644000000000000000000011502412526637474017202 0ustar 00000000000000/* Copyright (C) 2007-2012, Thomas Treichl OdePkg - A package for solving ordinary differential equations and more 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; either version 2 of the License, or (at your option) any later version. 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 . */ /* Compile this file manually and run some tests with the following command bash:~$ mkoctfile -v -Wall -W -Wshadow odepkg_octsolver_seulex.cc \ odepkg_auxiliary_functions.cc hairer/seulex.f hairer/dc_decsol.f \ hairer/decsol.f -o odesx.oct octave> octave --quiet --eval "autoload ('odesx', [pwd, '/odesx.oct']); \ test 'odepkg_octsolver_seulex.cc'" For an explanation about various parts of this source file cf. the source file odepkg_octsolver_rodas.cc. The implementation of that file is very similiar to the implementation of this file. */ #include #include #include #include #include #include "odepkg_auxiliary_functions.h" typedef octave_idx_type (*odepkg_seulex_usrtype) (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_seulex_jactype) (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_seulex_masstype) (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR); typedef octave_idx_type (*odepkg_seulex_soltype) (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* RC, const octave_idx_type& LRC, const double* IC, const octave_idx_type& LIC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN); extern "C" { F77_RET_T F77_FUNC (seulex, SEULEX) (const octave_idx_type& N, odepkg_seulex_usrtype, const octave_idx_type& IFCN, const double& X, const double* Y, const double& XEND, const double& H, const double* RTOL, const double* ATOL, const octave_idx_type& ITOL, odepkg_seulex_jactype, const octave_idx_type& IJAC, const octave_idx_type& MLJAC, const octave_idx_type& MUJAC, odepkg_seulex_masstype, const octave_idx_type& IMAS, const octave_idx_type& MLMAS, const octave_idx_type& MUMAS, odepkg_seulex_soltype, const octave_idx_type& IOUT, const double* WORK, const octave_idx_type& LWORK, const octave_idx_type* IWORK, const octave_idx_type& LIWORK, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, const octave_idx_type& IDID); double F77_FUNC (contex, CONTEX) (const octave_idx_type& I, const double& S, const double* RC, const octave_idx_type& LRC, const double* IC, const octave_idx_type& LIC); } // extern "C" static octave_value_list vseulexextarg; static octave_value vseulexodefun; static octave_value vseulexjacfun; static octave_value vseulexevefun; static octave_value vseulexevebrk; static octave_value_list vseulexevesol; static octave_value vseulexpltfun; static octave_value vseulexpltbrk; static octave_value vseulexoutsel; static octave_value vseulexrefine; static octave_value vseulexmass; static octave_value vseulexmassstate; octave_idx_type odepkg_seulex_usrfcn (const octave_idx_type& N, const double& X, const double* Y, double* F, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element wise, // otherwise Octave will crash if these variables will be freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) { A(vcnt) = Y[vcnt]; // octave_stdout << "I am here Y[" << vcnt << "] " << Y[vcnt] << std::endl; // octave_stdout << "I am here T " << X << std::endl; } // Fill the variable for the input arguments before evaluating the // function that keeps the set of differential algebraic equations octave_value_list varin; varin(0) = X; varin(1) = A; for (octave_idx_type vcnt = 0; vcnt < vseulexextarg.length (); vcnt++) varin(vcnt+2) = vseulexextarg(vcnt); octave_value_list vout = feval (vseulexodefun.function_value (), varin, 1); // Return the results from the function evaluation to the Fortran // solver, again copy them and don't just create a Fortran vector ColumnVector vcol = vout(0).column_vector_value (); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) F[vcnt] = vcol(vcnt); return (true); } octave_idx_type odepkg_seulex_jacfcn (const octave_idx_type& N, const double& X, const double* Y, double* DFY, GCC_ATTR_UNUSED const octave_idx_type& LDFY, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (X); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evaljacode (vseulexjacfun, vt, vy, vseulexextarg); Matrix vdfy = vout.matrix_value (); for (octave_idx_type vcol = 0; vcol < N; vcol++) for (octave_idx_type vrow = 0; vrow < N; vrow++) DFY[vrow+vcol*N] = vdfy (vrow, vcol); return (true); } F77_RET_T odepkg_seulex_massfcn (const octave_idx_type& N, double* AM, GCC_ATTR_UNUSED const octave_idx_type* LMAS, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = 0.0; // warning_with_id ("OdePkg:InvalidFunctionCall", // "Seulex can only handle M()=const Mass matrices"); // Set the values that are needed as input arguments before calling // the Jacobian function and then call the Jacobian interface octave_value vt = octave_value (0.0); octave_value vy = octave_value (A); octave_value vout = odepkg_auxiliary_evalmassode (vseulexmass, vseulexmassstate, vt, vy, vseulexextarg); Matrix vam = vout.matrix_value (); for (octave_idx_type vrow = 0; vrow < N; vrow++) for (octave_idx_type vcol = 0; vcol < N; vcol++) AM[vrow+vcol*N] = vam (vrow, vcol); return (true); } octave_idx_type odepkg_seulex_solfcn (const octave_idx_type& NR, const double& XOLD, const double& X, const double* Y, const double* RC, const octave_idx_type& LRC, const double* IC, const octave_idx_type& LIC, const octave_idx_type& N, GCC_ATTR_UNUSED const double* RPAR, GCC_ATTR_UNUSED const octave_idx_type* IPAR, octave_idx_type& IRTRN) { // Copy the values that come from the Fortran function element-wise, // otherwise Octave will crash if these variables are freed ColumnVector A(N); for (octave_idx_type vcnt = 0; vcnt < N; vcnt++) A(vcnt) = Y[vcnt]; // Set the values that are needed as input arguments before calling // the Output function, the solstore function or the Events function octave_value vt = octave_value (X); octave_value vy = octave_value (A); vseulexevebrk = false; // Check if an 'Events' function has been set by the user if (!vseulexevefun.is_empty ()) { vseulexevesol = odepkg_auxiliary_evaleventfun (vseulexevefun, vt, vy, vseulexextarg, 1); if (!vseulexevesol(0).cell_value ()(0).is_empty ()) if (vseulexevesol(0).cell_value ()(0).int_value () == 1) { ColumnVector vttmp = vseulexevesol(0).cell_value ()(2).column_vector_value (); Matrix vrtmp = vseulexevesol(0).cell_value ()(3).matrix_value (); vt = vttmp.extract (vttmp.length () - 1, vttmp.length () - 1); vy = vrtmp.extract (vrtmp.rows () - 1, 0, vrtmp.rows () - 1, vrtmp.cols () - 1); IRTRN = (vseulexevesol(0).cell_value ()(0).int_value () ? -1 : 0); vseulexevebrk = true; } } // Save the solutions that come from the Fortran core solver if this // is not the initial first call to this function if (NR > 1) odepkg_auxiliary_solstore (vt, vy, 1); // Check if an 'OutputFcn' has been set by the user (including the // values of the options for 'OutputSel' and 'Refine') vseulexpltbrk = false; if (!vseulexpltfun.is_empty ()) { if (vseulexrefine.int_value () > 0) { ColumnVector B(N); double vtb = 0.0; for (octave_idx_type vcnt = 1; vcnt < vseulexrefine.int_value (); vcnt++) { // Calculate time stamps between XOLD and X and get the // results at these time stamps vtb = (X - XOLD) * vcnt / vseulexrefine.int_value () + XOLD; for (octave_idx_type vcou = 0; vcou < N; vcou++) B(vcou) = F77_FUNC (contex, CONTEX) (vcou+1, vtb, RC, LRC, IC, LIC); // Evaluate the 'OutputFcn' with the approximated values from // the F77_FUNC before the output of the results is done octave_value vyr = octave_value (B); octave_value vtr = octave_value (vtb); odepkg_auxiliary_evalplotfun (vseulexpltfun, vseulexoutsel, vtr, vyr, vseulexextarg, 1); } } // Evaluate the 'OutputFcn' with the results from the solver, if // the OutputFcn returns true then set a negative value in IRTRN IRTRN = - odepkg_auxiliary_evalplotfun (vseulexpltfun, vseulexoutsel, vt, vy, vseulexextarg, 1); vseulexpltbrk = true; } return (true); } // PKG_ADD: autoload ("odesx", "dldsolver.oct"); DEFUN_DLD (odesx, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Command} {[@var{}] =} odesx (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{sol}] =} odesx (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} odesx (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])\n\ \n\ This function file can be used to solve a set of stiff ordinary differential equations (ODEs) and stiff differential algebraic equations (DAEs). This function file is a wrapper to Hairer's and Wanner's Fortran solver @file{seulex.f}.\n\ \n\ If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.\n\ \n\ If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.\n\ \n\ If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.\n\ \n\ For example,\n\ @example\n\ function y = odepkg_equations_lorenz (t, x)\n\ y = [10 * (x(2) - x(1));\n\ x(1) * (28 - x(3));\n\ x(1) * x(2) - 8/3 * x(3)];\n\ endfunction\n\ \n\ vopt = odeset (\"InitialStep\", 1e-3, \"MaxStep\", 1e-1, \\\n\ \"OutputFcn\", @@odephas3, \"Refine\", 5);\n\ odesx (@@odepkg_equations_lorenz, [0, 25], [3 15 1], vopt);\n\ @end example\n\ @end deftypefn\n\ \n\ @seealso{odepkg}") { octave_idx_type nargin = args.length (); // The number of input arguments octave_value_list vretval; // The cell array of return args octave_scalar_map vodeopt; // The OdePkg options structure // Check number and types of all input arguments if (nargin < 3) { print_usage (); return (vretval); } // If args(0)==function_handle is valid then set the vseulexodefun // variable that has been defined "static" before if (!args(0).is_function_handle () && !args(0).is_inline_function ()) { error_with_id ("OdePkg:InvalidArgument", "First input argument must be a valid function handle"); return (vretval); } else // We store the args(0) argument in the static variable vseulexodefun vseulexodefun = args(0); // Check if the second input argument is a valid vector describing // the time window that should be solved, it may be of length 2 ONLY if (args(1).is_scalar_type () || !odepkg_auxiliary_isvector (args(1))) { error_with_id ("OdePkg:InvalidArgument", "Second input argument must be a valid vector"); return (vretval); } // Check if the thirt input argument is a valid vector describing // the initial values of the variables of the differential equations if (!odepkg_auxiliary_isvector (args(2))) { error_with_id ("OdePkg:InvalidArgument", "Third input argument must be a valid vector"); return (vretval); } // Check if there are further input arguments ie. the options // structure and/or arguments that need to be passed to the // OutputFcn, Events and/or Jacobian etc. if (nargin >= 4) { // Fourth input argument != OdePkg option, need a default structure if (!args(3).is_map ()) { octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure for (octave_idx_type vcnt = 3; vcnt < nargin; vcnt++) vseulexextarg(vcnt-3) = args(vcnt); // Save arguments in vseulexextarg } // Fourth input argument == OdePkg option, extra input args given too else if (nargin > 4) { octave_value_list varin; varin(0) = args(3); varin(1) = "odesx"; octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create structure from args(4) for (octave_idx_type vcnt = 4; vcnt < nargin; vcnt++) vseulexextarg(vcnt-4) = args(vcnt); // Save extra arguments } // Fourth input argument == OdePkg option, no extra input args given else { octave_value_list varin; varin(0) = args(3); varin(1) = "odesx"; // Check structure octave_value_list tmp = feval ("odepkg_structure_check", varin, 1); if (error_state) return (vretval); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } } // if (nargin >= 4) else { // if nargin == 3, everything else has been checked before octave_value_list tmp = feval ("odeset", tmp, 1); vodeopt = tmp(0).scalar_map_value (); // Create a default structure } /* Start PREPROCESSING, ie. check which options have been set and * print warnings if there are options that can't be handled by this * solver or have not been implemented yet *******************************************************************/ // Implementation of the option RelTol has been finished, this // option can be set by the user to another value than default value octave_value vreltol = vodeopt.contents ("RelTol"); if (vreltol.is_empty ()) { vreltol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"RelTol\" not set, new value %3.1e is used", vreltol.double_value ()); } // vreltol.print (octave_stdout, true); return (vretval); if (!vreltol.is_scalar_type ()) { if (vreltol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"RelTol\" must be the same as the number of equations"); return (vretval); } } // Implementation of the option AbsTol has been finished, this // option can be set by the user to another value than default value octave_value vabstol = vodeopt.contents ("AbsTol"); if (vabstol.is_empty ()) { vabstol = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"AbsTol\" not set, new value %3.1e is used", vabstol.double_value ()); } // vabstol.print (octave_stdout, true); return (vretval); if (!vabstol.is_scalar_type ()) { if (vabstol.length () != args(2).length ()) { error_with_id ("OdePkg:InvalidOption", "Length of option \"AbsTol\" must be the same as the number of equations"); return (vretval); } } // Setting the tolerance type that depends on the types (scalar or // vector) of the options RelTol and AbsTol octave_idx_type vitol = 0; if (vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 0; else if (!vreltol.is_scalar_type () && (vreltol.length () == vabstol.length ())) vitol = 1; else { error_with_id ("OdePkg:InvalidOption", "Values of \"RelTol\" and \"AbsTol\" must have same length"); return (vretval); } // The option NormControl will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnorm = vodeopt.contents ("NormControl"); if (!vnorm.is_empty ()) if (vnorm.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"NormControl\" will be ignored by this solver"); // The option NonNegative will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vnneg = vodeopt.contents ("NonNegative"); if (!vnneg.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NonNegative\" will be ignored by this solver"); // Implementation of the option OutputFcn has been finished, this // option can be set by the user to another value than default value vseulexpltfun = vodeopt.contents ("OutputFcn"); if (vseulexpltfun.is_empty () && nargout == 0) vseulexpltfun = "odeplot"; // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vseulexoutsel = vodeopt.contents ("OutputSel"); // Implementation of the option OutputSel has been finished, this // option can be set by the user to another value than default value vseulexrefine = vodeopt.contents ("Refine"); // Implementation of the option Stats has been finished, this option // can be set by the user to another value than default value octave_value vstats = vodeopt.contents ("Stats"); // Implementation of the option InitialStep has been finished, this // option can be set by the user to another value than default value octave_value vinitstep = vodeopt.contents ("InitialStep"); if (args(1).length () > 2) { error_with_id ("OdePkg:InvalidOption", "Fixed time stamps are not supported by this solver"); } if (vinitstep.is_empty ()) { vinitstep = 1.0e-6; warning_with_id ("OdePkg:InvalidOption", "Option \"InitialStep\" not set, new value %3.1e is used", vinitstep.double_value ()); } // Implementation of the option MaxStep has been finished, this // option can be set by the user to another value than default value octave_value vmaxstep = vodeopt.contents ("MaxStep"); if (vmaxstep.is_empty () && args(1).length () == 2) { vmaxstep = (args(1).vector_value ()(1) - args(1).vector_value ()(0)) / 12.5; warning_with_id ("OdePkg:InvalidOption", "Option \"MaxStep\" not set, new value %3.1e is used", vmaxstep.double_value ()); } // Implementation of the option Events has been finished, this // option can be set by the user to another value than default // value, odepkg_structure_check already checks for a valid value vseulexevefun = vodeopt.contents ("Events"); // Implementation of the option 'Jacobian' has been finished, these // options can be set by the user to another value than default vseulexjacfun = vodeopt.contents ("Jacobian"); octave_idx_type vseulexjac = 0; // We need to set this if no Jac available if (!vseulexjacfun.is_empty ()) vseulexjac = 1; // The option JPattern will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vseulexjacpat = vodeopt.contents ("JPattern"); if (!vseulexjacpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"JPattern\" will be ignored by this solver"); // The option Vectorized will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vseulexvectorize = vodeopt.contents ("Vectorized"); if (!vseulexvectorize.is_empty ()) if (vseulexvectorize.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"Vectorized\" will be ignored by this solver"); // Implementation of the option 'Mass' has been finished, these // options can be set by the user to another value than default vseulexmass = vodeopt.contents ("Mass"); octave_idx_type vseulexmas = 0; if (!vseulexmass.is_empty ()) { vseulexmas = 1; if (vseulexmass.is_function_handle () || vseulexmass.is_inline_function ()) warning_with_id ("OdePkg:InvalidOption", "Option \"Mass\" only supports constant mass matrices M() and not M(t,y)"); } // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option vseulexmassstate = vodeopt.contents ("MStateDependence"); if (!vseulexmassstate.is_empty ()) if (vseulexmassstate.string_value ().compare ("weak") != 0) // 'weak' is default warning_with_id ("OdePkg:InvalidOption", "Option \"MStateDependence\" will be ignored by this solver"); // The option MStateDependence will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmvpat = vodeopt.contents ("MvPattern"); if (!vmvpat.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MvPattern\" will be ignored by this solver"); // The option MassSingular will be ignored by this solver, the // core Fortran solver doesn't support this option octave_value vmsing = vodeopt.contents ("MassSingular"); if (!vmsing.is_empty ()) if (vmsing.string_value ().compare ("maybe") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"MassSingular\" will be ignored by this solver"); // The option InitialSlope will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vinitslope = vodeopt.contents ("InitialSlope"); if (!vinitslope.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"InitialSlope\" will be ignored by this solver"); // The option MaxOrder will be ignored by this solver, the core // Fortran solver doesn't support this option octave_value vmaxder = vodeopt.contents ("MaxOrder"); if (!vmaxder.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxOrder\" will be ignored by this solver"); // The option BDF will be ignored by this solver, the core Fortran // solver doesn't support this option octave_value vbdf = vodeopt.contents ("BDF"); if (!vbdf.is_empty ()) if (vbdf.string_value ().compare ("off") != 0) warning_with_id ("OdePkg:InvalidOption", "Option \"BDF\" will be ignored by this solver"); // The option NewtonTol and MaxNewtonIterations will be ignored by // this solver, IT NEEDS TO BE CHECKED IF THE FORTRAN CORE SOLVER // CAN HANDLE THESE OPTIONS octave_value vntol = vodeopt.contents ("NewtonTol"); if (!vntol.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"NewtonTol\" will be ignored by this solver"); octave_value vmaxnewton = vodeopt.contents ("MaxNewtonIterations"); if (!vmaxnewton.is_empty ()) warning_with_id ("OdePkg:InvalidOption", "Option \"MaxNewtonIterations\" will be ignored by this solver"); /* Start MAINPROCESSING, set up all variables that are needed by this * solver and then initialize the solver function and get into the * main integration loop ********************************************************************/ NDArray vY0 = args(2).array_value (); NDArray vRTOL = vreltol.array_value (); NDArray vATOL = vabstol.array_value (); octave_idx_type N = args(2).length (); octave_idx_type IFCN = 1; double X = args(1).vector_value ()(0); double* Y = vY0.fortran_vec (); double XEND = args(1).vector_value ()(1); double H = vinitstep.double_value (); double *RTOL = vRTOL.fortran_vec (); double *ATOL = vATOL.fortran_vec (); octave_idx_type ITOL = vitol; octave_idx_type IJAC = vseulexjac; octave_idx_type MLJAC=N; octave_idx_type MUJAC=N; octave_idx_type IMAS=vseulexmas; octave_idx_type MLMAS=N; octave_idx_type MUMAS=N; octave_idx_type IOUT = 1; // The SOLOUT function will always be called octave_idx_type LWORK = N*(N+N+N+12+8)+4*12+20+(2+12*(12+3)/2)*N; OCTAVE_LOCAL_BUFFER (double, WORK, LWORK); for (octave_idx_type vcnt = 0; vcnt < LWORK; vcnt++) WORK[vcnt] = 0.0; octave_idx_type LIWORK = 2*N+12+20+N; OCTAVE_LOCAL_BUFFER (octave_idx_type, IWORK, LIWORK); for (octave_idx_type vcnt = 0; vcnt < LIWORK; vcnt++) IWORK[vcnt] = 0; double RPAR[1] = {0.0}; octave_idx_type IPAR[1] = {0}; octave_idx_type IDID = 0; IWORK[0] = 1; // Switch for transformation of Jacobian into Hessenberg form WORK[2] = -1; // Recompute Jacobian after every succesful step WORK[6] = vmaxstep.double_value (); // Set the maximum step size // Check if the user has set some of the options "OutputFcn", "Events" // etc. and initialize the plot, events and the solstore functions octave_value vtim = args(1).vector_value ()(0); octave_value vsol = args(2); odepkg_auxiliary_solstore (vtim, vsol, 0); if (!vseulexpltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vseulexpltfun, vseulexoutsel, args(1), args(2), vseulexextarg, 0); if (!vseulexevefun.is_empty ()) odepkg_auxiliary_evaleventfun (vseulexevefun, vtim, args(2), vseulexextarg, 0); // We are calling the core solver and solve the set of ODEs or DAEs F77_XFCN (seulex, SEULEX, // Keep 5 arguments per line here (N, odepkg_seulex_usrfcn, IFCN, X, Y, XEND, H, RTOL, ATOL, ITOL, odepkg_seulex_jacfcn, IJAC, MLJAC, MUJAC, odepkg_seulex_massfcn, IMAS, MLMAS, MUMAS, odepkg_seulex_solfcn, IOUT, WORK, LWORK, IWORK, LIWORK, RPAR, IPAR, IDID)); // If the solver reported IDID < 0 then an error occured. *BUT* the // seulex solver also reports an error if no error occurs but a user // break is done because of the 'OutputFcn' or the 'Events' if (IDID < 0 && (vseulexpltbrk.bool_value () == false) && (vseulexevebrk.bool_value () == false)) { error_with_id ("hugh:hugh", "missing implementation, error after solving %d", IDID); vretval(0) = 0.0; return (vretval); } /* Start POSTPROCESSING, check how many arguments should be returned * to the caller and check which extra arguments have to be set *******************************************************************/ // Return the results that have been stored in the // odepkg_auxiliary_solstore function octave_value vtres, vyres; odepkg_auxiliary_solstore (vtres, vyres, 2); // Set up variables to make it possible to call the cleanup // functions of 'OutputFcn' and 'Events' if any Matrix vlastline; vlastline = vyres.matrix_value (); vlastline = vlastline.extract (vlastline.rows () - 1, 0, vlastline.rows () - 1, vlastline.cols () - 1); octave_value vted = octave_value (XEND); octave_value vfin = octave_value (vlastline); if (!vseulexpltfun.is_empty ()) odepkg_auxiliary_evalplotfun (vseulexpltfun, vseulexoutsel, vted, vfin, vseulexextarg, 2); if (!vseulexevefun.is_empty ()) odepkg_auxiliary_evaleventfun (vseulexevefun, vted, vfin, vseulexextarg, 2); // Get the stats information as an octave_scalar_map if the option 'Stats' // has been set with odeset octave_value_list vstatinput; vstatinput(0) = IWORK[16]; vstatinput(1) = IWORK[17]; vstatinput(2) = IWORK[13]; vstatinput(3) = IWORK[14]; vstatinput(4) = IWORK[18]; vstatinput(5) = IWORK[19]; octave_value vstatinfo; if ((vstats.string_value ().compare ("on") == 0) && (nargout == 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, false); else if ((vstats.string_value ().compare ("on") == 0) && (nargout != 1)) vstatinfo = odepkg_auxiliary_makestats (vstatinput, true); // Set up output arguments that depend on how many output arguments // are desired from the caller if (nargout == 1) { octave_scalar_map vretmap; vretmap.assign ("x", vtres); vretmap.assign ("y", vyres); vretmap.assign ("solver", "odesx"); if (!vstatinfo.is_empty ()) // Event implementation vretmap.assign ("stats", vstatinfo); if (!vseulexevefun.is_empty ()) { vretmap.assign ("ie", vseulexevesol(0).cell_value ()(1)); vretmap.assign ("xe", vseulexevesol(0).cell_value ()(2)); vretmap.assign ("ye", vseulexevesol(0).cell_value ()(3)); } vretval(0) = octave_value (vretmap); } else if (nargout == 2) { vretval(0) = vtres; vretval(1) = vyres; } else if (nargout == 5) { Matrix vempty; // prepare an empty matrix vretval(0) = vtres; vretval(1) = vyres; vretval(2) = vempty; vretval(3) = vempty; vretval(4) = vempty; if (!vseulexevefun.is_empty ()) { vretval(2) = vseulexevesol(0).cell_value ()(2); vretval(3) = vseulexevesol(0).cell_value ()(3); vretval(4) = vseulexevesol(0).cell_value ()(1); } } return (vretval); } /* %! # We are using the "Van der Pol" implementation for all tests that %! # are done for this function. We also define a Jacobian, Events, %! # pseudo-Mass implementation. For further tests we also define a %! # reference solution (computed at high accuracy) and an OutputFcn %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]; %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]); %!function [vval, vtrm, vdir] = feve (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = zeros (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin) %! vval = fpol (vt, vy, varargin); %# We use the derivatives %! vtrm = ones (2,1); %# that's why component 2 %! vdir = ones (2,1); %# seems to not be exact %!function [vmas] = fmas (vt, vy) %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests %!function [vmas] = fmsa (vt, vy) %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix %!function [vref] = fref () %# The computed reference solut %! vref = [0.32331666704577, -1.83297456798624]; %!function [vout] = fout (vt, vy, vflag, varargin) %! if (regexp (char (vflag), 'init') == 1) %! if (size (vt) != [2, 1] && size (vt) != [1, 2]) %! error ('"fout" step "init"'); %! end %! elseif (isempty (vflag)) %! if (size (vt) ~= [1, 1]) error ('"fout" step "calc"'); end %! vout = false; %! elseif (regexp (char (vflag), 'done') == 1) %! if (size (vt) ~= [1, 1]) error ('"fout" step "done"'); end %! else error ('"fout" invalid vflag'); %! end %! %! %# Turn off output of warning messages for all tests, turn them on %! %# again if the last test is called %!error %# input argument number one %! warning ('off', 'OdePkg:InvalidOption'); %! B = odesx (1, [0 25], [3 15 1]); %!error %# input argument number two %! B = odesx (@fpol, 1, [3 15 1]); %!error %# input argument number three %! B = odesx (@fpol, [0 25], 1); %!error %# fixed step sizes not supported %! B = odesx (@fpol, [0:0.1:2], [2 0]); %!test %# one output argument %! vsol = odesx (@fpol, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! assert (isfield (vsol, 'solver')); %! assert (vsol.solver, 'odesx'); %!test %# two output arguments %! [vt, vy] = odesx (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %!test %# five output arguments and no Events %! [vt, vy, vxe, vye, vie] = odesx (@fpol, [0 2], [2 0]); %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3); %! assert ([vie, vxe, vye], []); %!test %# anonymous function instead of real function %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)]; %! vsol = odesx (fvdb, [0 2], [2 0]); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# extra input arguments passed trhough %! vsol = odesx (@fpol, [0 2], [2 0], 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# empty OdePkg structure *but* extra input arguments %! vopt = odeset; %! vsol = odesx (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL'); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!error %# strange OdePkg structure %! vopt = struct ('foo', 1); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %!test %# AbsTol option %! vopt = odeset ('AbsTol', 1e-5); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# AbsTol and RelTol option %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# RelTol and NormControl option -- higher accuracy %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on'); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Keeps initial values while integrating %! vopt = odeset ('NonNegative', 2); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6); %!test %# Details of OutputSel and Refine can't be tested %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %!test %# Stats must add further elements in vsol %! vopt = odeset ('Stats', 'on'); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert (isfield (vsol, 'stats')); %! assert (isfield (vsol.stats, 'nsteps')); %!test %# InitialStep option %! vopt = odeset ('InitialStep', 1e-8); %! vsol = odesx (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-5); %!test %# MaxStep option %! vopt = odeset ('MaxStep', 1e-2); %! vsol = odesx (@fpol, [0 0.2], [2 0], vopt); %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2); %!test %# Events option add further elements in vsol %! vopt = odeset ('Events', @feve); %! vsol = odesx (@fpol, [0 10], [2 0], vopt); %! assert (isfield (vsol, 'ie')); %! assert (vsol.ie(1), 2); %! assert (isfield (vsol, 'xe')); %! assert (isfield (vsol, 'ye')); %!test %# Events option, now stop integration %! warning ('off', 'OdePkg:HideWarning'); %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! vsol = odesx (@fpol, [0 10], [2 0], vopt); %! assert ([vsol.ie, vsol.xe, vsol.ye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 0.5); %!test %# Events option, five output arguments %! vopt = odeset ('Events', @fevn, 'NormControl', 'on'); %! [vt, vy, vxe, vye, vie] = odesx (@fpol, [0 10], [2 0], vopt); %! assert ([vie, vxe, vye], ... %! [2.0, 2.496110, -0.830550, -2.677589], 0.5); %! warning ('on', 'OdePkg:HideWarning'); %!test %# Jacobian option %! vopt = odeset ('Jacobian', @fjac); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Jacobian option and sparse return value %! vopt = odeset ('Jacobian', @fjcc); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for JPattern option is missing %! %# test for Vectorized option is missing %! %!test %# Mass option as function %! vopt = odeset ('Mass', @fmas); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as matrix %! vopt = odeset ('Mass', eye (2,2)); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as sparse matrix %! vopt = odeset ('Mass', sparse (eye (2,2))); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and sparse matrix %! vopt = odeset ('Mass', @fmsa); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Mass option as function and MStateDependence %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong'); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! %# test for MvPattern option is missing %! %# test for InitialSlope option is missing %! %# test for MaxOrder option is missing %! %!test %# Set BDF option to something else than default %! vopt = odeset ('BDF', 'on'); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set NewtonTol option to something else than default %! vopt = odeset ('NewtonTol', 1e-3); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %!test %# Set MaxNewtonIterations option to something else than default %! vopt = odeset ('MaxNewtonIterations', 2); %! vsol = odesx (@fpol, [0 2], [2 0], vopt); %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3); %! %! warning ('on', 'OdePkg:InvalidOption'); */ /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */