PeerCache.java

  1. /* Copyright 2022-2025 Thales Alenia Space
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.frames;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.orekit.time.AbsoluteDate;
  21. import org.orekit.time.FieldAbsoluteDate;

  22. import java.util.Map;
  23. import java.util.concurrent.ConcurrentHashMap;
  24. import java.util.concurrent.locks.ReentrantReadWriteLock;
  25. import java.util.function.Function;

  26. /** Cache for frame transforms.
  27.  * <p>
  28.  * This class is thread-safe.
  29.  * </p>
  30.  * @author Luc Maisonobe
  31.  * @since 13.1
  32.  */
  33. class PeerCache {

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

  36.     /** Cache for transforms with peer frame. */
  37.     private volatile CachedTransformProvider cache;

  38.     /** Lock for peer frame cache. */
  39.     private final ReentrantReadWriteLock lock;

  40.     /** Cache for transforms with peer frame. */
  41.     private volatile Map<Field<? extends CalculusFieldElement<?>>, FieldCachedTransformProvider<?>> fieldCaches;

  42.     /** create an instance not associated with any peer.
  43.      * @param origin origin frame
  44.      */
  45.     PeerCache(final Frame origin) {
  46.         this.origin      = origin;
  47.         this.cache       = null;
  48.         this.fieldCaches = null;
  49.         this.lock        = new ReentrantReadWriteLock();
  50.     }

  51.     /** Associate a cache with a peer frame, caching transforms.
  52.      * <p>
  53.      * The cache is a LRU cache (Least Recently Used), so entries remain in
  54.      * the cache if they are used frequently, and only older entries
  55.      * that have not been accessed for a while will be expunged.
  56.      * </p>
  57.      * <p>
  58.      * If a peer was already associated with this frame, it will be overridden.
  59.      * </p>
  60.      * <p>
  61.      * Peering is unidirectional, i.e. if frameA is peered with frameB,
  62.      * then frameB may be peered with another frameC or no frame at all.
  63.      * This allows several frames to be peered with a pivot one (typically
  64.      * Earth frame and many topocentric frames all peered with one inertial frame).
  65.      * </p>
  66.      * @param peer peer frame (if null, cache is cleared)
  67.      * @param cacheSize number of transforms kept in the date-based cache
  68.      */
  69.     public void setPeerCaching(final Frame peer, final int cacheSize) {

  70.         lock.writeLock().lock();
  71.         try {

  72.             if (peer == null) {
  73.                 // clear peering
  74.                 cache       = null;
  75.                 fieldCaches = null;
  76.             }

  77.             // caching for regular dates
  78.             cache = createCache(peer, cacheSize);

  79.             // caching for field dates
  80.             fieldCaches = new ConcurrentHashMap<>();
  81.         } finally {
  82.             lock.writeLock().unlock();
  83.         }

  84.     }

  85.     /** Get the peer associated to this frame.
  86.      * @return peer associated with this frame, null if not peered at all
  87.      */
  88.     Frame getPeer() {
  89.         lock.readLock().lock();
  90.         try {
  91.             return cache == null ? null : cache.getDestination();
  92.         } finally {
  93.             lock.readLock().unlock();
  94.         }
  95.     }

  96.     /** Get the cached transform provider associated with this destination.
  97.      * @param destination destination frame to which we want to transform vectors
  98.      * @return cached transform provider, or null if destination is not the instance peer
  99.      */
  100.     CachedTransformProvider getCachedTransformProvider(final Frame destination) {
  101.         lock.readLock().lock();
  102.         try {
  103.             if (cache == null || cache.getDestination() != destination) {
  104.                 return null;
  105.             } else {
  106.                 return cache;
  107.             }
  108.         } finally {
  109.             lock.readLock().unlock();
  110.         }
  111.     }

  112.     /** Get the cached transform provider associated with this destination.
  113.      * @param <T> the type of the field elements
  114.      * @param destination destination frame to which we want to transform vectors
  115.      * @param field field elements belong to
  116.      * @return cached transform provider, or null if destination is not the instance peer
  117.      */
  118.     @SuppressWarnings("unchecked")
  119.     <T extends CalculusFieldElement<T>> FieldCachedTransformProvider<T> getCachedTransformProvider(final Frame destination,
  120.                                                                                                    final Field<T> field) {
  121.         lock.readLock().lock();
  122.         try {
  123.             if (cache == null || cache.getDestination() != destination) {
  124.                 return null;
  125.             } else {
  126.                 @SuppressWarnings("unchedked")
  127.                 final FieldCachedTransformProvider<T> tp =
  128.                         (FieldCachedTransformProvider<T>) fieldCaches.computeIfAbsent(field,
  129.                                                                                       f -> createCache(destination,
  130.                                                                                                        cache.getCacheSize(),
  131.                                                                                                        field));
  132.                 return tp;
  133.             }
  134.         } finally {
  135.             lock.readLock().unlock();
  136.         }
  137.     }

  138.     /** Create cache.
  139.      * @param peer peer frame
  140.      * @param cacheSize number of transforms kept in the date-based cache
  141.      * @return built cache
  142.      * @since 13.0.3
  143.      */
  144.     private CachedTransformProvider createCache(final Frame peer, final int cacheSize) {
  145.         final Function<AbsoluteDate, Transform> fullGenerator =
  146.                 date -> origin.getTransformTo(peer,
  147.                                               Transform.IDENTITY,
  148.                                               frame -> frame.getTransformProvider().getTransform(date),
  149.                                               (t1, t2) -> new Transform(date, t1, t2),
  150.                                               Transform::getInverse);
  151.         final Function<AbsoluteDate, KinematicTransform> kinematicGenerator =
  152.                 date -> origin.getTransformTo(peer,
  153.                                               KinematicTransform.getIdentity(),
  154.                                               frame -> frame.getTransformProvider().getTransform(date),
  155.                                               (t1, t2) -> KinematicTransform.compose(date, t1, t2),
  156.                                               KinematicTransform::getInverse);
  157.         final Function<AbsoluteDate, StaticTransform> staticGenerator =
  158.                 date -> origin.getTransformTo(peer,
  159.                                               StaticTransform.getIdentity(),
  160.                                               frame -> frame.getTransformProvider().getTransform(date),
  161.                                               (t1, t2) -> StaticTransform.compose(date, t1, t2),
  162.                                               StaticTransform::getInverse);
  163.         return new CachedTransformProvider(origin, peer,
  164.                                            fullGenerator, kinematicGenerator, staticGenerator,
  165.                                            cacheSize);
  166.     }

  167.     /** Create field cache.
  168.      * @param <T> type of the field elements
  169.      * @param peer peer frame
  170.      * @param cacheSize number of transforms kept in the date-based cache
  171.      * @param field field elements belong to
  172.      * @return built cache
  173.      * @since 13.0.3
  174.      */
  175.     private <T extends CalculusFieldElement<T>> FieldCachedTransformProvider<T>
  176.         createCache(final Frame peer, final int cacheSize, final Field<T> field) {
  177.         final Function<FieldAbsoluteDate<T>, FieldTransform<T>> fullGenerator =
  178.                 d -> origin.getTransformTo(peer,
  179.                                            FieldTransform.getIdentity(field),
  180.                                            frame -> frame.getTransformProvider().getTransform(d),
  181.                                            (FieldTransform<T> t1, FieldTransform<T> t2) -> new FieldTransform<>(d, t1, t2),
  182.                                            FieldTransform::getInverse);
  183.         final Function<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicGenerator =
  184.                 d -> origin.getTransformTo(peer,
  185.                                            FieldKinematicTransform.getIdentity(field),
  186.                                            frame -> frame.getTransformProvider().getTransform(d),
  187.                                            (t1, t2) -> FieldKinematicTransform.compose(d, t1, t2),
  188.                                            FieldKinematicTransform::getInverse);
  189.         final Function<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticGenerator =
  190.                 d -> origin.getTransformTo(peer,
  191.                                            FieldStaticTransform.getIdentity(field),
  192.                                            frame -> frame.getTransformProvider().getTransform(d),
  193.                                            (t1, t2) -> FieldStaticTransform.compose(d, t1, t2),
  194.                                            FieldStaticTransform::getInverse);
  195.         return new FieldCachedTransformProvider<>(origin, peer,
  196.                                                   fullGenerator, kinematicGenerator, staticGenerator,
  197.                                                   cacheSize);
  198.     }

  199. }