Repository: dsavransky/EXOSIMS

Issue:
Stationkeeping and Slew fuel should be separate
Currently, stationkeeping and slew fuel use is undifferentiated, which does not adequately describe the case where different propulsion systems, with different fuel sources, are being used for each case.  Sk and Slew fuel masses should be separate values, and kept track of separately, with the option to use a single total fuel mass for both in cases where the same propulsion system is employed for both activities (or two different propulsion systems use the same fuel source).

Discussion:

Patch:
diff --git a/EXOSIMS/MissionSim.py b/EXOSIMS/MissionSim.py
index 30af843e..fe3a8b74 100644
--- a/EXOSIMS/MissionSim.py
+++ b/EXOSIMS/MissionSim.py
@@ -396,8 +396,8 @@ class MissionSim(object):
                       'char_fEZ', 'char_dMag', 'char_WA', 'char_d']
         keysFA = ['FA_det_status', 'FA_char_status', 'FA_char_SNR', 
                   'FA_char_fEZ', 'FA_char_dMag', 'FA_char_WA']
-        keysOcculter = ['slew_time','slew_dV','det_dF_lateral','scMass',
-                        'char_dF_axial','det_mass_used','slew_mass_used',
+        keysOcculter = ['slew_time','slew_dV','det_dF_lateral','scMass','slewMass',
+                        'skMass','char_dF_axial','det_mass_used','slew_mass_used',
                         'det_dF_axial','det_dV','slew_angle','char_dF_lateral']
 
         assert key in (keysStar + keysPlans + keysParams + keysFA + keysOcculter), \
diff --git a/EXOSIMS/Observatory/SotoStarshade.py b/EXOSIMS/Observatory/SotoStarshade.py
index 568851e0..05f8edf3 100644
--- a/EXOSIMS/Observatory/SotoStarshade.py
+++ b/EXOSIMS/Observatory/SotoStarshade.py
@@ -515,6 +515,9 @@ class SotoStarshade(ObservatoryL2Halo):
         DRM['slew_mass_used'] = slew_mass_used.to('kg')
         self.scMass = self.scMass - slew_mass_used
         DRM['scMass'] = self.scMass.to('kg')
+        if self.twotanks:
+            self.slewMass = self.slewMass - slew_mass_used
+            DRM['slewMass'] = self.slewMass.to('kg')
         
         return DRM
     
diff --git a/EXOSIMS/Prototypes/Observatory.py b/EXOSIMS/Prototypes/Observatory.py
index efcf22de..abc331bf 100644
--- a/EXOSIMS/Prototypes/Observatory.py
+++ b/EXOSIMS/Prototypes/Observatory.py
@@ -50,6 +50,12 @@ class Observatory(object):
             Occulter (maneuvering sc) wet mass in units of kg
         dryMass (astropy Quantity): 
             Occulter (maneuvering sc) dry mass in units of kg
+        slewMass (astropy Quantity):
+            Occulter (maneuvering sc) slewing fuel in units of kg
+        skMass (astropy Quantity):
+            Occulter (maneuvering sc) station keeping fuel in units of kg
+        twotanks (boolean):
+            Boolean signifying if the Occulter (maneuvering sc) has two separate fuel tanks.
         coMass (astropy Quantity): 
             Telescope (non-maneuvering sc) mass in units of kg
         occulterSep (astropy Quantity): 
@@ -84,7 +90,8 @@ class Observatory(object):
     _modtype = 'Observatory'
 
     def __init__(self, koAngles_SolarPanel=[0,180],
-        ko_dtStep=1, settlingTime=1, thrust=450, slewIsp=4160., scMass=6000., 
+        ko_dtStep=1, settlingTime=1, thrust=450, slewIsp=4160., scMass=6000.,
+        slewMass=0.,skMass=0.,twotanks=False,
         dryMass=3400., coMass=5800., occulterSep=55000., skIsp=220., 
         defburnPortion=0.05, constTOF=14, maxdVpct=0.02, spkpath=None, checkKeepoutEnd=True, 
         forceStaticEphem=False, occ_dtmin=10., occ_dtmax=61., cachedir=None, **specs):
@@ -106,6 +113,9 @@ class Observatory(object):
         self.thrust = float(thrust)*u.mN                   # occulter slew thrust (mN)
         self.slewIsp = float(slewIsp)*u.s                  # occulter slew specific impulse (s)
         self.scMass = float(scMass)*u.kg                   # occulter initial (wet) mass (kg)
+        self.slewMass = float(slewMass)*u.kg               # slew fuel initial mass (kg)
+        self.skMass = float(skMass)*u.kg                   # station keeping fuel initial mass (kg)
+        self.twotanks = bool(twotanks)                     # boolean used to seperate manuevering fuel
         self.dryMass = float(dryMass)*u.kg                 # occulter dry mass (kg)
         self.coMass = float(coMass)*u.kg                   # telescope mass (kg)
         self.occulterSep = float(occulterSep)*u.km         # occulter-telescope distance (km)
@@ -119,6 +129,11 @@ class Observatory(object):
         self.maxdVpct = float(maxdVpct)                    # Maximum deltaV percent
         self.ao = self.thrust/self.scMass
 
+        # check that twotanks and dry mass add up to total mass
+        if self.twotanks:
+            assert self.slewMass > 0 and self.skMass > 0, 'Tank mass must be positive'
+            self.scMass = self.slewMass + self.skMass + self.dryMass
+
         # find the cache directory
         self.cachedir = get_cache_dir(cachedir)
         self._outspec['cachedir'] = self.cachedir
@@ -1319,7 +1334,9 @@ class Observatory(object):
         DRM['slew_mass_used'] = slew_mass_used.to('kg')
         self.scMass = self.scMass - slew_mass_used
         DRM['scMass'] = self.scMass.to('kg')
-        
+        if self.twotanks:
+            self.slewMass = self.slewMass - slew_mass_used
+            DRM['slewMass'] = self.slewMass.to('kg')
         return DRM
 
     class SolarEph:
diff --git a/EXOSIMS/Prototypes/SurveySimulation.py b/EXOSIMS/Prototypes/SurveySimulation.py
index 0b8361e8..855f531d 100644
--- a/EXOSIMS/Prototypes/SurveySimulation.py
+++ b/EXOSIMS/Prototypes/SurveySimulation.py
@@ -1794,7 +1794,10 @@ class SurveySimulation(object):
         # update spacecraft mass
         Obs.scMass = Obs.scMass - mass_used
         DRM['scMass'] = Obs.scMass.to('kg')
-        
+        if Obs.twotanks:
+            Obs.skMass = Obs.skMass - mass_used
+            DRM['skMass'] = Obs.skMass.to('kg')
+
         return DRM
 
     def reset_sim(self, genNewPlanets=True, rewindPlanets=True, seed=None):
diff --git a/EXOSIMS/Prototypes/TimeKeeping.py b/EXOSIMS/Prototypes/TimeKeeping.py
index d7aa8f93..9d3ed9a7 100644
--- a/EXOSIMS/Prototypes/TimeKeeping.py
+++ b/EXOSIMS/Prototypes/TimeKeeping.py
@@ -199,10 +199,16 @@ class TimeKeeping(object):
         is_over = ((self.currentTimeNorm + Obs.settlingTime + mode['syst']['ohTime'] >= self.missionLife.to('day')) \
             or (self.exoplanetObsTime.to('day') + Obs.settlingTime + mode['syst']['ohTime'] >= self.missionLife.to('day')*self.missionPortion) \
             or (self.currentTimeNorm + Obs.settlingTime + mode['syst']['ohTime'] >= self.OBendTimes[-1]) \
-            or (OS.haveOcculter and Obs.scMass < Obs.dryMass))
+            or (OS.haveOcculter and Obs.scMass < Obs.dryMass)
+            or (OS.haveOcculter and Obs.twotanks and Obs.slewMass < 0)
+            or (OS.haveOcculter and Obs.twotanks and Obs.skMass < 0))
 
         if (OS.haveOcculter and Obs.scMass < Obs.dryMass):
