def runTest(self): try: baddiveseg = SegmentAscDesc(3, 150, 10, self.trimixtank1, 0) baddiveseg.check() except UnauthorizedMod: pass else: self.fail("should raise UnauthorizedMod")
def test_wrong_mod_4(self): """test wrong mod 4.""" try: baddiveseg = SegmentAscDesc(3, 150, 10, self.trimixtank1, 0) baddiveseg.check() except UnauthorizedMod: pass else: self.fail("should raise UnauthorizedMod")
def ascend(self, target_depth): """Ascend to target depth, decompressing if necessary. If inFinalAscent then gradient factors start changing, and automatic gas selection is made. This method is called by do_dive() :param float target_depth: in meter, target depth for the ascend :raises ModelException: <Exceptions from model> """ force_deco_stop = False in_deco_cycle = False deco_stop_time = 0 if self.in_final_ascent and settings.USE_OC_DECO: self.set_deco_gas(self.current_depth) if self.current_depth < target_depth: # going backwards ! raise ProcessingError("Not allowed to ascend while descending !") # Set initial stop to be the next integral stop depth if self.current_depth % settings.STOP_DEPTH_INCREMENT > 0: # we are not on a stop depth already : go to the next stop depth next_stop_depth = ( int(self.current_depth / settings.STOP_DEPTH_INCREMENT) * settings.STOP_DEPTH_INCREMENT) else: next_stop_depth = int(self.current_depth - settings.STOP_DEPTH_INCREMENT) self.logger.debug("next_stop_depth: %s", next_stop_depth) # hack in case we are overshooting or hit last stop or any of # the other bizzar combinations ... if (next_stop_depth < target_depth or self.current_depth < settings.LAST_STOP_DEPTH): next_stop_depth = target_depth self.logger.debug("new next_stop_depth: %s", next_stop_depth) elif next_stop_depth == settings.LAST_STOP_DEPTH: self.logger.warning("next_stop_depth==LAST_STOP_DEPTH !") next_stop_depth = target_depth self.logger.debug("new next_stop_depth: %s", next_stop_depth) elif next_stop_depth < settings.LAST_STOP_DEPTH: next_stop_depth = settings.LAST_STOP_DEPTH self.logger.debug("new next_stop_depth: %s", next_stop_depth) # Initialise ascent segment start depth start_depth = self.current_depth in_ascent_cycle = True # Start in free ascent # Initialise gradient factor for next (in this case first) stop depth self.model.gradient.set_gf_at_depth(next_stop_depth) # Remember maxM-Value and controlling compartment max_mv = self.model.m_value(depth_to_pressure(self.current_depth)) control = self.model.control_compartment() while self.current_depth > target_depth: self.logger.debug("ascent from: %s, to: %s", self.current_depth, next_stop_depth) # can we move to the proposed next stop depth ? model_ceiling = self.model.ceiling() self.logger.debug("model ceiling: %s", model_ceiling) while force_deco_stop or next_stop_depth < model_ceiling: in_deco_cycle = True # Only used for first entry into deco stop force_deco_stop = False if in_ascent_cycle: # Finalise last ascent cycle as we are now decomp if start_depth > self.current_depth: # add ascent segment self.logger.debug( "Add AscDesc(1): start_depth:%s " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, settings.ASCENT_RATE, self.current_tank, self.pp_o2)) in_ascent_cycle = False # set m-value gradient under the following conditions: # - if not in multilevel mode, then set it as soon as # we do a decompression cycle # - otherwise wait until we are finally # surfacing before setting it if ((not settings.MULTILEVEL_MODE or self.in_final_ascent) and (not self.model.gradient.gf_set)): self.model.gradient.set_gf_slope_at_depth( self.current_depth) self.model.gradient.set_gf_at_depth(next_stop_depth) self.logger.debug("...set m-value gradient: %s", self.model.gradient.gf) # calculate stop_time # if (deco_stop_time == 0 and # self.run_time % settings.STOP_TIME_INCREMENT > 0): # stop_time = ( # int(self.run_time / settings.STOP_TIME_INCREMENT) * # settings.STOP_TIME_INCREMENT + # settings.STOP_TIME_INCREMENT - self.run_time) # print("+++++ ", stop_time) # if stop_time == 0: # stop_time = settings.STOP_TIME_INCREMENT # in second # else: stop_time = settings.STOP_TIME_INCREMENT # in second # execute the stop self.model.const_depth(depth_to_pressure(self.current_depth), stop_time, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) deco_stop_time += stop_time # sanity check for infinite loop if deco_stop_time > 300000: raise InfiniteDeco("Infinite deco error") model_ceiling = self.model.ceiling() # finished decompression loop if in_deco_cycle: self.logger.debug("...in deco cycle") # finalise the last deco cycle self.run_time += deco_stop_time self.logger.debug( "update run time with deco time: %ss at %sm (runtime:%s)", deco_stop_time, self.current_depth, self.run_time) if settings.FORCE_ALL_STOPS: force_deco_stop = True # write deco segment deco_segment = SegmentDeco(self.current_depth, deco_stop_time, self.current_tank, self.pp_o2) deco_segment.mv_max = max_mv deco_segment.gf_used = self.model.gradient.gf deco_segment.control_compartment = control self.output_segments.append(deco_segment) in_deco_cycle = False deco_stop_time = 0 if in_ascent_cycle: self.logger.debug("...in ascent cycle, do asc from %s to %s", self.current_depth, next_stop_depth) self.model.asc_desc(depth_to_pressure(self.current_depth), depth_to_pressure(next_stop_depth), -settings.ASCENT_RATE, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.run_time += abs( (float(self.current_depth) - float(next_stop_depth)) / float(settings.ASCENT_RATE)) self.logger.debug("update run time : %ss", self.run_time) else: self.logger.debug("...in deco cycle, do asc from %s to %s", self.current_depth, next_stop_depth) self.model.asc_desc(depth_to_pressure(self.current_depth), depth_to_pressure(next_stop_depth), -settings.DECO_ASCENT_RATE, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.run_time += abs( (float(self.current_depth) - float(next_stop_depth)) / float(settings.DECO_ASCENT_RATE)) self.logger.debug("update run time : %ss", self.run_time) self.output_segments.append( SegmentAscDesc(self.current_depth, next_stop_depth, settings.DECO_ASCENT_RATE, self.current_tank, self.pp_o2)) # now we moved up the the next depth self.current_depth = next_stop_depth max_mv = self.model.m_value(depth_to_pressure(self.current_depth)) control = self.model.control_compartment() # Check and switch deco gas temp_tank = self.current_tank # remember in case we switch if self.set_deco_gas(self.current_depth): # True if we changed gas if in_ascent_cycle: self.logger.debug( "Add AscDesc(2): start_depth:%s, " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, settings.ASCENT_RATE, temp_tank, self.pp_o2)) start_depth = self.current_depth # set next rounded stop depth next_stop_depth = int( self.current_depth) - settings.STOP_DEPTH_INCREMENT self.logger.debug("next stop depth: %s, target depth: %s", next_stop_depth, target_depth) # check in cas we are overshooting or hit last stop if (next_stop_depth < target_depth or self.current_depth < settings.LAST_STOP_DEPTH): self.logger.debug("next_stop_depth (%s) < target_depth (%s)", next_stop_depth, target_depth) next_stop_depth = target_depth elif self.current_depth < settings.LAST_STOP_DEPTH: self.logger.debug("current_depth (%s) < LAST_STOP_DEPTH (%s)", self.current_depth, settings.LAST_STOP_DEPTH) next_stop_depth = target_depth elif (next_stop_depth < settings.LAST_STOP_DEPTH and next_stop_depth > 0): self.logger.debug( "next_stop_depth (%s) < " "settings.LAST_STOP_DEPTH (%s)", next_stop_depth, settings.LAST_STOP_DEPTH) next_stop_depth = target_depth if self.model.gradient.gf_set: # update gf for next stop self.model.gradient.set_gf_at_depth(next_stop_depth) # are we still in ascent segment ? if in_ascent_cycle: self.logger.debug( "Add AscDesc(3): start_depth:%s, " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, -settings.ASCENT_RATE, self.current_tank, self.pp_o2))
def do_dive(self): """Process the dive. :raises NothingToProcess: if there is no input segment to process :raises ModelException: <Exceptions from model> """ if self.is_dive_segments() is False: raise NothingToProcess # check the segments: for seg in self.input_segments: seg.check() run_time_flag = settings.RUN_TIME # sets initial state # # else: first_segment = self.input_segments[0] self.current_tank = first_segment.tank # Sort self.tanks based on MOD ? why ? see below ? self.tanks.sort() self.current_depth = 0.0 self.pp_o2 = first_segment.setpoint if self.pp_o2 == 0.0: self.is_closed_circuit = False else: self.is_closed_circuit = True self.in_final_ascent = False # check if tank for 1rst segment is suitable for descent (OC mode) if (not self.is_closed_circuit and self.input_segments[0].tank.get_min_od() > 0): # tank is not ok, we need to look for another better tank # at first, try to find a tank suitable # from 0m to depth of first segment self.logger.debug("bottom gaz not ok for descent") self.tanks.reverse() for tank in self.tanks: if tank.get_min_od() == 0: self.logger.debug( "This tank may be suitable:%s, mod:%s, end at d:%s", str(tank), tank.mod, tank.get_end_for_given_depth( self.input_segments[0].depth)) if (tank.mod >= self.input_segments[0].depth and tank.get_end_for_given_depth( self.input_segments[0].depth) < settings.DEFAULT_MAX_END): # ok we have a winner self.logger.info("Changed tank for descent to:%s", str(tank)) self.current_tank = tank break if self.current_tank == self.input_segments[0].tank: # not found : we need to stop in the descent # to switch from first gas # to bottom gas self.logger.debug("No directly usage tank found," " try to stop and change tank") for tank in self.tanks: if tank.get_min_od() == 0: self.logger.debug( "This tank may be suitable:%s, " "mod:%s, end at d:%s", str(tank), tank.mod, tank.get_end_for_given_depth( self.input_segments[0].depth)) if settings.TRAVEL_SWITCH == 'late': depth = min( tank.mod, tank.get_mod_for_given_end( settings.DEFAULT_MAX_END)) self.input_segments.insert( 0, SegmentDive(depth=depth, tank=self.input_segments[0].tank, time=0)) self.input_segments.insert( 0, SegmentDive(depth=depth, tank=tank, time=0)) self.current_tank = tank break else: # early depth = self.input_segments[0].tank.get_min_od( min_ppo2=settings.DEFAULT_MIN_PPO2) self.input_segments.insert( 0, SegmentDive(depth=depth, tank=self.input_segments[0].tank, time=0)) self.input_segments.insert( 0, SegmentDive(depth=depth, tank=tank, time=0)) self.current_tank = tank break self.tanks.sort() for seg in self.input_segments: if seg.type == 'const': # only dive segment allowed for input delta_depth = float(seg.depth) - float(self.current_depth) # Ascend or descend to dive segment, # using existing gas and ppO2 settings if delta_depth > 0.0: # descent self.model.asc_desc(depth_to_pressure(self.current_depth), depth_to_pressure(seg.depth), settings.DESCENT_RATE, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.output_segments.append( SegmentAscDesc(self.current_depth, seg.depth, settings.DESCENT_RATE, self.current_tank, self.pp_o2)) self.run_time += abs( float(delta_depth) / (float(settings.DESCENT_RATE))) self.logger.debug( "descent time : %ss", float(delta_depth) / settings.DESCENT_RATE) else: # ascent # call ascend method of this class # for decompression calculation self.ascend(seg.depth) # we are now at the desired depth : process the dive segment self.current_depth = seg.depth # new depth self.pp_o2 = seg.setpoint self.current_tank = seg.tank if seg.time > 0: # only do this if it's not a waypoint if run_time_flag: run_time_flag = False # do this one only self.model.const_depth(depth_to_pressure(seg.depth), seg.time - self.run_time, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.output_segments.append( SegmentDive(seg.depth, seg.time - self.run_time, self.current_tank, self.pp_o2)) self.metadata += "Dive to %s for %ss\n" % ( seg.depth, seg.time - self.run_time) self.logger.debug("Dive to %s for %ss", seg.depth, seg.time - self.run_time) # run_time = seg_time because it's # only done the first time self.run_time = seg.time self.logger.debug("update run time : %ss", self.run_time) else: self.model.const_depth(depth_to_pressure(seg.depth), seg.time, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.output_segments.append( SegmentDive(seg.depth, seg.time, self.current_tank, self.pp_o2)) self.metadata += "Dive to %s for %ss\n" % (seg.depth, seg.time) self.logger.debug("Dive to %s for %ss", seg.depth, seg.time) self.run_time += seg.time self.logger.debug("update run time : %ss", self.run_time) else: # process waypoint self.output_segments.append( SegmentDive(seg.depth, seg.time, self.current_tank, self.pp_o2)) # all input segment are now processed: process to ascend to the surface self.in_final_ascent = True # ascend to the surface self.ascend(0.0) # for each output segment, recalculate runtime and update segments total_time = 0 for output_seg in self.output_segments: total_time += output_seg.time output_seg.run_time = total_time if total_time != self.run_time: self.logger.warning( "dive run_time (%ss) differs from" " all segments time (%ss)", self.run_time, total_time) # write metadata into the model self.model.metadata = self.metadata # recalculate the gas consumptions self.do_gas_calcs() # save the tanks parameters : next dives may use the same tanks, # but we need here to duplicate tank object within this dive in # order to save the tank parameters for this dive only saved_tanks = [] for tank in self.tanks: saved_tanks.append(copy.deepcopy(tank)) self.tanks = saved_tanks