FieldCachedTransformProvider.java

/* Copyright 2022-2025 Thales Alenia Space
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS 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.orekit.frames;

import org.hipparchus.CalculusFieldElement;
import org.orekit.time.FieldAbsoluteDate;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;

/** Thread-safe cached provider for frame transforms.
 * <p>
 * This provider is based on a thread-safe Least Recently Used cache
 * using date as it access key, hence saving computation time on
 * transform building.
 * </p>
 * <p>
 * This class is thread-safe.
 * </p>
 * @param <T> type of the field elements
 * @author Luc Maisonobe
 * @since 13.0.3
 */
public class FieldCachedTransformProvider<T extends CalculusFieldElement<T>> {

    /** Origin frame. */
    private final Frame origin;

    /** Destination frame. */
    private final Frame destination;

    /** Number of transforms kept in the date-based cache. */
    private final int cacheSize;

    /** Generator for full transforms. */
    private final Function<FieldAbsoluteDate<T>, FieldTransform<T>> fullGenerator;

    /** Generator for kinematic transforms. */
    private final Function<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicGenerator;

    /** Generator for static transforms. */
    private final Function<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticGenerator;

    /** Lock for concurrent access. */
    private final ReentrantLock lock;

    /** Transforms LRU cache. */
    private final Map<FieldAbsoluteDate<T>, FieldTransform<T>> fullCache;

    /** Transforms LRU cache. */
    private final Map<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicCache;

    /** Transforms LRU cache. */
    private final Map<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticCache;

    /**
     * Simple constructor.
     * @param origin             origin frame
     * @param destination        destination frame
     * @param fullGenerator      generator for full transforms
     * @param kinematicGenerator generator for kinematic transforms
     * @param staticGenerator    generator for static transforms
     * @param cacheSize          number of transforms kept in the date-based cache
     */
    public FieldCachedTransformProvider(final Frame origin, final Frame destination,
                                        final Function<FieldAbsoluteDate<T>, FieldTransform<T>> fullGenerator,
                                        final Function<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicGenerator,
                                        final Function<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticGenerator,
                                        final int cacheSize) {

        this.origin             = origin;
        this.destination        = destination;
        this.cacheSize          = cacheSize;
        this.fullGenerator      = fullGenerator;
        this.kinematicGenerator = kinematicGenerator;
        this.staticGenerator    = staticGenerator;
        this.lock               = new ReentrantLock();

        // cache for full transforms
        this.fullCache = new LinkedHashMap<FieldAbsoluteDate<T>, FieldTransform<T>>(cacheSize, 0.75f, true) {
            /** {@inheritDoc} */
            @Override
            protected boolean removeEldestEntry(final Map.Entry<FieldAbsoluteDate<T>, FieldTransform<T>> eldest) {
                return size() > cacheSize;
            }
        };

        // cache for kinematic transforms
        this.kinematicCache = new LinkedHashMap<FieldAbsoluteDate<T>, FieldKinematicTransform<T>>(cacheSize, 0.75f, true) {
            /** {@inheritDoc} */
            @Override
            protected boolean removeEldestEntry(final Map.Entry<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> eldest) {
                return size() > cacheSize;
            }
        };

        // cache for static transforms
        this.staticCache = new LinkedHashMap<FieldAbsoluteDate<T>, FieldStaticTransform<T>>(cacheSize, 0.75f, true) {
            /** {@inheritDoc} */
            @Override
            protected boolean removeEldestEntry(final Map.Entry<FieldAbsoluteDate<T>, FieldStaticTransform<T>> eldest) {
                return size() > cacheSize;
            }
        };

    }

    /** Get origin frame.
     * @return origin frame
     */
    public Frame getOrigin() {
        return origin;
    }

    /** Get destination frame.
     * @return destination frame
     */
    public Frame getDestination() {
        return destination;
    }

    /** Get the nmber of transforms kept in the date-based cache.
     * @return nmber of transforms kept in the date-based cache
     */
    public int getCacheSize() {
        return cacheSize;
    }

    /** Get the {@link Transform} corresponding to specified date.
     * @param date current date
     * @return transform at specified date
     */
    public FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
        lock.lock();
        try {
            return fullCache.computeIfAbsent(date, fullGenerator);
        } finally {
            lock.unlock();
        }
    }

    /** Get the {@link Transform} corresponding to specified date.
     * @param date current date
     * @return transform at specified date
     */
    public FieldKinematicTransform<T> getKinematicTransform(final FieldAbsoluteDate<T> date) {
        lock.lock();
        try {
            return kinematicCache.computeIfAbsent(date, kinematicGenerator);
        } finally {
            lock.unlock();
        }
    }

    /** Get the {@link Transform} corresponding to specified date.
     * @param date current date
     * @return transform at specified date
     */
    public FieldStaticTransform<T> getStaticTransform(final FieldAbsoluteDate<T> date) {
        lock.lock();
        try {
            return staticCache.computeIfAbsent(date, staticGenerator);
        } finally {
            lock.unlock();
        }
    }

}