-            self.vprint('Total fuel mass (Obs.dryMass %.2f) less than (Obs.scMass %.2f) at currentTimeNorm %sd'%(Obs.dryMass.value, Obs.scMass.value, self.currentTimeNorm.to('day').round(2)))
+            self.vprint('Total Occulter mass (Obs.scMass %.2f) less than (Obs.dryMass %.2f) at currentTimeNorm %sd'%(Obs.scMass.value, Obs.dryMass.value, self.currentTimeNorm.to('day').round(2)))
+        if (OS.haveOcculter and Obs.twotanks and Obs.slewMass < 0):
+            self.vprint('Occulter slewing fuel mass (Obs.slewMass %.2f) less than 0 at currentTimeNorm %sd'%(Obs.slewMass.value, self.currentTimeNorm.to('day').round(2)))
+        if (OS.haveOcculter and Obs.twotanks and Obs.skMass < 0):
+            self.vprint('Occulter station keeping fuel mass (Obs.skMass %.2f) less than 0 at currentTimeNorm %sd'%(Obs.skMass.value, self.currentTimeNorm.to('day').round(2)))
         if (self.currentTimeNorm + Obs.settlingTime + mode['syst']['ohTime'] >= self.OBendTimes[-1]):
             self.vprint('Last Observing Block (OBnum %d, OBendTime[-1] %.2f) would be exceeded at currentTimeNorm %sd'%(self.OBnumber, self.OBendTimes[-1].value, self.currentTimeNorm.to('day').round(2)))
         if (self.exoplanetObsTime.to('day') + Obs.settlingTime + mode['syst']['ohTime'] >= self.missionLife.to('day')*self.missionPortion):
