1   /* Copyright 2022-2025 Romain Serra
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.forces.radiation;
18  
19  import org.hipparchus.complex.Complex;
20  import org.hipparchus.complex.ComplexField;
21  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
22  import org.hipparchus.geometry.euclidean.threed.Vector3D;
23  import org.hipparchus.ode.events.Action;
24  import org.hipparchus.util.FastMath;
25  import org.junit.jupiter.api.Assertions;
26  import org.junit.jupiter.api.Test;
27  import org.mockito.Mockito;
28  import org.orekit.frames.Frame;
29  import org.orekit.propagation.FieldSpacecraftState;
30  import org.orekit.propagation.SpacecraftState;
31  import org.orekit.propagation.events.*;
32  import org.orekit.propagation.events.handlers.EventHandler;
33  import org.orekit.propagation.events.handlers.FieldStopOnEvent;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.time.FieldAbsoluteDate;
36  import org.orekit.utils.ExtendedPositionProvider;
37  
38  import java.util.List;
39  
40  class CylindricallyShadowedLightFluxModelTest {
41  
42      @Test
43      void testGetLightingRatio() {
44          // GIVEN
45          final double occultingBodyRadius = 0.5;
46          final Vector3D sunPosition = Vector3D.PLUS_I.scalarMultiply(10.);
47          final ExtendedPositionProvider sun = mockProvider(sunPosition);
48          final CylindricallyShadowedLightFluxModel model = new CylindricallyShadowedLightFluxModel(Double.NaN, sun, occultingBodyRadius);
49          final CylindricalShadowEclipseDetector detector = new CylindricalShadowEclipseDetector(sun,
50                  model.getOccultingBodyRadius(), Mockito.mock(EventHandler.class));
51          // WHEN & THEN
52          final int size = 100;
53          for (int i = 0; i < size; i++) {
54              final double angle = i * FastMath.PI / size;
55              final Vector3D position = new Vector3D(angle, 0.);
56              final double ratio = model.getLightingRatio(position, sunPosition);
57              final SpacecraftState state = mockState(position);
58              final double g = detector.g(state);
59              if (g < 0.) {
60                  Assertions.assertEquals(0., ratio);
61              } else {
62                  Assertions.assertEquals(1., ratio);
63              }
64          }
65      }
66  
67      private ExtendedPositionProvider mockProvider(final Vector3D sunPosition) {
68          final ExtendedPositionProvider mockedProvider = Mockito.mock(ExtendedPositionProvider.class);
69          Mockito.when(mockedProvider.getPosition(Mockito.any(AbsoluteDate.class), Mockito.any(Frame.class)))
70                  .thenReturn(sunPosition);
71          return mockedProvider;
72      }
73  
74      private SpacecraftState mockState(final Vector3D position) {
75          final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
76          Mockito.when(mockedState.getPosition()).thenReturn(position);
77          Mockito.when(mockedState.getDate()).thenReturn(AbsoluteDate.ARBITRARY_EPOCH);
78          Mockito.when(mockedState.getFrame()).thenReturn(Mockito.mock(Frame.class));
79          return mockedState;
80      }
81  
82      @Test
83      void testFieldGetLightingRatio() {
84          // GIVEN
85          final ComplexField field = ComplexField.getInstance();
86          final double occultingBodyRadius = 0.5;
87          final FieldVector3D<Complex> sunPosition = FieldVector3D.getPlusI(field).scalarMultiply(10.);
88          final ExtendedPositionProvider sun = mockFieldProvider(sunPosition);
89          final CylindricallyShadowedLightFluxModel model = new CylindricallyShadowedLightFluxModel(Double.NaN, sun, occultingBodyRadius);
90          final FieldCylindricalShadowEclipseDetector<Complex> detector = new FieldCylindricalShadowEclipseDetector<>(sun,
91                  new Complex(model.getOccultingBodyRadius()), new FieldStopOnEvent<>());
92          // WHEN & THEN
93          final int size = 100;
94          for (int i = 0; i < size; i++) {
95              final double angle = i * FastMath.PI / size;
96              final FieldVector3D<Complex> position = new FieldVector3D<>(field, new Vector3D(angle, 0.));
97              final double ratio = model.getLightingRatio(position, sunPosition).getReal();
98              final FieldSpacecraftState<Complex> state = mockFieldState(position);
99              final double g = detector.g(state).getReal();
100             if (g < 0.) {
101                 Assertions.assertEquals(0., ratio);
102             } else {
103                 Assertions.assertEquals(1., ratio);
104             }
105         }
106     }
107 
108     @SuppressWarnings("unchecked")
109     private ExtendedPositionProvider mockFieldProvider(final FieldVector3D<Complex> sunPosition) {
110         final ExtendedPositionProvider mockedProvider = Mockito.mock(ExtendedPositionProvider.class);
111         Mockito.when(mockedProvider.getPosition(Mockito.any(FieldAbsoluteDate.class), Mockito.any(Frame.class)))
112                 .thenReturn(sunPosition);
113         return mockedProvider;
114     }
115 
116     @SuppressWarnings("unchecked")
117     private FieldSpacecraftState<Complex> mockFieldState(final FieldVector3D<Complex> position) {
118         final FieldSpacecraftState<Complex> mockedState = Mockito.mock(FieldSpacecraftState.class);
119         Mockito.when(mockedState.getPosition()).thenReturn(position);
120         Mockito.when(mockedState.getDate()).thenReturn(Mockito.mock(FieldAbsoluteDate.class));
121         Mockito.when(mockedState.getFrame()).thenReturn(Mockito.mock(Frame.class));
122         return mockedState;
123     }
124 
125     @Test
126     void testGetEclipseConditionsDetector() {
127         // GIVEN
128         final CylindricallyShadowedLightFluxModel model = Mockito.mock(CylindricallyShadowedLightFluxModel.class);
129         Mockito.when(model.getOccultingBodyRadius()).thenReturn(1.);
130         Mockito.when(model.getEclipseConditionsDetector()).thenCallRealMethod();
131         Mockito.when(model.getEventDetectionSettings()).thenReturn(EventDetectionSettings.getDefaultEventDetectionSettings());
132         // WHEN
133         final List<EventDetector> detectors = model.getEclipseConditionsDetector();
134         // THEN
135         Assertions.assertEquals(1, detectors.size());
136         final EventDetector detector = detectors.get(0);
137         Assertions.assertInstanceOf(CylindricalShadowEclipseDetector.class, detector);
138         final Action action = detector.getHandler().eventOccurred(Mockito.mock(SpacecraftState.class), detector, false);
139         Assertions.assertEquals(Action.RESET_DERIVATIVES, action);
140     }
141 
142     @Test
143     void testGetFieldEclipseConditionsDetector() {
144         // GIVEN
145         final ComplexField field = ComplexField.getInstance();
146         final CylindricallyShadowedLightFluxModel model = Mockito.mock(CylindricallyShadowedLightFluxModel.class);
147         Mockito.when(model.getOccultingBodyRadius()).thenReturn(1.);
148         Mockito.when(model.getEventDetectionSettings()).thenReturn(EventDetectionSettings.getDefaultEventDetectionSettings());
149         Mockito.when(model.getFieldEclipseConditionsDetector(field)).thenCallRealMethod();
150         Mockito.when(model.getEclipseConditionsDetector()).thenCallRealMethod();
151         // WHEN
152         final List<FieldEventDetector<Complex>> fieldEventDetectors = model.getFieldEclipseConditionsDetector(field);
153         // THEN
154         final List<EventDetector> eventDetectors = model.getEclipseConditionsDetector();
155         Assertions.assertEquals(eventDetectors.size(), fieldEventDetectors.size());
156         Assertions.assertInstanceOf(FieldCylindricalShadowEclipseDetector.class, fieldEventDetectors.get(0));
157         final FieldCylindricalShadowEclipseDetector<Complex> fieldShadowDetector = (FieldCylindricalShadowEclipseDetector<Complex>) fieldEventDetectors.get(0);
158         final CylindricalShadowEclipseDetector shadowDetector = (CylindricalShadowEclipseDetector) eventDetectors.get(0);
159         Assertions.assertEquals(shadowDetector.getThreshold(), fieldShadowDetector.getThreshold().getReal());
160         compareActions(shadowDetector, fieldShadowDetector);
161     }
162 
163     @SuppressWarnings("unchecked")
164     private void compareActions(final EventDetector eventDetector, final FieldEventDetector<Complex> fieldEventDetector) {
165         final boolean isIncreasing = false;
166         final Action expectedAction = eventDetector.getHandler().eventOccurred(Mockito.mock(SpacecraftState.class), eventDetector, isIncreasing);
167         final Action actualAction = fieldEventDetector.getHandler().eventOccurred(Mockito.mock(FieldSpacecraftState.class), fieldEventDetector, isIncreasing);
168         Assertions.assertEquals(expectedAction, actualAction);
169     }
170 
171 }