package org.spaceroots.rkcheck;

import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.IOException;

import java.util.TreeSet;
import java.util.Iterator;

/**
 * This application checks the order conditions of Runge-Kutta
 * processes.

 * 

It reads the description of a Runge-Kutta method from an XML * file having a structure similar to this example: *

 * <?xml version="1.0" encoding="ISO-8859-1" ?>
 * <!DOCTYPE Runge-Kutta PUBLIC "-//spaceroots.org//DTD Runge-Kutta V1.2//EN"
 *                              "http://www.spaceroots.org/Runge-Kutta.dtd" >
 * 
 * <Runge-Kutta name="Higham and Hall" >
 * 
 *   <time-steps>
 *     <zero/>
 *     <rational> <p>2</p> <q>9</q> </rational>
 *     <rational> <p>1</p> <q>3</q> </rational>
 *     <rational> <p>1</p> <q>2</q> </rational>
 *     <rational> <p>3</p> <q>5</q> </rational>
 *     <one/>
 *     <one/>
 *   </time-steps>
 * 
 *   <internal-weights>
 *     <row>
 *       <!-- empty first row -->
 *     </row>
 *     <row>
 *       <rational> <p>  2</p> <q>  9</q> </rational>
 *     </row>
 *     <row>
 *       <rational> <p>  1</p> <q> 12</q> </rational>
 *       <rational> <p>  1</p> <q>  4</q> </rational>
 *     </row>
 *     <row>
 *       <rational> <p>  1</p> <q>  8</q> </rational>
 *       <zero/>
 *       <rational> <p>  3</p> <q>  8</q> </rational>
 *     </row>
 *     <row>
 *       <rational> <p> 91</p> <q>500</q> </rational>
 *       <rational> <p>-27</p> <q>100</q> </rational>
 *       <rational> <p> 78</p> <q>125</q> </rational>
 *       <rational> <p>  8</p> <q>125</q> </rational>
 *     </row>
 *     <row>
 *       <rational> <p>-11</p> <q> 20</q> </rational>
 *       <rational> <p> 27</p> <q> 20</q> </rational>
 *       <rational> <p> 12</p> <q>  5</q> </rational>
 *       <rational> <p>-36</p> <q>  5</q> </rational>
 *       <integer>5</integer>
 *     </row>
 *     <row>
 *       <rational> <p>  1</p> <q> 12</q> </rational>
 *       <zero/>
 *       <rational> <p> 27</p> <q> 32</q> </rational>
 *       <rational> <p> -4</p> <q>  3</q> </rational>
 *       <rational> <p>125</p> <q> 96</q> </rational>
 *       <rational> <p>  5</p> <q> 48</q> </rational>
 *     </row>
 *   </internal-weights>
 * 
 *   <estimation-weights>
 *     <rational> <p>  1</p> <q> 12</q> </rational>
 *     <zero/>
 *     <rational> <p> 27</p> <q> 32</q> </rational>
 *     <rational> <p> -4</p> <q>  3</q> </rational>
 *     <rational> <p>125</p> <q> 96</q> </rational>
 *     <rational> <p>  5</p> <q> 48</q> </rational>
 *     <zero/>
 *   </estimation-weights>
 * 
 *   <error-weights>
 *     <rational> <p> -1</p> <q> 20</q> </rational>
 *     <zero/>
 *     <rational> <p> 81</p> <q>160</q> </rational>
 *     <rational> <p> -6</p> <q>  5</q> </rational>
 *     <rational> <p> 25</p> <q> 32</q> </rational>
 *     <rational> <p>  1</p> <q> 16</q> </rational>
 *     <rational> <p> -1</p> <q> 10</q> </rational>
 *   </error-weights>
 * 
 * </Runge-Kutta>
 *

*

This file describes the various coefficients tables of the * process. In addition to the elements displayed in this example, * coefficients can also be entered as real numbers (like * <real>-0.5</real> for example) with an arbitrary * precision. Real numbers are converted to rational numbers during * parsing, using continued fractions. For simple numbers, this is * fine and exact arithmetic can still be used afterwards, however, * this is not true if too few digits are given. This feature should * be used with care and avoided if possible. The * (<error-weights>...</error-weights>) table is optional, * it is used only for embedded methods with error control.

*

Given this description, the application checks the homogeneity * conditions and the order conditions, increasing the order until it * finds contitions that are not met anymore by the coefficients * arrays. It then displays the order of the method. If there is an * <error-weights>...</error-weights> entry, the order of * the error estimation is also displayed.

*

Checking is done using exact arithmetic by default. However, if * a method involves real numbers for which not enough digits are * given or which are not really rational numbers, it is likely that * all tests will fail and the method will be declared to be of order * 0 ! In this case, it is advised to use a tolerance for the final * tests. Be aware however that for high orders, the order conditions * typically involve small numbers like 1/7983360, so the tolerance * should really be small in order to avoid too optimistic results * about the order of a given method. For reliable results, it is far * better to have an exact representation of the coefficients and to * use only exact arithmetic.

* @version $Id: CheckOrderConditions.java,v 1.6 2004/05/23 13:29:04 luc Exp $ * @author L. Maisonobe */ public class CheckOrderConditions { /** Check the homogeneity conditions. * These conditions bind the time steps to the internal weights * @param c times steps table * @param a internal weights table * @param tolerance if tolerance is negative, computation is done * analytically and an exact match is expected, if tolerance is * positive, the final test is done using floating point arithmetic * and checked against the specified tolerance * @return an error string if condition are not fulfilled and an * empty string if they are fulfilled */ private static String checkHomogeneityConditions(QuadraticSurd[] c, QuadraticSurd[][] a, double tolerance) { StringBuffer sb = new StringBuffer(); if (! c[0].isZero()) { if (Math.abs(c[0].doubleValue()) > tolerance) { sb.append("c[1] = "); if (tolerance <= 0) { sb.append(c[0].toString()); } else { sb.append(c[0].doubleValue()); } sb.append(" (should be null)"); } } for (int i = 1; i < c.length; ++i) { QuadraticSurd sum = new QuadraticSurd(0); for (int j = 0; j < i; ++j) { sum.addToSelf(a[i][j]); } QuadraticSurd delta = QuadraticSurd.subtract(c[i], sum); if (! delta.isZero()) { if (Math.abs(delta.doubleValue()) > tolerance) { if (sb.length() != 0) { sb.append(System.getProperty("line.separator")); } sb.append("c["); sb.append(i + 1); sb.append("] - sum(a["); sb.append(i + 1); sb.append("][j]) = "); if (tolerance <= 0) { sb.append(delta.toString()); } else { sb.append(delta.doubleValue()); } sb.append(" (should be null)"); } } } return sb.toString(); } /** Check the order conditions * @param generator generator for the derivation trees * @param c time steps table * @param a internal weights table * @param b estimation weights table * @param tolerance if tolerance is negative, computation is done * analytically and an exact match is expected, if tolerance is * positive, the final test is done using floating point arithmetic * and checked against the specified tolerance * @param verbose indicator for adding the description of conditions * not met in the returned string * @return a description string for the order conditions met */ private static String checkOrderConditions(DerivationTreesSetGenerator generator, QuadraticSurd[] c, QuadraticSurd[][] a, QuadraticSurd[] b, double tolerance, boolean verbose) { int order = 1; StringBuffer sb = new StringBuffer(); while (sb.length() == 0) { TreeSet set = generator.getTreesSet(order); for (Iterator iter = set.iterator(); iter.hasNext();) { DerivationTree dt = (DerivationTree) iter.next(); QuadraticSurd residual = dt.orderConditionResidual(c, a, b); if (! residual.isZero()) { if (Math.abs(residual.doubleValue()) > tolerance) { if (sb.length() == 0) { sb.append(" is of order " + (order - 1)); } if (verbose) { sb.append(System.getProperty("line.separator")); sb.append("residual for tree " + dt.toString() + " = "); if (tolerance <= 0) { sb.append(residual.toString()); } else { sb.append(residual.doubleValue()); } } } } } ++order; } return sb.toString(); } /** Entry point of the application. * @param args application arguments. This application supports the * following arguments : * */ public static void main(String[] args) { try { String uri = null; boolean verbose = false; double tolerance = -1.0; for (int i = 0; i < args.length; ++i) { if (args[i].equals("-help")) { usage(0); } else if (args[i].equals("-verbose")) { verbose = true; } else if (args[i].equals("-tolerance")) { if (++i == args.length) { System.err.println("missing argument for -tolerance switch"); usage(1); } tolerance = Double.parseDouble(args[i]); } else { uri = args[i]; } } if (uri == null) { usage(1); } RungeKuttaMethod method = new RungeKuttaMethod(); new RungeKuttaFile(method).read(uri); checkHomogeneityConditions(method.getTimeSteps(), method.getInternalWeights(), tolerance); DerivationTreesSetGenerator generator = new DerivationTreesSetGenerator(); System.out.print(method.getName() + " method"); System.out.println(checkOrderConditions(generator, method.getTimeSteps(), method.getInternalWeights(), method.getEstimationWeights(), tolerance, verbose)); QuadraticSurd[] e = method.getErrorWeights(); if (e != null) { QuadraticSurd[] b = method.getEstimationWeights(); QuadraticSurd[] bHat = new QuadraticSurd[b.length]; for (int i = 0; i < bHat.length; ++i) { bHat[i] = QuadraticSurd.add(b[i], e[i]); } System.out.print("error estimation"); System.out.println(checkOrderConditions(generator, method.getTimeSteps(), method.getInternalWeights(), bHat, tolerance, verbose)); } } catch (NumberFormatException nfe) { System.err.println("error parsing number: " + nfe.getMessage()); System.exit(1); } catch (IOException ioe) { System.err.println(ioe.getMessage()); System.exit(1); } catch (FactoryConfigurationError fce) { System.err.println(fce.getMessage()); System.exit(1); } catch (RKCheckException rke) { System.err.println(rke.getMessage()); System.exit(1); } } /** Display usage and exit. * @param exit exit code */ private static void usage(int exit) { System.out.println("usage: java CheckOrderConditions [-help] [-verbose] [-tolerance t] uri"); System.exit(exit); } }

Ads help maintain this website.