diff --git a/EXOSIMS/SurveySimulation/SS_char_only.py b/EXOSIMS/SurveySimulation/SS_char_only.py
index 982553a3..842c4781 100644
--- a/EXOSIMS/SurveySimulation/SS_char_only.py
+++ b/EXOSIMS/SurveySimulation/SS_char_only.py
@@ -155,12 +155,6 @@ class SS_char_only(SurveySimulation):
                 if np.isinf(TK.OBduration):
                     obsLength = (TK.obsEnd-TK.obsStart).to('day')
                     TK.next_observing_block(dt=obsLength)
-                
-                # with occulter, if spacecraft fuel is depleted, exit loop
-                if OS.haveOcculter and Obs.scMass < Obs.dryMass:
-
-                    self.vprint('Total fuel mass exceeded at %s'%TK.obsEnd.round(2))
-                    break
         
         else:
             dtsim = (time.time() - t0)*u.s
diff --git a/EXOSIMS/SurveySimulation/SS_char_only2.py b/EXOSIMS/SurveySimulation/SS_char_only2.py
index 87bbf5db..a1ed877b 100644
--- a/EXOSIMS/SurveySimulation/SS_char_only2.py
+++ b/EXOSIMS/SurveySimulation/SS_char_only2.py
@@ -142,12 +142,6 @@ class SS_char_only2(SurveySimulation):
                 if np.isinf(TK.OBduration):
                     obsLength = (TK.obsEnd-TK.obsStart).to('day')
                     TK.next_observing_block(dt=obsLength)
-                
-                # with occulter, if spacecraft fuel is depleted, exit loop
-                if OS.haveOcculter and Obs.scMass < Obs.dryMass:
-
-                    self.vprint('Total fuel mass exceeded at %s'%TK.obsEnd.round(2))
-                    break
         
         else:
             dtsim = (time.time() - t0)*u.s
diff --git a/EXOSIMS/SurveySimulation/SS_det_only.py b/EXOSIMS/SurveySimulation/SS_det_only.py
index d19f939d..50528ecf 100644
--- a/EXOSIMS/SurveySimulation/SS_det_only.py
+++ b/EXOSIMS/SurveySimulation/SS_det_only.py
@@ -122,11 +122,6 @@ class SS_det_only(SurveySimulation):
                 if np.isinf(TK.OBduration):
                     obsLength = (TK.obsEnd - TK.obsStart).to('day')
                     TK.next_observing_block(dt=obsLength)
