Source code for test_unitTestTabularAtmosphere

#
#  ISC License
#
#  Copyright (c) 2022, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
#
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
#
#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#


#
#   Unit Test Script
#   Module Name:        tabularAtmosphere
#   Author:             Mikaela Felix
#   Creation Date:      Feb 11, 2022
#

import os

import numpy as np
import pytest
from Basilisk import __path__

bskPath = __path__[0]
fileName = os.path.basename(os.path.splitext(__file__)[0])

from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import unitTestSupport
from Basilisk.simulation import tabularAtmosphere
from Basilisk.utilities import macros
from Basilisk.architecture import messaging
from Basilisk.architecture import bskLogging
from Basilisk.utilities import orbitalMotion
from Basilisk.utilities.readAtmTable import readAtmTable

[docs]@pytest.mark.parametrize("accuracy", [1e-12]) @pytest.mark.parametrize("altitude", [42.0, 33.33333, 10000.0, -10.0]) # exact, interpolate, above, below @pytest.mark.parametrize("useMinReach", [ True, False]) @pytest.mark.parametrize("useMaxReach", [ True, False]) def test_tabularAtmosphere(altitude, accuracy, useMinReach, useMaxReach): r""" **Validation Test Description** TabularAtmosphere interpolates from user-provided data to compute density and temperature at the current s/c altitude. The unit test checks altitudes at, between, above, and below the values included in the table. This test uses a python helper function to provide data from EarthGRAM (see supportData\AtmosphereData\support). Data lists can also be manually-input, but check sorting and units per documentation and support info (above). The module returns 0 for both density and temperature if ANY ONE of the following conditions is met: - altitude below minimum value in provided table - altitude above maximum value in provided table - altitude below envMinReach - altitude above envMaxReach Note that this results in nonphysical behavior for temperature (absolute zero) when outside defined range. **Test Parameters** Args: - altitude (float): Spacecraft altitude for which density, temperature are returned - accuracy (float): accuracy value used in validation tests - useMinReach (bool): set value of envMinReach - useMaxReach (bool): set value of envMaxReach **Description of Variables Being Tested** The unit test checks density (kg/m^3) and temperature (K) against their expected values: - ``densData[0]`` - ``tempData[0]`` """ # each test method requires a single assert method to be called [testResults, testMessage] = tabularAtmosphereTestFunction(altitude, accuracy, useMinReach, useMaxReach) assert testResults < 1, testMessage
def tabularAtmosphereTestFunction(altitude, accuracy, useMinReach, useMaxReach): testFailCount = 0 # zero unit test result counter unitTaskName = "unitTask" # arbitrary name (don't change) unitProcessName = "TestProcess" # arbitrary name (don't change) bskLogging.setDefaultLogLevel(bskLogging.BSK_WARNING) # Create a sim module as an empty container unitTestSim = SimulationBaseClass.SimBaseClass() # Create test thread testProcessRate = macros.sec2nano(0.5) # update process rate update time testProc = unitTestSim.CreateNewProcess(unitProcessName) testProc.addTask(unitTestSim.CreateNewTask(unitTaskName, testProcessRate)) # Construct algorithm and associated C++ container module = tabularAtmosphere.TabularAtmosphere() # update with current values module.ModelTag = "tabularAtmosphere" # update python name of test module # define constants & load data r_eq = 6378136.6 filename = bskPath + '/../../supportData/AtmosphereData/EarthGRAMNominal.txt' altList, rhoList, tempList = readAtmTable(filename,'EarthGRAM') # assign constants & ref. data to module module.planetRadius = r_eq module.altList = tabularAtmosphere.DoubleVector(altList) module.rhoList = tabularAtmosphere.DoubleVector(rhoList) module.tempList = tabularAtmosphere.DoubleVector(tempList) # Add test module to runtime call list unitTestSim.AddModelToTask(unitTaskName, module) # CHECK - env min and max if useMinReach: minReach = 50.0 * 1000 module.envMinReach = minReach else: minReach = -1.0 * 1000 if useMaxReach: maxReach = 20.0 * 1000 module.envMaxReach = maxReach else: maxReach = 5000.0 * 1000 # setup orbit and simulation time r0 = r_eq + (altitude * 1000.0) # meters oe = orbitalMotion.ClassicElements() mu = 0.3986004415E+15 # meters^3/s^2 oe.a = r0 oe.e = 0.0 oe.i = 45.0 * macros.D2R oe.Omega = 30.0 * macros.D2R oe.omega = 120.0 * macros.D2R oe.f = 0.0 * macros.D2R r0N, v0N = orbitalMotion.elem2rv(mu, oe) # create the input messages scStateMsg = messaging.SCStatesMsgPayload() # Create a structure for the input message scStateMsg.r_BN_N = np.array(r0N) scInMsg = messaging.SCStatesMsg().write(scStateMsg) # add spacecraft to environment model module.addSpacecraftToModel(scInMsg) # Setup logging on the test module output message so that we get all the writes to it dataLog = module.envOutMsgs[0].recorder() unitTestSim.AddModelToTask(unitTaskName, dataLog) # Need to call the self-init and cross-init methods unitTestSim.InitializeSimulation() # Set the simulation time. # NOTE: the total simulation time may be longer than this value. The # simulation is stopped at the next logging event on or after the # simulation end time. unitTestSim.ConfigureStopTime(macros.sec2nano(1.0)) # seconds to stop simulation # Begin the simulation time run set above unitTestSim.ExecuteSimulation() # This pulls the actual data log from the simulation run. densData = dataLog.neutralDensity tempData = dataLog.localTemp # define python function to compute truth values def tabAtmoComp(val, xList, yList): if (val < xList[0]) or (val <= minReach): out = 0.0 return out elif (val > xList[-1]) or (val >= maxReach): out = 0.0 return out else: for i, x in enumerate(xList): if x >= val: x0 = xList[i-1] y0 = yList[i-1] y1 = yList[i] m = (y1 - y0)/(x - x0) out = y0 + (val - x0) * m return out # compute truth values trueDensity = tabAtmoComp(altitude * 1000, altList, rhoList) print('\nmodule density: {0:.6e}'.format(densData[0])) print('true density: {0:.6e}'.format(trueDensity)) trueTemp = tabAtmoComp(altitude * 1000, altList, tempList) print('\nmodule temperature: {0:.6e}'.format(tempData[0])) print('true temperature: {0:.6e}\n'.format(trueTemp)) # compare truth values to module results if trueDensity != 0: testFailCount = not unitTestSupport.isDoubleEqualRelative(densData[0], trueDensity, accuracy) else: testFailCount = not unitTestSupport.isDoubleEqual(densData[0], trueDensity, accuracy) if testFailCount == 0: testMessage = "density computed correctly" else: testMessage = "density computed incorrectly" # compare truth values to module results for temperature if trueTemp != 0 : # needs checking testFailCount = not unitTestSupport.isDoubleEqualRelative(tempData[0], trueTemp, accuracy) else: testFailCount = not unitTestSupport.isDoubleEqual(tempData[0], trueTemp, accuracy) if testFailCount == 0: testMessage += " and temperature computed correctly" else: testMessage += " and temperature computed incorrectly" # print out success message if no error were found if testFailCount == 0: print("PASSED: " + module.ModelTag) return [testFailCount, testMessage] # # This statement below ensures that the unitTestScript can be run as a # stand-along python script # if __name__ == "__main__": test_tabularAtmosphere( 10000.0, # altitude 1e-12, # accuracy True, True )