Example #1
0
 def runTest(self):
     try:
         baddiveseg = SegmentAscDesc(3, 150, 10, self.trimixtank1, 0)
         baddiveseg.check()
     except UnauthorizedMod:
         pass
     else:
         self.fail("should raise UnauthorizedMod")
Example #2
0
 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")
Example #3
0
    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))
Example #4
0
    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