-                
-                # with occulter, if spacecraft fuel is depleted, exit loop
-                if OS.haveOcculter and Obs.scMass < Obs.dryMass:
-                    self.vprint('Total fuel mass exceeded at %s'%TK.obsEnd.round(2))
-                    break
         
         else:
             dtsim = (time.time() - t0)*u.s
@@ -279,6 +274,9 @@ class SS_det_only(SurveySimulation):
             DRM['slew_mass_used'] = slew_mass_used.to('kg')
             Obs.scMass = Obs.scMass - slew_mass_used
             DRM['scMass'] = Obs.scMass.to('kg')
+            if Obs.twotanks:
+                Obs.slewMass = Obs.slewMass - slew_mass_used
+                DRM['slewMass'] = Obs.slewMass.to('kg')
             # update current time by adding slew time for the chosen target
             TK.allocate_time(slewTimes[sInd])
             if TK.mission_is_over():
diff --git a/EXOSIMS/SurveySimulation/tieredScheduler.py b/EXOSIMS/SurveySimulation/tieredScheduler.py
index 811090b1..1ce17c55 100644
--- a/EXOSIMS/SurveySimulation/tieredScheduler.py
+++ b/EXOSIMS/SurveySimulation/tieredScheduler.py
@@ -360,6 +360,9 @@ class tieredScheduler(SurveySimulation):
                     DRM['slew_mass_used'] = slew_mass_used.to('kg')
                     Obs.scMass = Obs.scMass - slew_mass_used
                     DRM['scMass'] = Obs.scMass.to('kg')
+                    if Obs.twotanks:
+                        Obs.slewMass = Obs.slewMass - slew_mass_used
+                        DRM['slewMass'] = Obs.slewMass.to('kg')
 
                     self.logger.info('  Starshade and telescope aligned at target star')
                     self.vprint('  Starshade and telescope aligned at target star')
@@ -442,11 +445,6 @@ class tieredScheduler(SurveySimulation):
                 # to the next OB with timestep equivalent to time spent on one target
                 if np.isinf(TK.OBduration) and (TK.missionPortion < 1):
                     self.arbitrary_time_advancement(TK.currentTimeNorm.to('day').copy() - DRM['arrival_time'])
-                
-                # With occulter, if spacecraft fuel is depleted, exit loop
-                if Obs.scMass < Obs.dryMass:
-                    self.vprint('Total fuel mass exceeded at %s' %TK.obsEnd.round(2))
-                    break
 
             else:#sInd == None
                 sInd = old_sInd#Retain the last observed star
diff --git a/EXOSIMS/SurveySimulation/tieredScheduler_DD.py b/EXOSIMS/SurveySimulation/tieredScheduler_DD.py
index 3dd61122..e613e612 100644
--- a/EXOSIMS/SurveySimulation/tieredScheduler_DD.py
+++ b/EXOSIMS/SurveySimulation/tieredScheduler_DD.py
@@ -171,6 +171,9 @@ class tieredScheduler_DD(tieredScheduler):
                     DRM['slew_mass_used'] = slew_mass_used.to('kg')
                     Obs.scMass = Obs.scMass - slew_mass_used
                     DRM['scMass'] = Obs.scMass.to('kg')
+                    if Obs.twotanks:
+                        Obs.slewMass = Obs.slewMass - slew_mass_used
+                        DRM['slewMass'] = Obs.slewMass.to('kg')
 
                     self.logger.info('  Starshade and telescope aligned at target star')
                     self.vprint('  Starshade and telescope aligned at target star')
@@ -254,11 +257,6 @@ class tieredScheduler_DD(tieredScheduler):
                 if np.isinf(TK.OBduration) and (TK.missionPortion < 1):
                     self.arbitrary_time_advancement(TK.currentTimeNorm.to('day').copy() - DRM['arrival_time'])
 
