add solve_ivp ODE function

This commit is contained in:
wyq 2025-07-30 16:11:13 +08:00
parent a3aefa95d3
commit 7594c6c1f6
42 changed files with 3456 additions and 96 deletions

1
.idea/compiler.xml generated
View File

@ -24,6 +24,7 @@
<module name="meteoinfo-ui" />
<module name="meteoinfo-math" />
<module name="meteoinfo-common" />
<module name="meteoinfo-jython" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel target="11" />

2
.idea/encodings.xml generated
View File

@ -17,6 +17,8 @@
<file url="file://$PROJECT_DIR$/meteoinfo-geometry/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/meteoinfo-image/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/meteoinfo-image/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/meteoinfo-jython/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/meteoinfo-jython/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/meteoinfo-lab/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/meteoinfo-lab/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/meteoinfo-map/src/main/java" charset="UTF-8" />

View File

@ -1544,7 +1544,11 @@ public class Plot2D extends AbstractPlot2D {
ShapeTypes stype = ShapeTypes.POLYLINE;
ls = new LegendScheme(stype);
for (Graphic g : this.graphics.getGraphics()) {
ls.getLegendBreaks().add(g.getLegend());
if (g instanceof GraphicCollection) {
ls.addLegendBreak(((GraphicCollection) g).getLegends());
} else {
ls.addLegendBreak(g.getLegend());
}
/*if (g.getShapeType() == ShapeTypes.POLYLINE) {
ls.getLegendBreaks().add(g.getLegend());
}*/

View File

@ -69,7 +69,7 @@ import java.util.zip.ZipInputStream;
public static String getVersion() {
String version = GlobalUtil.class.getPackage().getImplementationVersion();
if (version == null || version.equals("")) {
version = "4.0.4";
version = "4.0.5";
}
return version;
}

View File

@ -32,11 +32,6 @@
<artifactId>autocomplete</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.meteothink</groupId>
<artifactId>meteoinfo-ndarray</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -1,63 +0,0 @@
package org.meteoinfo.console.jython;
import org.checkerframework.checker.units.qual.C;
import org.meteoinfo.ndarray.Array;
import org.meteoinfo.ndarray.Complex;
import org.meteoinfo.ndarray.DataType;
import org.python.core.PyComplex;
import java.util.List;
public class JythonUtil {
/**
* Convert PyComplex list to ArrayComplex
* @param data PyComplex list
* @return ArrayComplex
*/
public static Array toComplexArray(PyComplex data) {
Array a = Array.factory(DataType.COMPLEX, new int[]{1});
Complex d = new Complex(data.real, data.imag);
a.setComplex(0, d);
return a;
}
/**
* Convert PyComplex list to ArrayComplex
* @param data PyComplex list
* @return ArrayComplex
*/
public static Array toComplexArray(List<Object> data) {
if (data.get(0) instanceof List) {
int ndim = data.size();
int len = ((List) data.get(0)).size();
Array a = Array.factory(DataType.COMPLEX, new int[]{ndim, len});
PyComplex pd;
for (int i = 0; i < ndim; i++) {
List<Object> d = (List) data.get(i);
for (int j = 0; j < len; j++) {
if (d.get(j) instanceof PyComplex) {
pd = (PyComplex) d.get(j);
a.setComplex(i * len + j, new Complex(pd.real, pd.imag));
} else {
a.setComplex(i * len + j, new Complex((double) d.get(j), 0));
}
}
}
return a;
} else {
Array a = Array.factory(DataType.COMPLEX, new int[]{data.size()});
PyComplex pd;
for (int i = 0; i < data.size(); i++) {
if (data.get(i) instanceof PyComplex) {
pd = (PyComplex) data.get(i);
a.setComplex(i, new Complex(pd.real, pd.imag));
} else {
a.setComplex(i, new Complex((double) data.get(i), 0));
}
}
return a;
}
}
}

View File

@ -483,6 +483,19 @@ public class GraphicCollection extends Graphic implements Iterator {
}
}
/**
* Get legend list
* @return Legend list
*/
public List<ColorBreak> getLegends() {
List<ColorBreak> breaks = new ArrayList<>();
for (Graphic graphic : this.graphics) {
breaks.add(graphic.getLegend());
}
return breaks;
}
/**
* Select graphics by an extent
*

View File

@ -1,32 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="milconfig.xml" Type="configurefile">
<Path OpenPath="D:\Working\MIScript\Jython\mis\io\geotiff">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite\calipso"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\burf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite\ascat"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\image"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math\ndimage"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\micaps"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\zarr"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\plot"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<Path OpenPath="D:\Working\MIScript\Jython\mis\common_math\integrate">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\funny"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo\calc"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\geotiff"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math\ndimage"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array\complex"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\micaps"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart\legend"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math\integrate"/>
</Path>
<File>
<OpenedFiles>
<OpenedFile File="D:\Working\MIScript\Jython\mis\satellite\ascat\ascat_1.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\funny\bird_1.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\geotiff\geotif_KX10.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\common_math\integrate\odeint_lorenz.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\common_math\integrate\solve_ivp_celestial_motion.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\common_math\integrate\solve_ivp_Lotka-Volterra.py"/>
</OpenedFiles>
<RecentFiles>
<RecentFile File="D:\Working\MIScript\Jython\mis\satellite\ascat\ascat_1.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\funny\bird_1.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\io\geotiff\geotif_KX10.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\common_math\integrate\odeint_lorenz.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\common_math\integrate\solve_ivp_celestial_motion.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\common_math\integrate\solve_ivp_Lotka-Volterra.py"/>
</RecentFiles>
</File>
<Font>
@ -34,5 +34,5 @@
</Font>
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
<Figure DoubleBuffering="true"/>
<Startup MainFormLocation="-6,0" MainFormSize="1339,808"/>
<Startup MainFormLocation="-6,-6" MainFormSize="1292,764"/>
</MeteoInfo>

View File

@ -28,6 +28,11 @@
<artifactId>meteoinfo-console</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>meteoinfo-jython</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>de.sciss</groupId>
<artifactId>docking-frames-common</artifactId>

View File

@ -72,11 +72,11 @@ def jdate(t):
def jdatetime(t):
"""
Convert python date to joda DateTime.
Convert python date to java DateTime.
:param t: Python date
:returns: Joda DateTime
:returns: Java DateTime
"""
if isinstance(t, (list, tuple)):
r = []

View File

@ -1,3 +1,5 @@
from ._ode import *
from ._ivp import *
__all__ = _ode.__all__
__all__ += _ivp.__all__

View File

@ -0,0 +1,4 @@
from .ivp import *
__all__ = []
__all__ += ivp._all_

View File

@ -0,0 +1,166 @@
from java.lang import Double
from org.meteoinfo.math.integrate import ODESolver
from warnings import warn
from mipylib.numeric import core as np
ESP = Double.MIN_VALUE
def validate_first_step(first_step, t0, t_bound):
"""Assert that first_step is valid and return it."""
if first_step <= 0:
raise ValueError("`first_step` must be positive.")
if first_step > np.abs(t_bound - t0):
raise ValueError("`first_step` exceeds bounds.")
return first_step
def validate_max_step(max_step):
"""Assert that max_Step is valid and return it."""
if max_step <= 0:
raise ValueError("`max_step` must be positive.")
return max_step
def warn_extraneous(extraneous):
"""Display a warning for extraneous keyword arguments.
The initializer of each solver class is expected to collect keyword
arguments that it doesn't understand and warn about them. This function
prints a warning for each key in the supplied dictionary.
Parameters
----------
extraneous : dict
Extraneous keyword arguments
"""
if extraneous:
warn("The following arguments have no effect for a chosen solver: " \
"{', '.join(f'`{x}`' for x in extraneous)}.",
stacklevel=3)
def validate_tol(rtol, atol, n):
"""Validate tolerance values."""
if np.any(rtol < 100 * EPS):
warn("At least one element of `rtol` is too small. " \
"Setting `rtol = np.maximum(rtol, {})`.".format(100 * EPS),
stacklevel=3)
rtol = np.maximum(rtol, 100 * EPS)
atol = np.asarray(atol)
if atol.ndim > 0 and atol.shape != (n,):
raise ValueError("`atol` has wrong shape.")
if np.any(atol < 0):
raise ValueError("`atol` must be positive.")
return rtol, atol
def norm(x):
"""Compute RMS norm."""
return np.linalg.norm(x) / x.size ** 0.5
def select_initial_step(fun, t0, y0, t_bound,
max_step, f0, direction, order, rtol, atol):
"""Empirically select a good initial step.
The algorithm is described in [1]_.
Parameters
----------
fun : callable
Right-hand side of the system.
t0 : float
Initial value of the independent variable.
y0 : ndarray, shape (n,)
Initial value of the dependent variable.
t_bound : float
End-point of integration interval; used to ensure that t0+step<=tbound
and that fun is only evaluated in the interval [t0,tbound]
max_step : float
Maximum allowable step size.
f0 : ndarray, shape (n,)
Initial value of the derivative, i.e., ``fun(t0, y0)``.
direction : float
Integration direction.
order : float
Error estimator order. It means that the error controlled by the
algorithm is proportional to ``step_size ** (order + 1)`.
rtol : float
Desired relative tolerance.
atol : float
Desired absolute tolerance.
Returns
-------
h_abs : float
Absolute value of the suggested initial step.
References
----------
.. [1] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential
Equations I: Nonstiff Problems", Sec. II.4.
"""
if y0.size == 0:
return np.inf
interval_length = abs(t_bound - t0)
if interval_length == 0.0:
return 0.0
scale = atol + np.abs(y0) * rtol
d0 = norm(y0 / scale)
d1 = norm(f0 / scale)
if d0 < 1e-5 or d1 < 1e-5:
h0 = 1e-6
else:
h0 = 0.01 * d0 / d1
# Check t0+h0*direction doesn't take us beyond t_bound
h0 = min(h0, interval_length)
y1 = y0 + h0 * direction * f0
f1 = fun(t0 + h0 * direction, y1)
d2 = norm((f1 - f0) / scale) / h0
if d1 <= 1e-15 and d2 <= 1e-15:
h1 = max(1e-6, h0 * 1e-3)
else:
h1 = (0.01 / max(d1, d2)) ** (1 / (order + 1))
return min(100 * h0, h1, interval_length, max_step)
class OdeSolution():
"""Continuous ODE solution.
It is organized as a collection of `DenseOutput` objects which represent
local interpolants. It provides an algorithm to select a right interpolant
for each given point.
"""
def __init__(self, solver):
self.solver = solver
def __call__(self, t):
"""Evaluate the solution.
Parameters
----------
t : float or array_like with shape (n_points,)
Points to evaluate at.
Returns
-------
y : ndarray, shape (n_states,) or (n_states, n_points)
Computed values. Shape depends on whether `t` is a scalar or a
1-D array.
"""
t = np.asarray(t)
ys = self.solver.solve(t._array)
return np.array(ys)

View File

@ -0,0 +1,444 @@
from org.meteoinfo.math.integrate import ODEEquations, ODESolver
from mipylib.numeric import core as np
from ...optimize import OptimizeResult
from .common import OdeSolution
_all_ = ['solve_ivp']
METHODS = ['RK23', 'RK45', 'DOP853', 'Radau', 'BDF', 'LSODA']
class OdeResult(OptimizeResult):
pass
class ODE(ODEEquations):
def __init__(self, f):
"""
Initialize
:param f: Jython function
"""
self.f = f
self._args = list(f.__code__.co_varnames)[2:]
self._args = tuple(self._args)
self.order = len(self._args)
def doComputeDerivatives(self, y, t):
y = np.array(y)
args = tuple(self.getParameters())
r = self.f(t, y, *args)
if isinstance(r, np.NDArray):
r = tuple(r.aslist())
return r
def solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False, args=None, **options):
"""Solve an initial value problem for a system of ODEs.
This function numerically integrates a system of ordinary differential
equations given an initial value::
dy / dt = f(t, y)
y(t0) = y0
Here t is a 1-D independent variable (time), y(t) is an
N-D vector-valued function (state), and an N-D
vector-valued function f(t, y) determines the differential equations.
The goal is to find y(t) approximately satisfying the differential
equations, given an initial value y(t0)=y0.
Some of the solvers support integration in the complex domain, but note
that for stiff ODE solvers, the right-hand side must be
complex-differentiable (satisfy Cauchy-Riemann equations [11]_).
To solve a problem in the complex domain, pass y0 with a complex data type.
Another option always available is to rewrite your problem for real and
imaginary parts separately.
Parameters
----------
fun : callable
Right-hand side of the system: the time derivative of the state ``y``
at time ``t``. The calling signature is ``fun(t, y)``, where ``t`` is a
scalar and ``y`` is a ndarray with ``len(y) = len(y0)``. Additional
arguments need to be passed if ``args`` is used (see documentation of
``args`` argument). ``fun`` must return an array of the same shape as
``y``. See `vectorized` for more information.
t_span : 2-member sequence
Interval of integration (t0, tf). The solver starts with t=t0 and
integrates until it reaches t=tf. Both t0 and tf must be floats
or values interpretable by the float conversion function.
y0 : array_like, shape (n,)
Initial state. For problems in the complex domain, pass `y0` with a
complex data type (even if the initial value is purely real).
method : string or `OdeSolver`, optional
Integration method to use:
* 'RK45' (default): Explicit Runge-Kutta method of order 5(4) [1]_.
The error is controlled assuming accuracy of the fourth-order
method, but steps are taken using the fifth-order accurate
formula (local extrapolation is done). A quartic interpolation
polynomial is used for the dense output [2]_. Can be applied in
the complex domain.
* 'RK23': Explicit Runge-Kutta method of order 3(2) [3]_. The error
is controlled assuming accuracy of the second-order method, but
steps are taken using the third-order accurate formula (local
extrapolation is done). A cubic Hermite polynomial is used for the
dense output. Can be applied in the complex domain.
* 'DOP853': Explicit Runge-Kutta method of order 8 [13]_.
Python implementation of the "DOP853" algorithm originally
written in Fortran [14]_. A 7-th order interpolation polynomial
accurate to 7-th order is used for the dense output.
Can be applied in the complex domain.
* 'Radau': Implicit Runge-Kutta method of the Radau IIA family of
order 5 [4]_. The error is controlled with a third-order accurate
embedded formula. A cubic polynomial which satisfies the
collocation conditions is used for the dense output.
* 'BDF': Implicit multi-step variable-order (1 to 5) method based
on a backward differentiation formula for the derivative
approximation [5]_. The implementation follows the one described
in [6]_. A quasi-constant step scheme is used and accuracy is
enhanced using the NDF modification. Can be applied in the
complex domain.
* 'LSODA': Adams/BDF method with automatic stiffness detection and
switching [7]_, [8]_. This is a wrapper of the Fortran solver
from ODEPACK.
Explicit Runge-Kutta methods ('RK23', 'RK45', 'DOP853') should be used
for non-stiff problems and implicit methods ('Radau', 'BDF') for
stiff problems [9]_. Among Runge-Kutta methods, 'DOP853' is recommended
for solving with high precision (low values of `rtol` and `atol`).
If not sure, first try to run 'RK45'. If it makes unusually many
iterations, diverges, or fails, your problem is likely to be stiff and
you should use 'Radau' or 'BDF'. 'LSODA' can also be a good universal
choice, but it might be somewhat less convenient to work with as it
wraps old Fortran code.
You can also pass an arbitrary class derived from `OdeSolver` which
implements the solver.
t_eval : array_like or None, optional
Times at which to store the computed solution, must be sorted and lie
within `t_span`. If None (default), use points selected by the solver.
dense_output : bool, optional
Whether to compute a continuous solution. Default is False.
args : tuple, optional
Additional arguments to pass to the user-defined functions. If given,
the additional arguments are passed to all user-defined functions.
So if, for example, `fun` has the signature ``fun(t, y, a, b, c)``,
then `jac` (if given) and any event functions must have the same
signature, and `args` must be a tuple of length 3.
first_step : float or None, optional
Initial step size. Default is `None` which means that the algorithm
should choose.
max_step : float, optional
Maximum allowed step size. Default is np.inf, i.e., the step size is not
bounded and determined solely by the solver.
rtol, atol : float or array_like, optional
Relative and absolute tolerances. The solver keeps the local error
estimates less than ``atol + rtol * abs(y)``. Here `rtol` controls a
relative accuracy (number of correct digits), while `atol` controls
absolute accuracy (number of correct decimal places). To achieve the
desired `rtol`, set `atol` to be smaller than the smallest value that
can be expected from ``rtol * abs(y)`` so that `rtol` dominates the
allowable error. If `atol` is larger than ``rtol * abs(y)`` the
number of correct digits is not guaranteed. Conversely, to achieve the
desired `atol` set `rtol` such that ``rtol * abs(y)`` is always smaller
than `atol`. If components of y have different scales, it might be
beneficial to set different `atol` values for different components by
passing array_like with shape (n,) for `atol`. Default values are
1e-3 for `rtol` and 1e-6 for `atol`.
jac : array_like, sparse_matrix, callable or None, optional
Jacobian matrix of the right-hand side of the system with respect
to y, required by the 'Radau', 'BDF' and 'LSODA' method. The
Jacobian matrix has shape (n, n) and its element (i, j) is equal to
``d f_i / d y_j``. There are three ways to define the Jacobian:
* If array_like or sparse_matrix, the Jacobian is assumed to
be constant. Not supported by 'LSODA'.
* If callable, the Jacobian is assumed to depend on both
t and y; it will be called as ``jac(t, y)``, as necessary.
Additional arguments have to be passed if ``args`` is
used (see documentation of ``args`` argument).
For 'Radau' and 'BDF' methods, the return value might be a
sparse matrix.
* If None (default), the Jacobian will be approximated by
finite differences.
It is generally recommended to provide the Jacobian rather than
relying on a finite-difference approximation.
jac_sparsity : array_like, sparse matrix or None, optional
Defines a sparsity structure of the Jacobian matrix for a finite-
difference approximation. Its shape must be (n, n). This argument
is ignored if `jac` is not `None`. If the Jacobian has only few
non-zero elements in *each* row, providing the sparsity structure
will greatly speed up the computations [10]_. A zero entry means that
a corresponding element in the Jacobian is always zero. If None
(default), the Jacobian is assumed to be dense.
Not supported by 'LSODA', see `lband` and `uband` instead.
lband, uband : int or None, optional
Parameters defining the bandwidth of the Jacobian for the 'LSODA'
method, i.e., ``jac[i, j] != 0 only for i - lband <= j <= i + uband``.
Default is None. Setting these requires your jac routine to return the
Jacobian in the packed format: the returned array must have ``n``
columns and ``uband + lband + 1`` rows in which Jacobian diagonals are
written. Specifically ``jac_packed[uband + i - j , j] = jac[i, j]``.
The same format is used in `scipy.linalg.solve_banded` (check for an
illustration). These parameters can be also used with ``jac=None`` to
reduce the number of Jacobian elements estimated by finite differences.
min_step : float, optional
The minimum allowed step size for 'LSODA' method.
By default `min_step` is zero.
Returns
-------
Bunch object with the following fields defined:
t : ndarray, shape (n_points,)
Time points.
y : ndarray, shape (n, n_points)
Values of the solution at `t`.
sol : `OdeSolution` or None
Found solution as `OdeSolution` instance; None if `dense_output` was
set to False.
t_events : list of ndarray or None
Contains for each event type a list of arrays at which an event of
that type event was detected. None if `events` was None.
y_events : list of ndarray or None
For each value of `t_events`, the corresponding value of the solution.
None if `events` was None.
nfev : int
Number of evaluations of the right-hand side.
njev : int
Number of evaluations of the Jacobian.
nlu : int
Number of LU decompositions.
status : int
Reason for algorithm termination:
* -1: Integration step failed.
* 0: The solver successfully reached the end of `tspan`.
* 1: A termination event occurred.
message : string
Human-readable description of the termination reason.
success : bool
True if the solver reached the interval end or a termination event
occurred (``status >= 0``).
References
----------
.. [1] J. R. Dormand, P. J. Prince, "A family of embedded Runge-Kutta
formulae", Journal of Computational and Applied Mathematics, Vol. 6,
No. 1, pp. 19-26, 1980.
.. [2] L. W. Shampine, "Some Practical Runge-Kutta Formulas", Mathematics
of Computation,, Vol. 46, No. 173, pp. 135-150, 1986.
.. [3] P. Bogacki, L.F. Shampine, "A 3(2) Pair of Runge-Kutta Formulas",
Appl. Math. Lett. Vol. 2, No. 4. pp. 321-325, 1989.
.. [4] E. Hairer, G. Wanner, "Solving Ordinary Differential Equations II:
Stiff and Differential-Algebraic Problems", Sec. IV.8.
.. [5] `Backward Differentiation Formula
<https://en.wikipedia.org/wiki/Backward_differentiation_formula>`_
on Wikipedia.
.. [6] L. F. Shampine, M. W. Reichelt, "THE MATLAB ODE SUITE", SIAM J. SCI.
COMPUTE., Vol. 18, No. 1, pp. 1-22, January 1997.
.. [7] A. C. Hindmarsh, "ODEPACK, A Systematized Collection of ODE
Solvers," IMACS Transactions on Scientific Computation, Vol 1.,
pp. 55-64, 1983.
.. [8] L. Petzold, "Automatic selection of methods for solving stiff and
nonstiff systems of ordinary differential equations", SIAM Journal
on Scientific and Statistical Computing, Vol. 4, No. 1, pp. 136-148,
1983.
.. [9] `Stiff equation <https://en.wikipedia.org/wiki/Stiff_equation>`_ on
Wikipedia.
.. [10] A. Curtis, M. J. D. Powell, and J. Reid, "On the estimation of
sparse Jacobian matrices", Journal of the Institute of Mathematics
and its Applications, 13, pp. 117-120, 1974.
.. [11] `Cauchy-Riemann equations
<https://en.wikipedia.org/wiki/Cauchy-Riemann_equations>`_ on
Wikipedia.
.. [12] `Lotka-Volterra equations
<https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations>`_
on Wikipedia.
.. [13] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential
Equations I: Nonstiff Problems", Sec. II.
.. [14] `Page with original Fortran code of DOP853
<http://www.unige.ch/~hairer/software.html>`_.
Examples
--------
Basic exponential decay showing automatically chosen time points.
>>> from mipylib.numeric.integrate import solve_ivp
>>> def exponential_decay(t, y): return -0.5 * y
>>> sol = solve_ivp(exponential_decay, [0, 10], [2, 4, 8])
>>> print(sol.t)
[ 0. 0.11487653 1.26364188 3.06061781 4.81611105 6.57445806
8.33328988 10. ]
>>> print(sol.y)
[[2. 1.88836035 1.06327177 0.43319312 0.18017253 0.07483045
0.03107158 0.01350781]
[4. 3.7767207 2.12654355 0.86638624 0.36034507 0.14966091
0.06214316 0.02701561]
[8. 7.5534414 4.25308709 1.73277247 0.72069014 0.29932181
0.12428631 0.05403123]]
Specifying points where the solution is desired.
>>> sol = solve_ivp(exponential_decay, [0, 10], [2, 4, 8],
... t_eval=[0, 1, 2, 4, 10])
>>> print(sol.t)
[ 0 1 2 4 10]
>>> print(sol.y)
[[2. 1.21305369 0.73534021 0.27066736 0.01350938]
[4. 2.42610739 1.47068043 0.54133472 0.02701876]
[8. 4.85221478 2.94136085 1.08266944 0.05403753]]
Cannon fired upward with terminal event upon impact. The ``terminal`` and
``direction`` fields of an event are applied by monkey patching a function.
Here ``y[0]`` is position and ``y[1]`` is velocity. The projectile starts
at position 0 with velocity +10. Note that the integration never reaches
t=100 because the event is terminal.
>>> def upward_cannon(t, y): return [y[1], -0.5]
>>> def hit_ground(t, y): return y[0]
>>> hit_ground.terminal = True
>>> hit_ground.direction = -1
>>> sol = solve_ivp(upward_cannon, [0, 100], [0, 10], events=hit_ground)
>>> print(sol.t_events)
[array([40.])]
>>> print(sol.t)
[0.00000000e+00 9.99900010e-05 1.09989001e-03 1.10988901e-02
1.11088891e-01 1.11098890e+00 1.11099890e+01 4.00000000e+01]
Use `dense_output` and `events` to find position, which is 100, at the apex
of the cannonball's trajectory. Apex is not defined as terminal, so both
apex and hit_ground are found. There is no information at t=20, so the sol
attribute is used to evaluate the solution. The sol attribute is returned
by setting ``dense_output=True``. Alternatively, the `y_events` attribute
can be used to access the solution at the time of the event.
>>> def apex(t, y): return y[1]
>>> sol = solve_ivp(upward_cannon, [0, 100], [0, 10],
... events=(hit_ground, apex), dense_output=True)
>>> print(sol.t_events)
[array([40.]), array([20.])]
>>> print(sol.t)
[0.00000000e+00 9.99900010e-05 1.09989001e-03 1.10988901e-02
1.11088891e-01 1.11098890e+00 1.11099890e+01 4.00000000e+01]
>>> print(sol.sol(sol.t_events[1][0]))
[100. 0.]
>>> print(sol.y_events)
[array([[-5.68434189e-14, -1.00000000e+01]]),
array([[1.00000000e+02, 1.77635684e-15]])]
As an example of a system with additional parameters, we'll implement
the Lotka-Volterra equations [12]_.
>>> def lotkavolterra(t, z, a, b, c, d):
... x, y = z
... return [a*x - b*x*y, -c*y + d*x*y]
...
We pass in the parameter values a=1.5, b=1, c=3 and d=1 with the `args`
argument.
>>> sol = solve_ivp(lotkavolterra, [0, 15], [10, 5], args=(1.5, 1, 3, 1),
... dense_output=True)
Compute a dense solution and plot it.
>>> t = np.linspace(0, 15, 300)
>>> z = sol.sol(t)
>>> import matplotlib.pyplot as plt
>>> plt.plot(t, z.T)
>>> plt.xlabel('t')
>>> plt.legend(['x', 'y'], shadow=True)
>>> plt.title('Lotka-Volterra System')
>>> plt.show()
A couple examples of using solve_ivp to solve the differential
equation ``y' = Ay`` with complex matrix ``A``.
>>> A = np.array([[-0.25 + 0.14j, 0, 0.33 + 0.44j],
... [0.25 + 0.58j, -0.2 + 0.14j, 0],
... [0, 0.2 + 0.4j, -0.1 + 0.97j]])
Solving an IVP with ``A`` from above and ``y`` as 3x1 vector:
>>> def deriv_vec(t, y):
... return A @ y
>>> result = solve_ivp(deriv_vec, [0, 25],
... np.array([10 + 0j, 20 + 0j, 30 + 0j]),
... t_eval=np.linspace(0, 25, 101))
>>> print(result.y[:, 0])
[10.+0.j 20.+0.j 30.+0.j]
>>> print(result.y[:, -1])
[18.46291039+45.25653651j 10.01569306+36.23293216j
-4.98662741+80.07360388j]
Solving an IVP with ``A`` from above with ``y`` as 3x3 matrix :
>>> def deriv_mat(t, y):
... return (A @ y.reshape(3, 3)).flatten()
>>> y0 = np.array([[2 + 0j, 3 + 0j, 4 + 0j],
... [5 + 0j, 6 + 0j, 7 + 0j],
... [9 + 0j, 34 + 0j, 78 + 0j]])
>>> result = solve_ivp(deriv_mat, [0, 25], y0.flatten(),
... t_eval=np.linspace(0, 25, 101))
>>> print(result.y[:, 0].reshape(3, 3))
[[ 2.+0.j 3.+0.j 4.+0.j]
[ 5.+0.j 6.+0.j 7.+0.j]
[ 9.+0.j 34.+0.j 78.+0.j]]
>>> print(result.y[:, -1].reshape(3, 3))
[[ 5.67451179 +12.07938445j 17.2888073 +31.03278837j
37.83405768 +63.25138759j]
[ 3.39949503 +11.82123994j 21.32530996 +44.88668871j
53.17531184+103.80400411j]
[ -2.26105874 +22.19277664j -15.1255713 +70.19616341j
-38.34616845+153.29039931j]]
"""
if method not in METHODS:
raise ValueError("`method` must be one of {METHODS}.")
t0, tf = map(float, t_span)
first_step = options.pop('first_step', None)
max_step = options.pop('max_step', None)
rtol = options.pop('rtol', None)
atol = options.pop('atol', None)
fun = ODE(fun)
if args is not None:
fun.setParameters(args)
if isinstance(y0, (tuple, list)):
y0 = np.array(y0)
y0 = y0.astype('double')
ndim = len(y0)
fun.setDimension(ndim)
if t_eval is not None: t_eval = np.array(t_eval)._array
solver = ODESolver(method, fun, t0, tf, y0._array)
solver.setDenseOutput(dense_output)
if t_eval is not None: solver.setTEval(t_eval)
if first_step is not None: solver.setMinStep(first_step)
if max_step is not None: solver.setMaxStep(max_step)
if rtol is not None: solver.setRTol(rtol)
if atol is not None: solver.setATol(atol)
solver.solve()
ts = solver.getTResult()
ys = solver.getYResult()
if ts is not None: ts = np.array(ts)
if ys is not None: ys = np.array(ys)
sol = OdeSolution(solver) if dense_output else None
return OdeResult(t=ts, y=ys, sol=sol)

View File

@ -149,4 +149,48 @@ def check_random_state(seed):
return seed
raise ValueError('%r cannot be used to seed a numpy.random.RandomState'
' instance' % seed)
' instance' % seed)
class _RichResult(dict):
""" Container for multiple outputs with pretty-printing """
def __getattr__(self, name):
try:
return self[name]
except KeyError as e:
raise AttributeError(name)
__setattr__ = dict.__setitem__ # type: ignore[assignment]
__delattr__ = dict.__delitem__ # type: ignore[assignment]
def __repr__(self):
order_keys = ['message', 'success', 'status', 'fun', 'funl', 'x', 'xl',
'col_ind', 'nit', 'lower', 'upper', 'eqlin', 'ineqlin',
'converged', 'flag', 'function_calls', 'iterations',
'root']
order_keys = getattr(self, '_order_keys', order_keys)
# 'slack', 'con' are redundant with residuals
# 'crossover_nit' is probably not interesting to most users
omit_keys = {'slack', 'con', 'crossover_nit', '_order_keys'}
def key(item):
try:
return order_keys.index(item[0].lower())
except ValueError: # item not in list
return np.inf
def omit_redundant(items):
for item in items:
if item[0] in omit_keys:
continue
yield item
def item_sorter(d):
return sorted(omit_redundant(d.items()), key=key)
if self.keys():
return _dict_formatter(self, sorter=item_sorter)
else:
return self.__class__.__name__ + "()"
def __dir__(self):
return list(self.keys())

View File

@ -1,4 +1,5 @@
from .minpack import *
from ._lsq import *
from ._optimize import *
__all__ = [s for s in dir() if not s.startswith('_')]

View File

@ -0,0 +1,50 @@
from ..lib._util import _RichResult
__all__ = ['OptimizeResult']
class OptimizeResult(_RichResult):
"""
Represents the optimization result.
Attributes
----------
x : ndarray
The solution of the optimization.
success : bool
Whether or not the optimizer exited successfully.
status : int
Termination status of the optimizer. Its value depends on the
underlying solver. Refer to `message` for details.
message : str
Description of the cause of the termination.
fun : float
Value of objective function at `x`.
jac, hess : ndarray
Values of objective function's Jacobian and its Hessian at `x` (if
available). The Hessian may be an approximation, see the documentation
of the function in question.
hess_inv : object
Inverse of the objective function's Hessian; may be an approximation.
Not available for all solvers. The type of this attribute may be
either np.ndarray or scipy.sparse.linalg.LinearOperator.
nfev, njev, nhev : int
Number of evaluations of the objective functions and of its
Jacobian and Hessian.
nit : int
Number of iterations performed by the optimizer.
maxcv : float
The maximum constraint violation.
Notes
-----
Depending on the specific solver being used, `OptimizeResult` may
not have all attributes listed here, and they may have additional
attributes not listed here. Since this class is essentially a
subclass of dict with attribute accessors, one can see which
attributes are available using the `OptimizeResult.keys` method.
"""
pass

View File

@ -3836,6 +3836,7 @@ class Axes(object):
clegend = ChartLegend(ols)
else:
clegend = self._axes.getLegend()
ls = kwargs.pop('legend', None)
if len(args) > 0:
if isinstance(args[0], MILayer):
@ -3848,6 +3849,7 @@ class Axes(object):
if not args[0].isSingleLegend():
ls = args[0].getLegendScheme()
args = args[1:]
if ls is None:
if len(args) > 0:
lbs = []
@ -3858,10 +3860,12 @@ class Axes(object):
lbs.extend(lb.legend().getLegendBreaks())
else:
lbs.append(lb)
if len(args) == 2:
labels = args[1]
for i in range(0, len(lbs)):
lbs[i].setCaption(labels[i])
if isinstance(lbs[0], basestring):
clegend.setTickCaptions(lbs)
else:

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="config.xml" Type="configurefile">
<Path OpenPath="D:\Temp\nc\2021_daily_4_VOD"/>
<Path OpenPath="D:\Temp\image"/>
<Font>
<TextFont FontName="YaHei Consolas Hybrid" FontSize="14"/>
<LegendFont FontName="宋体" FontSize="12"/>

View File

@ -0,0 +1,7 @@
package org.meteoinfo.math.integrate;
public enum IntegrateMethod {
RK45,
RK23,
DOP853;
}

View File

@ -3,14 +3,18 @@ package org.meteoinfo.math.integrate;
import org.apache.commons.math4.legacy.ode.ContinuousOutputModel;
import org.apache.commons.math4.legacy.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math4.legacy.ode.FirstOrderIntegrator;
import org.apache.commons.math4.legacy.ode.nonstiff.DormandPrince54Integrator;
import org.apache.commons.math4.legacy.ode.nonstiff.DormandPrince853Integrator;
import org.apache.commons.math4.legacy.ode.sampling.StepHandler;
import org.apache.commons.math4.legacy.ode.sampling.StepInterpolator;
import org.meteoinfo.ndarray.Array;
import org.meteoinfo.ndarray.DataType;
import org.meteoinfo.ndarray.Index2D;
import org.meteoinfo.ndarray.IndexIterator;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class IntegrateUtil {
@ -57,4 +61,83 @@ public class IntegrateUtil {
return r;
}
/**
* Integrate a system of ordinary differential equations
*
* @param equations Computes the derivative of y at t
* @param y0 Initial condition on y
* @param t0 Integrate start time
* @param t Integrate end time
* @param tEval Times at which to store the computed solution
* @param minStep Minimal step
* @param maxStep Maximal step
* @param rtol Allowed relative error
* @param atol Allowed absolute error
* @return Array containing the value of y for each desired time in t, with the initial value y0 in the first row.
*/
public static Array[] ode45(FirstOrderDifferentialEquations equations, Array y0, double t0, double t,
Array tEval, Double minStep, Double maxStep, Double rtol, Double atol) throws IOException, ClassNotFoundException {
y0 = y0.copyIfView();
int ny0 = (int) y0.getSize();
minStep = minStep == null ? 1.0e-6 : minStep;
maxStep = maxStep == null ? 100.0 : maxStep;
rtol = rtol == null ? 1.0e-6 : rtol;
atol = atol == null ? 1.0e-6 : atol;
FirstOrderIntegrator integrator = new DormandPrince54Integrator(minStep, maxStep, atol, rtol);
double[] y0v = (double[]) y0.getStorage();
double[] yDot = new double[ny0];
List<Double> tlist = new ArrayList<>();
List<double[]> ylist = new ArrayList<>();
if (tEval == null) {
integrator.addStepHandler(new StepHandler() {
@Override
public void init(double t0, double[] y0, double t) {
tlist.add(t0);
ylist.add(y0);
//System.out.printf("Start: t0 = %.4f, y0 = %.6f%n", t0, y0[0]);
}
@Override
public void handleStep(StepInterpolator interpolator, boolean isLast) {
double t = interpolator.getCurrentTime();
tlist.add(t);
double[] y = interpolator.getInterpolatedState();
ylist.add(y.clone());
//System.out.printf("t = %.4f, y = %.6f%n", t, y[0]);
}
});
integrator.integrate(equations, t0, y0v, t, yDot);
} else {
ContinuousOutputModel solution = new ContinuousOutputModel();
integrator.addStepHandler(solution);
integrator.integrate(equations, t0, y0v, t, yDot);
for (int i = 0; i < tEval.getSize(); i++) {
tlist.add(tEval.getDouble(i));
solution.setInterpolatedTime(tEval.getDouble(i));
ylist.add(solution.getInterpolatedState());
}
}
int nt = tlist.size();
Array rt = Array.factory(DataType.DOUBLE, new int[]{nt});
Array r = Array.factory(DataType.DOUBLE, new int[]{ny0, nt});
Index2D index = (Index2D) r.getIndex();
for (int i = 0; i < nt; i++) {
rt.setDouble(i, tlist.get(i));
double[] interpolatedY = ylist.get(i);
index.set1(i);
for (int j = 0; j < ny0; j++) {
index.set0(j);
r.setDouble(index, interpolatedY[j]);
}
}
return new Array[]{rt, r};
}
}

