| /* |
| * 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.math4.legacy.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.math4.legacy.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} */ |
| @Override |
| 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} */ |
| @Override |
| 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} */ |
| @Override |
| 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} */ |
| @Override |
| public void resetState(double t, double[] y) { |
| // delegate to raw handler |
| rawHandler.resetState(t, y); |
| } |
| |
| } |