-                # With occulter, if spacecraft fuel is depleted, exit loop
-                if Obs.scMass < Obs.dryMass:
-                    self.vprint('Total fuel mass exceeded at %s' %TK.obsEnd.round(2))
-                    break
-
             else:#sInd == None
                 sInd = old_sInd#Retain the last observed star
                 if(TK.currentTimeNorm.copy() >= TK.OBendTimes[TK.OBnumber]): # currentTime is at end of OB
diff --git a/EXOSIMS/SurveySimulation/tieredScheduler_DD_SLSQP.py b/EXOSIMS/SurveySimulation/tieredScheduler_DD_SLSQP.py
index f78d24c3..cc4ff9b3 100644
--- a/EXOSIMS/SurveySimulation/tieredScheduler_DD_SLSQP.py
+++ b/EXOSIMS/SurveySimulation/tieredScheduler_DD_SLSQP.py
@@ -171,6 +171,9 @@ class tieredScheduler_DD_SLSQP(tieredScheduler_SLSQP):
                     DRM['slew_mass_used'] = slew_mass_used.to('kg')
                     Obs.scMass = Obs.scMass - slew_mass_used
                     DRM['scMass'] = Obs.scMass.to('kg')
+                    if Obs.twotanks:
+                        Obs.slewMass = Obs.slewMass - slew_mass_used
+                        DRM['slewMass'] = Obs.slewMass.to('kg')
 
                     self.logger.info('  Starshade and telescope aligned at target star')
                     self.vprint('  Starshade and telescope aligned at target star')
@@ -253,11 +256,6 @@ class tieredScheduler_DD_SLSQP(tieredScheduler_SLSQP):
                 # to the next OB with timestep equivalent to time spent on one target
                 if np.isinf(TK.OBduration) and (TK.missionPortion < 1):
                     self.arbitrary_time_advancement(TK.currentTimeNorm.to('day').copy() - DRM['arrival_time'])
-                
-                # With occulter, if spacecraft fuel is depleted, exit loop
-                if Obs.scMass < Obs.dryMass:
-                    self.vprint('Total fuel mass exceeded at %s' %TK.obsEnd.round(2))
-                    break
 
             else:#sInd == None
                 sInd = old_sInd#Retain the last observed star
diff --git a/EXOSIMS/SurveySimulation/tieredScheduler_DD_sotoSS.py b/EXOSIMS/SurveySimulation/tieredScheduler_DD_sotoSS.py
index 49858117..7176ca09 100644
--- a/EXOSIMS/SurveySimulation/tieredScheduler_DD_sotoSS.py
+++ b/EXOSIMS/SurveySimulation/tieredScheduler_DD_sotoSS.py
@@ -164,6 +164,9 @@ class tieredScheduler_DD_sotoSS(tieredScheduler_sotoSS):
                     DRM['slew_mass_used'] = slew_mass_used.to('kg')
                     Obs.scMass = Obs.scMass - slew_mass_used
                     DRM['scMass'] = Obs.scMass.to('kg')
+                    if Obs.twotanks:
+                        Obs.slewMass = Obs.slewMass - slew_mass_used
+                        DRM['slewMass'] = Obs.slewMass.to('kg')
 
                     self.logger.info('  Starshade and telescope aligned at target star')
                     self.vprint('  Starshade and telescope aligned at target star')
@@ -242,11 +245,6 @@ class tieredScheduler_DD_sotoSS(tieredScheduler_sotoSS):
                 # to the next OB with timestep equivalent to time spent on one target
                 if np.isinf(TK.OBduration) and (TK.missionPortion < 1):
                     self.arbitrary_time_advancement(TK.currentTimeNorm.to('day').copy() - DRM['arrival_time'])
-                
-                # With occulter, if spacecraft fuel is depleted, exit loop
-                if Obs.scMass < Obs.dryMass:
-                    self.vprint('Total fuel mass exceeded at %s' %TK.obsEnd.round(2))
-                    break
 
             else:#sInd == None
                 sInd = old_sInd#Retain the last observed star