View File

@ -0,0 +1,219 @@
package org.meteoinfo.math.integrate;
import org.apache.commons.math4.legacy.ode.ContinuousOutputModel;
import org.apache.commons.math4.legacy.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math4.legacy.ode.FirstOrderIntegrator;
import org.apache.commons.math4.legacy.ode.nonstiff.DormandPrince54Integrator;
import org.apache.commons.math4.legacy.ode.nonstiff.DormandPrince853Integrator;
import org.apache.commons.math4.legacy.ode.sampling.StepHandler;
import org.apache.commons.math4.legacy.ode.sampling.StepInterpolator;
import org.meteoinfo.ndarray.Array;
import org.meteoinfo.ndarray.DataType;
import org.meteoinfo.ndarray.Index2D;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
public class ODESolver {
private IntegrateMethod method;
private FirstOrderIntegrator integrator;
private FirstOrderDifferentialEquations equations;
private Array y0;
private double t0;
private double tf;
private Array tEval;
private double minStep = 1.0e-6;
private double maxStep = 100.0;
private double rTol = 1.0e-6;
private double aTol = 1.0e-6;
private boolean denseOutput = false;
private ContinuousOutputModel denseOutputModel;
private Array tResult;
private Array yResult;
/**
* Constructor
* @param method Integration method
* @param equations Differential equations
* @param t0 Integration start time
* @param tf Integration end time
* @param y0 Initial state
*/
public ODESolver(String method, FirstOrderDifferentialEquations equations, double t0, double tf,
Array y0) {
this.method = IntegrateMethod.valueOf(method.toUpperCase());
this.equations = equations;
this.t0 = t0;
this.tf = tf;
this.y0 = y0.copyIfView();
}
/**
* Set times at which to store the computed solution
* @param value The value
*/
public void setTEval(Array value) {
this.tEval = value;
}
/**
* Set first step size
* @param value First step size
*/
public void setMinStep(double value) {
this.minStep = value;
}
/**
* Set maximum allowed step size
* @param value Maximum allowed step size
*/
public void setMaxStep(double value) {
this.maxStep = value;
}
/**
* Set relative tolerance
* @param value Relative tolerance
*/
public void setRTol(double value) {
this.rTol = value;
}
/**
* Set absolute tolerance
* @param value Absolute tolerance
*/
public void setATol(double value) {
this.aTol = value;
}
/**
* Set dense output or not
* @param value Dense output or not
*/
public void setDenseOutput(boolean value) {
this.denseOutput = value;
}
/**
* Get time result
* @return Time result
*/
public Array getTResult() {
return this.tResult;
}
/**
* Get y result
* @return Y result
*/
public Array getYResult() {
return this.yResult;
}
/**
* Solve function
* @throws NoSuchMethodException
*/
public void solve() throws NoSuchMethodException {
switch (this.method) {
case RK45:
this.integrator = new DormandPrince54Integrator(minStep, maxStep, aTol, rTol);
break;
case DOP853:
this.integrator = new DormandPrince853Integrator(minStep, maxStep, aTol, rTol);
break;
default:
throw new NoSuchMethodException("No such");
}
int ny0 = (int) y0.getSize();
double[] y0v = (double[]) y0.getStorage();
double[] yDot = new double[ny0];
List<Double> tlist = new ArrayList<>();
List<double[]> ylist = new ArrayList<>();
if (this.denseOutput) {
denseOutputModel = new ContinuousOutputModel();
integrator.addStepHandler(denseOutputModel);
integrator.integrate(equations, t0, y0v, tf, yDot);
} else {
if (tEval == null) {
integrator.addStepHandler(new StepHandler() {
@Override
public void init(double t0, double[] y0, double t) {
tlist.add(t0);
ylist.add(y0);
//System.out.printf("Start: t0 = %.4f, y0 = %.6f%n", t0, y0[0]);
}
@Override
public void handleStep(StepInterpolator interpolator, boolean isLast) {
double t = interpolator.getCurrentTime();
tlist.add(t);
double[] y = interpolator.getInterpolatedState();
ylist.add(y.clone());
//System.out.printf("t = %.4f, y = %.6f%n", t, y[0]);
}
});
integrator.integrate(equations, t0, y0v, tf, yDot);
} else {
ContinuousOutputModel denseOutputModel = new ContinuousOutputModel();
integrator.addStepHandler(denseOutputModel);
integrator.integrate(equations, t0, y0v, tf, yDot);
for (int i = 0; i < tEval.getSize(); i++) {
tlist.add(tEval.getDouble(i));
denseOutputModel.setInterpolatedTime(tEval.getDouble(i));
ylist.add(denseOutputModel.getInterpolatedState());
}
}
int nt = tlist.size();
tResult = Array.factory(DataType.DOUBLE, new int[]{nt});
yResult = Array.factory(DataType.DOUBLE, new int[]{ny0, nt});
Index2D index = (Index2D) yResult.getIndex();
for (int i = 0; i < nt; i++) {
tResult.setDouble(i, tlist.get(i));
double[] interpolatedY = ylist.get(i);
index.set1(i);
for (int j = 0; j < ny0; j++) {
index.set0(j);
yResult.setDouble(index, interpolatedY[j]);
}
}
}
}
/**
* Solve by time steps - only valid with dense output
* @param t Time steps
* @return Values of the solution at time steps
*/
public Array solve(Array t) {
t = t.copyIfView();
int ny0 = (int) y0.getSize();
int nt = (int) t.getSize();
Array y = Array.factory(DataType.DOUBLE, new int[]{ny0, nt});
Index2D index = (Index2D) y.getIndex();
for (int i = 0; i < t.getSize(); i++) {
denseOutputModel.setInterpolatedTime(t.getDouble(i));
double[] v = denseOutputModel.getInterpolatedState();
index.set1(i);
for (int j = 0; j < ny0; j++) {
index.set0(j);
y.setDouble(index, v[j]);
}
}
return y;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
package org.meteoinfo.math.integrate.lsoda;
public class SupportingFunctions {
/**
* @param n number of elements
* @param v value of the vector
* @param w weights
* @return vector norm
*/
public static double vmnorm(int n, double[] v, double[] w){
double vm = 0.0;
for (int i=1; i<=n; i++)
vm = Math.max(vm, Math.abs(v[i])*w[i]);
return vm;
}
/**
* full matrix norm
* Computes the norm of a full n by n matrix,
* stored in the array a, that is consistent with the weighted max-norm
* on vectors, with weights stored in the array w.
* fnorm = max(i=1,...,n) ( w[i] * sum(j=1,...,n) fabs( a[i][j] ) / w[j] )
* @param n dimensions
* @param a the matrix
* @param w weights
* @return matrix norm
*/
public static double fnorm(int n, double[][] a, double[] w){
double an = 0.0, sum;
for (int i=1; i <= n ; i++){
sum = 0;
for(int j=1; j<=n; j++)
sum += Math.abs(a[i][j])/w[j];
an = Math.max(an, sum*w[i]);
}
return an;
}
/**
* compute EWT
* @param ycur current value of y
*/
public static double[] ewset(double[] ycur, int itol, double[] rtol, double[] atol, int n){
int i;
double[] EWT = new double[n+1];
switch (itol){
case 1:
for (i=1; i<=n; i++) // both are scalars
EWT[i] = rtol[1] * Math.abs(ycur[i])+atol[1];
break;
case 2:
for (i=1;i<=n; i++) //scalar rtol and array atol
EWT[i] = rtol[1] * Math.abs(ycur[i]) + atol[i];
break;
case 3:
for (i=1;i<=n; i++) //array rtol and scalar atol
EWT[i] = rtol[i] * Math.abs(ycur[i]) + atol[1];
break;
case 4:
for (i=1;i<=n; i++) // both are arrays
EWT[i] = rtol[i] * Math.abs(ycur[i]) + atol[i];
break;
}
return EWT;
}
/**
* Manages the solution of the linear system arising from
* a chord iteration. It is called if miter != 0.
* @param y the right-hand side vector on input, and the solution vector on output.
* @return solution of the linear system
*/
public static double[] solsy(double[] y, double[][] wm, int n, int[] ipvt, int miter){
if (miter != 2){
System.out.println("solsy: miter != 2");
return y;
}
return Utility.solveLinearSys(wm,n,ipvt,y);
}
/**
* interpolate at the output station
* @param t value of independent variable
* @param k integer that specifies the desired derivative order
*/
public static double[] intdy(double t, int k, double[] dky, int nq, double tn,
double hu, double ETA, double h, double[][] yh, int n){
int ic ,jp1;
double r, s, tp;
if (k < 0 || k > nq){
System.err.printf("intdy: k = %d illegal", k);
dky[0] = -1;
return dky;
}
tp = tn - hu -100.0 * ETA * (tn + hu);
if ((t-tp) * (t-tn)> 0.0){
System.err.printf("intdy: t = %f illegal\n" +
"t not in interval tcur-hu to tcur\n", t);
dky[0] = -2;
return dky;
}
s = (t-tn)/h;
ic = 1;
for (int i = nq+1-k; i <= nq; i++){
ic *= i; // ic = nq(nq-1)...(nq-k+1)
}
for (int i=1; i<=n; i++)
dky[i] = ic* yh[nq+1][i];
// use Horner's rule
for (int j = nq-1; j >= k; j--){
jp1 = j+1; // order of current derivatives
ic = 1;
for (int jj = jp1-k; jj<=j;jj++)
ic *= jj; // coefficient
for (int i=1; i<=n; i++)
dky[i] = ic * yh[jp1][i] + s * dky[i];
}
if (k == 0){
return dky;
}
r = Math.pow(h, -k);
for (int i=1; i<=n; i++)
dky[i] = r*dky[i];
return dky;
}
}

View File

@ -0,0 +1,314 @@
package org.meteoinfo.math.integrate.lsoda;
import java.util.Arrays;
public class Utility {
/**
* ddot.c
* @param n number of elements
* @param dx n+1 vector (dx[0] is not used)
* @param incx storage spacing between elements of dx
* @param dy n+1 vector (dy[0] is not used)
* @param incy storage spacing between elements of dy
* @return dot product of dx and dy
*/
public static double Dot(int n, double[] dx, int incx, double[] dy, int incy){
double dotProd = 0; // result
int ix = 1, iy = 1; // index of dx and dy
if (n <= 0)
return dotProd;
// unequal or non-positive increments
if( incx != incy || incx < 1){
if (incx < 0)
ix = (-n+1) * incx + 1;
if (incx < 0)
iy = (-n+1) * incy + 1;
for(int i=1;i<=n;i++){
dotProd += dx[ix] * dy[iy];
ix += incx;
iy += incy;
}
return dotProd;
}
// Both increments equal to 1
if(incx == 1){
int m = n % 5;
// Clean-up loop so remaining vector length is a multiple of 5
if (m != 0){
for (int i=1; i<=m;i++)
dotProd += dx[i] * dy[i];
if (n < 5)
return dotProd;
}
for (int i=m+1; i<=n;i=i+5)
dotProd += 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];
return dotProd;
}
// positive equal non-unit increments
for (int i=1; i<= n*incx; i=i+incx)
dotProd += dx[i] * dy[i];
return dotProd;
}
/**
* idamax.c
* Find largest component of double vector dx
* @param n number of elements
* @param dx n+1 vector (dx[0] is not used)
* @param incx storage spacing between elements of dx
* @return smallest index
*/
public static int findMaxMagnitude(int n, double[] dx, int incx){
double maxValue, curValue;
int xindex = 0;
if (n <= 0)
return xindex;
xindex = 1;
if (n <= 1 || incx <= 0)
return xindex;
maxValue = Math.abs(dx[1]);
// increments are not euqal to 1
if (incx != 1){
int curIndex = 2;
for (int i=1+incx; i <= n*incx; i=i+incx){
curValue = Math.abs(dx[i]);
if (curValue > maxValue){
xindex = curIndex;
maxValue = curValue;
}
curIndex++;
}
return xindex;
}
// increments are equal to 1.
for (int i=2; i <= n; i++){
curValue = Math.abs(dx[i]);
if (curValue > maxValue){
xindex = i;
maxValue = curValue;
}
}
return xindex;
}
/**
* dscal.c
* scalar vector multiplication
* @param n number of elements
* @param da double scalar multiplier
* @param dx start+n-1 vector (dx[0] is not used)
* @param incx storage spacing between elements of dx (should be positive)
* @param start start position
* @return da*dx
*/
public static double[] calAX(int n, double da, double[] dx, int incx, int start){
if (n <= 0 || incx * (n-1) + start >= dx.length){
System.out.println("function calAX: illegal input");
return dx;
}
// increments are not equal to 1
if (incx != 1){
for (int i = start; i <= (n-1)*incx + start; i = i+incx)
dx[i] = da * dx[i];
return dx;
}
// increments are equal to 1
int m = n % 5;
if ( m != 0 ) {
for (int i = start ; i <= m+start - 1 ; i++ )
dx[i] = da * dx[i];
if ( n < 5 )
return dx;
}
for (int i = m + start; i <= n + start - 1; i = i + 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];
}
return dx;
}
/**
* daxpy.c
* @param n number of elements
* @param da double scalar multiplier
* @param dx start+n-1 vector (dx[0] is not used)
* @param incx storage spacing between elements of dx
* @param dy start+n-1 vector (dy[0] is not used)
* @param incy storage spacing between elements of dy
* @param ystart start position of y
* @param xstart start position of x
* @return da * dx + dy
*/
public static double[] calAXPlusY(int n, double da, double[] dx, int incx, double[] dy, int incy, int xstart, int ystart){
int ix, iy; // index
if (incx * (n-1) + xstart >= dx.length||incy * (n-1) + ystart >= dy.length||n<=0){
System.out.println("function calAXPlusY: illegal input");
return dy;
}
if ( da == 0.0)
return dy;
// nonequal or nonpositive increments
if (incx != incy || incx < 1){
ix = xstart;
iy = ystart;
if (incx < 0)
ix = (-n+1) * incx + xstart;
if (incx < 0)
iy = (-n+1) * incy + ystart;
for (int i=1; i<=n; i++){
dy[iy] += da * dx[ix];
ix += incx;
iy += incy;
}
return dy;
}
// increments are equal to 1
if (incx == 1){
// Clean-up loop
int m = n % 4;
if (m != 0){
for (int i=1; i<=m; i++)
dy[i+ystart-1] += da * dx[i+xstart-1];
if (n < 4)
return dy;
}
for (int i=m+1; i <= n; i=i+4){
dy[i+ystart-1] = dy[i+ystart-1] + da * dx[i+xstart-1];
dy[i+ystart] = dy[i+ystart] + da * dx[i+xstart];
dy[i+ystart+1] = dy[i+ystart+1] + da * dx[i+xstart+1];
dy[i+ystart+2] = dy[i+ystart+2] + da * dx[i+xstart+2];
}
return dy;
}
// incrememts are equal, positive but not unit
for (int i = 1; i <= n*incx; i=i+incx)
dy[ystart+i-1] += da*dx[xstart+i-1];
return dy;
}
/**
* LU decomposition using partial pivoting
* @param a (n+1)x(n+1) matrix
* @param n dimension
* @return a, ipvt, info
* if info != 0, the matrix is singular
*/
public static ReturningValues LUDecomposition(double[][] a, int n){
double t;
int info = 0;
int[] ipvt = new int[n+1];
for (int k=1 ; k<=n-1 ; k++){
// Find j = pivot index
int j = findMaxMagnitude(n-k+1, Arrays.copyOfRange(a[k],k-1,n+1),1)+k-1;
ipvt[k] = j;
// zero pivot implies this row already triangularised
if (a[k][j] == 0.0){
info = k;
continue;
}
// interchange
if(j != k){
t = a[k][j];
a[k][j] = a[k][k];
a[k][k] = t;
}
// compute multipliers
t = - 1.0/ a[k][k];
a[k] = calAX(n-k, t, a[k],1, k+1);
// column elimination with row indexing
for (int i=k+1; i<=n;i++){
t = a[i][j];
if(j != k){
a[i][j] = a[i][k];
a[i][k] = t;
}
a[i] = calAXPlusY(n-k, t, a[k], 1, a[i], 1,k+1,k+1);
}
}
ipvt[n] = n;
if(a[n][n] == 0.0)
info = n;
return new ReturningValues(a, ipvt, info);
}
/**
* solves the linear system using the factors computed by LUDecomposition
* @param a (n+1)x(n+1) matrix
* @param n row dimension of a
* @param ipvt the pivot vector from LUDecomposition
* @param b the right hand side vector
* @return b the solution vector x.
*/
public static double[] solveLinearSys(double[][] a, int n, int[] ipvt, double[] b){
int j;
double t;
// Ax = LUx = b; Let Ux = y, so Ly = b.
// 1. Solve L * y = b.
for(int k=1; k <= n; k++){
t = Dot(k-1, Arrays.copyOfRange(a[k],0,k),1,b,1);
b[k] = (b[k]-t) / a[k][k];
}
// 2. Solve U * x = y.
for (int k=n-1; k >= 1; k--){
b[k] = b[k] + Dot(n-k, Arrays.copyOfRange(a[k],k,n+1),1, Arrays.copyOfRange(b,k,n+1),1);
j = ipvt[k];
if (j != k){
t = b[j];
b[j] = b[k];
b[k] = t;
}
}
return b;
}
public static Double[] transformYVec(double[] y){
int len = y.length-1;
Double[] yvec = new Double[len];
for (int i=0; i<len;i++)
yvec[i] = y[i+1];
return yvec;
}
}
class ReturningValues{
public final double[][] a;
public final int info;
public final int[] ipvt;
public ReturningValues(double[][] a, int[] ipvt, int info){
this.info = info;
this.a = a;
this.ipvt = ipvt;
}
}

