summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/ode/events/EventFilter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/ode/events/EventFilter.java')
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/EventFilter.java204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/ode/events/EventFilter.java b/src/main/java/org/apache/commons/math3/ode/events/EventFilter.java
new file mode 100644
index 0000000..ffc2715
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/EventFilter.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+import java.util.Arrays;
+
+/** Wrapper used to detect only increasing or decreasing events.
+ *
+ * <p>General {@link EventHandler events} are defined implicitly
+ * by a {@link EventHandler#g(double, double[]) g function} crossing
+ * zero. This function needs to be continuous in the event neighborhood,
+ * and its sign must remain consistent between events. This implies that
+ * during an ODE integration, events triggered are alternately events
+ * for which the function increases from negative to positive values,
+ * and events for which the function decreases from positive to
+ * negative values.
+ * </p>
+ *
+ * <p>Sometimes, users are only interested in one type of event (say
+ * increasing events for example) and not in the other type. In these
+ * cases, looking precisely for all events location and triggering
+ * events that will later be ignored is a waste of computing time.</p>
+ *
+ * <p>Users can wrap a regular {@link EventHandler event handler} in
+ * an instance of this class and provide this wrapping instance to
+ * the {@link org.apache.commons.math3.ode.FirstOrderIntegrator ODE solver}
+ * in order to avoid wasting time looking for uninteresting events.
+ * The wrapper will intercept the calls to the {@link
+ * EventHandler#g(double, double[]) g function} and to the {@link
+ * EventHandler#eventOccurred(double, double[], boolean)
+ * eventOccurred} method in order to ignore uninteresting events. The
+ * wrapped regular {@link EventHandler event handler} will the see only
+ * the interesting events, i.e. either only {@code increasing} events or
+ * {@code decreasing} events. the number of calls to the {@link
+ * EventHandler#g(double, double[]) g function} will also be reduced.</p>
+ *
+ * @since 3.2
+ */
+
+public class EventFilter implements EventHandler {
+
+ /** Number of past transformers updates stored. */
+ private static final int HISTORY_SIZE = 100;
+
+ /** Wrapped event handler. */
+ private final EventHandler rawHandler;
+
+ /** Filter to use. */
+ private final FilterType filter;
+
+ /** Transformers of the g function. */
+ private final Transformer[] transformers;
+
+ /** Update time of the transformers. */
+ private final double[] updates;
+
+ /** Indicator for forward integration. */
+ private boolean forward;
+
+ /** Extreme time encountered so far. */
+ private double extremeT;
+
+ /** Wrap an {@link EventHandler event handler}.
+ * @param rawHandler event handler to wrap
+ * @param filter filter to use
+ */
+ public EventFilter(final EventHandler rawHandler, final FilterType filter) {
+ this.rawHandler = rawHandler;
+ this.filter = filter;
+ this.transformers = new Transformer[HISTORY_SIZE];
+ this.updates = new double[HISTORY_SIZE];
+ }
+
+ /** {@inheritDoc} */
+ public void init(double t0, double[] y0, double t) {
+
+ // delegate to raw handler
+ rawHandler.init(t0, y0, t);
+
+ // initialize events triggering logic
+ forward = t >= t0;
+ extremeT = forward ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ Arrays.fill(transformers, Transformer.UNINITIALIZED);
+ Arrays.fill(updates, extremeT);
+
+ }
+
+ /** {@inheritDoc} */
+ public double g(double t, double[] y) {
+
+ final double rawG = rawHandler.g(t, y);
+
+ // search which transformer should be applied to g
+ if (forward) {
+ final int last = transformers.length - 1;
+ if (extremeT < t) {
+ // we are at the forward end of the history
+
+ // check if a new rough root has been crossed
+ final Transformer previous = transformers[last];
+ final Transformer next = filter.selectTransformer(previous, rawG, forward);
+ if (next != previous) {
+ // there is a root somewhere between extremeT and t.
+ // the new transformer is valid for t (this is how we have just computed
+ // it above), but it is in fact valid on both sides of the root, so
+ // it was already valid before t and even up to previous time. We store
+ // the switch at extremeT for safety, to ensure the previous transformer
+ // is not applied too close of the root
+ System.arraycopy(updates, 1, updates, 0, last);
+ System.arraycopy(transformers, 1, transformers, 0, last);
+ updates[last] = extremeT;
+ transformers[last] = next;
+ }
+
+ extremeT = t;
+
+ // apply the transform
+ return next.transformed(rawG);
+
+ } else {
+ // we are in the middle of the history
+
+ // select the transformer
+ for (int i = last; i > 0; --i) {
+ if (updates[i] <= t) {
+ // apply the transform
+ return transformers[i].transformed(rawG);
+ }
+ }
+
+ return transformers[0].transformed(rawG);
+
+ }
+ } else {
+ if (t < extremeT) {
+ // we are at the backward end of the history
+
+ // check if a new rough root has been crossed
+ final Transformer previous = transformers[0];
+ final Transformer next = filter.selectTransformer(previous, rawG, forward);
+ if (next != previous) {
+ // there is a root somewhere between extremeT and t.
+ // the new transformer is valid for t (this is how we have just computed
+ // it above), but it is in fact valid on both sides of the root, so
+ // it was already valid before t and even up to previous time. We store
+ // the switch at extremeT for safety, to ensure the previous transformer
+ // is not applied too close of the root
+ System.arraycopy(updates, 0, updates, 1, updates.length - 1);
+ System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1);
+ updates[0] = extremeT;
+ transformers[0] = next;
+ }
+
+ extremeT = t;
+
+ // apply the transform
+ return next.transformed(rawG);
+
+ } else {
+ // we are in the middle of the history
+
+ // select the transformer
+ for (int i = 0; i < updates.length - 1; ++i) {
+ if (t <= updates[i]) {
+ // apply the transform
+ return transformers[i].transformed(rawG);
+ }
+ }
+
+ return transformers[updates.length - 1].transformed(rawG);
+
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Action eventOccurred(double t, double[] y, boolean increasing) {
+ // delegate to raw handler, fixing increasing status on the fly
+ return rawHandler.eventOccurred(t, y, filter.getTriggeredIncreasing());
+ }
+
+ /** {@inheritDoc} */
+ public void resetState(double t, double[] y) {
+ // delegate to raw handler
+ rawHandler.resetState(t, y);
+ }
+
+}