diff --git a/EXOSIMS/SurveySimulation/tieredScheduler_SLSQP.py b/EXOSIMS/SurveySimulation/tieredScheduler_SLSQP.py
index 8a06093c..500def7e 100644
--- a/EXOSIMS/SurveySimulation/tieredScheduler_SLSQP.py
+++ b/EXOSIMS/SurveySimulation/tieredScheduler_SLSQP.py
@@ -332,6 +332,9 @@ class tieredScheduler_SLSQP(SLSQPScheduler):
                     DRM['slew_mass_used'] = slew_mass_used.to('kg')
                     Obs.scMass = Obs.scMass - slew_mass_used
                     DRM['scMass'] = Obs.scMass.to('kg')
+                    if Obs.twotanks:
+                        Obs.slewMass = Obs.slewMass - slew_mass_used
+                        DRM['slewMass'] = Obs.slewMass.to('kg')
 
                     self.logger.info('  Starshade and telescope aligned at target star')
                     self.vprint('  Starshade and telescope aligned at target star')
@@ -412,11 +415,6 @@ class tieredScheduler_SLSQP(SLSQPScheduler):
                 # to the next OB with timestep equivalent to time spent on one target
                 if np.isinf(TK.OBduration) and (TK.missionPortion < 1):
                     self.arbitrary_time_advancement(TK.currentTimeNorm.to('day').copy() - DRM['arrival_time'])
-                
-                # With occulter, if spacecraft fuel is depleted, exit loop
-                if Obs.scMass < Obs.dryMass:
-                    self.vprint('Total fuel mass exceeded at %s' %TK.obsEnd.round(2))
-                    break
 
             else:#sInd == None
                 sInd = old_sInd#Retain the last observed star
diff --git a/EXOSIMS/SurveySimulation/tieredScheduler_sotoSS.py b/EXOSIMS/SurveySimulation/tieredScheduler_sotoSS.py
index e5af9a5a..71f4fd6e 100644
--- a/EXOSIMS/SurveySimulation/tieredScheduler_sotoSS.py
+++ b/EXOSIMS/SurveySimulation/tieredScheduler_sotoSS.py
@@ -274,6 +274,9 @@ class tieredScheduler_sotoSS(SurveySimulation):
                     DRM['slew_mass_used'] = slew_mass_used.to('kg')
                     Obs.scMass = Obs.scMass - slew_mass_used
                     DRM['scMass'] = Obs.scMass.to('kg')
+                    if Obs.twotanks:
+                        Obs.slewMass = Obs.slewMass - slew_mass_used
+                        DRM['slewMass'] = Obs.slewMass.to('kg')
 
                     self.logger.info('  Starshade and telescope aligned at target star')
                     self.vprint('  Starshade and telescope aligned at target star')
@@ -351,11 +354,6 @@ class tieredScheduler_sotoSS(SurveySimulation):
                 # to the next OB with timestep equivalent to time spent on one target
                 if np.isinf(TK.OBduration) and (TK.missionPortion < 1):
                     self.arbitrary_time_advancement(TK.currentTimeNorm.to('day').copy() - DRM['arrival_time'])
-                
-                # With occulter, if spacecraft fuel is depleted, exit loop
-                if Obs.scMass < Obs.dryMass:
-                    self.vprint('Total fuel mass exceeded at %s' %TK.obsEnd.round(2))
-                    break
 
             else:#sInd == None
                 sInd = old_sInd#Retain the last observed star


Test patch:
diff --git a/tests/Observatory/test_Observatory.py b/tests/Observatory/test_Observatory.py
index 31a1cc4b..3668441b 100644
--- a/tests/Observatory/test_Observatory.py
+++ b/tests/Observatory/test_Observatory.py
@@ -52,9 +52,9 @@ class TestObservatory(unittest.TestCase):
         """
 
         req_atts = ['koAngles_SolarPanel', 'ko_dtStep', 'settlingTime', 'thrust', 'slewIsp', 
-                    'scMass', 'dryMass', 'coMass', 'occulterSep', 'skIsp', 'defburnPortion', 
-                    'checkKeepoutEnd', 'forceStaticEphem', 'constTOF', 'occ_dtmin', 'occ_dtmax', 
-                    'maxdVpct', 'dVtot', 'dVmax', 'flowRate', 'havejplephem']
+                    'scMass', 'slewMass','skMass', 'twotanks','dryMass', 'coMass', 'occulterSep', 
+                    'skIsp', 'defburnPortion', 'checkKeepoutEnd', 'forceStaticEphem', 'constTOF',
+                    'occ_dtmin', 'occ_dtmax', 'maxdVpct', 'dVtot', 'dVmax', 'flowRate', 'havejplephem']
 
         for mod in self.allmods:
             with RedirectStreams(stdout=self.dev_null):
@@ -89,7 +89,7 @@ class TestObservatory(unittest.TestCase):
         Test that log_occulter_results returns proper dictionary with keys
         """
 