View File

@ -0,0 +1,15 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class EwtException extends RuntimeException{
private String errorMsg;
public EwtException(Number i, Number value){
super();
errorMsg ="lsoda: ewt["+i+"] = "+value+" <= 0.0";
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,15 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class ExceedMaxStepsException extends RuntimeException{
private String errorMsg;
public ExceedMaxStepsException(int mxstep){
super();
errorMsg = "lsoda: "+mxstep+" steps taken before reaching tout";
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,14 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class IllegalInputException extends RuntimeException{
private String errorMsg;
public IllegalInputException(String param, Number value){
super();
errorMsg = "lsoda: illegal "+param+"="+value;
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,30 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class IllegalTException extends RuntimeException{
private String errorMsg;
public IllegalTException(String type, double t, double tout, Number tt){
switch (type){
case "tout_behind_t":
errorMsg = "lsoda: tout ="+tout+" behind t = "+t +
"\n integration direction is given by "+tt;
break;
case "tcrit_behind_tout":
errorMsg = "lsoda: itask = 4 or 5 and tcrit behind tout";
break;
case "tcrit_behind_tcur":
errorMsg = "lsoda: itask = 4 or 5 and tcrit behind tcur";
break;
case "tout_close_to_t":
errorMsg = "lsoda: tout too close to t to start integration";
break;
case "tout_behind_tcur_hu":
errorMsg = "lsoda: itask = "+tt+" and tout behind tcur - hu";
break;
}
}
@Override
public String getMessage(){
return errorMsg;
}
}

View File

@ -0,0 +1,16 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class InterpolationException extends RuntimeException{
private String errorMsg;
public InterpolationException(int itask, double tout){
super();
errorMsg = "lsoda: trouble from intdy, itask = "+itask+
", tout = "+tout;
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,21 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class IstateException extends RuntimeException{
private String errorMsg;
public IstateException(int istate){
super();
errorMsg = "lsoda: illegal istate ="+istate;
}
public IstateException(int istate, int init){
super();
if (istate>1 && init==0)
errorMsg = "lsoda: istate > 1 but lsoda not initialised";
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,20 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class NeqException extends RuntimeException{
private String errorMsg;
public NeqException(int neq){
super();
errorMsg = "lsoda: neq = "+neq+" is less than 1";
}
public NeqException(int neq, int istate){
super();
errorMsg = "lsoda: istate = "+istate+" and neq increased";
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,16 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class RepeatedInputException extends RuntimeException{
private String errorMsg;
public RepeatedInputException(){
super();
errorMsg ="lsoda: repeated calls with istate = 1 and tout = t\n"+
" run aborted.. apparent infinite loop";
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,23 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class TestsFailException extends RuntimeException{
private String errorMsg;
public TestsFailException(String type, double tn, double h){
super();
errorMsg = "lsoda: at t =" + tn + " and step size h = " + h+"\n";
switch (type){
case "error_test":
errorMsg += " error test failed repeatedly or with abs(h)=hmin";
break;
case "convergence_test":
errorMsg += " corrector convergence failed repeatedly or with abs(h)=hmin";
break;
}
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,23 @@
package org.meteoinfo.math.integrate.lsoda.exception;
public class TooMuchAccuracyException extends RuntimeException{
private String errorMsg;
public TooMuchAccuracyException(double tolsf){
super();
errorMsg = "lsoda: at start of problem, too much accuracy\n" +
" requested for precision of machine,\n" +
" suggested scaling factor = "+tolsf;
}
public TooMuchAccuracyException(double t, double tolsf){
errorMsg = "lsoda: at t = " + t + ", too much accuracy requested\n" +
" for precision of machine, suggested\n" +
" scaling factor = " + tolsf;
}
@Override
public String getMessage() {
return errorMsg;
}
}

View File

@ -0,0 +1,42 @@
package org.meteoinfo.math.integrate.lsoda.tools;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Data {
private String filePath;
private BufferedWriter bufferedWriter;
private int n;
public Data(String filepath, String fileName, int neq) throws IOException {
n=neq;
filePath = filepath + "/" + fileName;
// header
bufferedWriter = new BufferedWriter(new FileWriter(filePath));
bufferedWriter.write("t,");
for (int i=1; i<neq; i++)
bufferedWriter.write("y_"+i+",");
bufferedWriter.write("y_"+neq);
bufferedWriter.flush();
}
public void write(double t, double[] y) {
try{
bufferedWriter.newLine();
bufferedWriter.write(t +",");
for (int i=1; i<n; i++)
bufferedWriter.write(y[i]+",");
bufferedWriter.write(Double.toString(y[n]));
bufferedWriter.flush();
} catch (IOException e) {
System.out.println("Error occurred in file writing.");
}
}
public void closeWriter() throws IOException {
bufferedWriter.close();
}
}

View File

@ -30,12 +30,13 @@
<module>meteoinfo-projection</module>
<module>meteoinfo-mkl</module>
<module>meteoinfo-render2d</module>
<module>meteoinfo-jython</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<revision>4.0.4</revision>
<revision>4.0.5</revision>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.release>8</maven.compiler.release>