-        atts_list = ['slew_time', 'slew_angle', 'slew_dV', 'slew_mass_used', 'scMass']
+        atts_list = ['slew_time', 'slew_angle', 'slew_dV', 'slew_mass_used', 'scMass', 'slewMass']
         for mod in self.allmods:
             if 'log_occulterResults' in mod.__dict__:
                 with RedirectStreams(stdout=self.dev_null):
@@ -114,9 +114,9 @@ class TestObservatory(unittest.TestCase):
         """
 
         atts_list = ['koAngles_SolarPanel', 'ko_dtStep', 'settlingTime', 'thrust', 'slewIsp', 
-                    'scMass', 'dryMass', 'coMass', 'occulterSep', 'skIsp', 'defburnPortion', 
-                    'checkKeepoutEnd', 'forceStaticEphem', 'constTOF', 'occ_dtmin', 'occ_dtmax', 
-                    'maxdVpct', 'dVtot', 'dVmax', 'flowRate', 'havejplephem']
+                    'scMass', 'slewMass', 'skMass', 'twotanks', 'dryMass', 'coMass', 'occulterSep', 
+                    'skIsp', 'defburnPortion', 'checkKeepoutEnd', 'forceStaticEphem', 'constTOF', 
+                    'occ_dtmin', 'occ_dtmax', 'maxdVpct', 'dVtot', 'dVmax', 'flowRate', 'havejplephem']
 
         for mod in self.allmods:
             with RedirectStreams(stdout=self.dev_null):
diff --git a/tests/Prototypes/test_TimeKeeping.py b/tests/Prototypes/test_TimeKeeping.py
index 2d2f721c..9156a8bf 100644
--- a/tests/Prototypes/test_TimeKeeping.py
+++ b/tests/Prototypes/test_TimeKeeping.py
@@ -264,8 +264,25 @@ class TestTimeKeepingMethods(unittest.TestCase):
         tk.currentTimeNorm = tk.OBendTimes[tk.OBnumber] + 1*u.d
         tk.currentTimeAbs = tk.missionStart + tk.currentTimeNorm
         self.assertTrue(tk.mission_is_over(OS, Obs, det_mode))
-        tk.currentTimeAbs = 0*u.d
+        tk.currentTimeNorm = 0*u.d
         tk.currentTimeAbs = tk.missionStart
+        
+        # 5) Fuel Exceeded
+        OS.haveOcculter = True
+        tmpscMass = Obs.scMass.copy()
+        Obs.scMass = Obs.dryMass - 1*u.kg
+        self.assertTrue(tk.mission_is_over(OS, Obs, det_mode))
+        Obs.twotanks = True
+        Obs.slewMass = 1*u.kg
+        Obs.skMass = -1*u.kg
+        Obs.scMass = Obs.slewMass + Obs.skMass + Obs.dryMass
+        self.assertTrue(tk.mission_is_over(OS, Obs, det_mode))
+        Obs.slewMass = 1*u.kg
+        Obs.skMass = -1*u.kg
+        Obs.scMass = Obs.slewMass + Obs.skMass + Obs.dryMass
+        self.assertTrue(tk.mission_is_over(OS, Obs, det_mode))
+        Obs.twotanks = False
+        Obs.scMass = tmpscMass.copy()
 
     def test_advancetToStartOfNextOB(self):
         r""" Test advancetToStartOfNextOB method