Exemple #1
0
    def gas_used(self):
        """Calculate and return the quantity of gas used for this segment.

        (in liter)

        :returns: in liter, quantity of gas used
        :rtype: float
        """
        if self.setpoint > 0:
            return 0
        else:
            pressure = (depth_to_pressure(self.depth) +
                        settings.AMBIANT_PRESSURE_SURFACE)
            return (pressure * self.time *
                    float(settings.DECO_CONSUMPTION_RATE))
Exemple #2
0
    def get_end_for_given_depth(self, depth):
        """calculate end (equivalent narcotic depth)
        based on given depth and based on gaz inside the tank

        .. note::
            end calculation is based on narcotic index for all gases.

            By default, dipplanner considers that oxygen is narcotic
            (same narcotic index than nitrogen)

            All narcotic indexes can by changed in the config file,
            in the [advanced] section

        *Keyword arguments:*
            depth -- int -- in meter

        *Returns:*
            end -- int -- equivalent narcotic depth in meter

        *Raise:*
            <nothing>
        """
        p_absolute = depth_to_pressure(depth) + settings.AMBIANT_PRESSURE_SURFACE
        # calculate the reference narcotic effect of air
        # Air consists of: Nitrogen N2: 78.08%,
        #                  Oxygen O2: 20.95%,
        #                  Argon Ar: 0.934%
        reference_narcotic = settings.AMBIANT_PRESSURE_SURFACE * (
            settings.N2_NARCOTIC_VALUE * 0.7808
            + settings.O2_NARCOTIC_VALUE * 0.2095
            + settings.AR_NARCOTIC_VALUE * 0.00934
        )
        # OC mode
        narcotic_index = p_absolute * (
            self.f_n2 * settings.N2_NARCOTIC_VALUE
            + self.f_o2 * settings.O2_NARCOTIC_VALUE
            + self.f_he * settings.HE_NARCOTIC_VALUE
        )

        end = pressure_to_depth(narcotic_index / reference_narcotic - settings.AMBIANT_PRESSURE_SURFACE)
        if end < 0:
            end = 0
        return end
Exemple #3
0
    def get_mod_for_given_end(self, end):
        """calculate a mod based on given end and based on gaz inside the tank

        .. note::
            end calculation is based on narcotic index for all gases.

            By default, dipplanner considers that oxygen is narcotic
            (same narcotic index than nitrogen)

            All narcotic indexes can by changed in the config file,
            in the [advanced] section

        *Keyword arguments:*
            :end: (int) -- equivalent narcotic depth in meter

        *Returns:*
            int -- mod: depth in meter based on given end

        *Raise:*
            <nothing>
        """
        # calculate the reference narcotic effect of air
        # Air consists of: Nitrogen N2: 78.08%,
        #                  Oxygen O2: 20.95%,
        #                  Argon Ar: 0.934%
        # OC
        reference_narcotic = settings.AMBIANT_PRESSURE_SURFACE * (
            settings.N2_NARCOTIC_VALUE * 0.7808
            + settings.O2_NARCOTIC_VALUE * 0.2095
            + settings.AR_NARCOTIC_VALUE * 0.00934
        )
        # OC mode
        narcotic_tank = (
            self.f_n2 * settings.N2_NARCOTIC_VALUE
            + self.f_o2 * settings.O2_NARCOTIC_VALUE
            + self.f_he * settings.HE_NARCOTIC_VALUE
        )

        p_absolute = (depth_to_pressure(end) + settings.AMBIANT_PRESSURE_SURFACE) * reference_narcotic / narcotic_tank
        mod = pressure_to_depth(p_absolute - settings.AMBIANT_PRESSURE_SURFACE)
        return mod
Exemple #4
0
    def gas_used(self):
        """Calculate and return the quantity of gas used for this segment.

        (in liter)

        in this segment because it's ascend or descent,
        the gas is not used at the same rate during the segment
        Because the rate is the same during all the segment, we can use the
        consumption at the average depth of the segment

        :returns: in liter, quantity of gas used
        :rtype: float
        """
        if self.setpoint > 0:
            return 0
        else:
            average_depth = (float(self.start_depth) +
                             float(self.end_depth)) / 2.0
            pressure = (depth_to_pressure(average_depth) +
                        settings.AMBIANT_PRESSURE_SURFACE)
            return (pressure * self.time *
                    float(settings.DIVE_CONSUMPTION_RATE))
Exemple #5
0
    def get_mod_for_given_end(self, end):
        """Calculate a mod based on given end and based on gaz inside the tank.

        .. note::
            end calculation is based on narcotic index for all gases.

            By default, dipplanner considers that oxygen is narcotic
            (same narcotic index than nitrogen)

            All narcotic indexes can by changed in the config file,
            in the [advanced] section

        :param int end: equivalent narcotic depth in meter

        :returns: mod: depth in meter based on given end
        :rtype int:

        .. todo:: get and return float instead of int ?
        """
        # calculate the reference narcotic effect of air
        # Air consists of: Nitrogen N2: 78.08%,
        #                  Oxygen O2: 20.95%,
        #                  Argon Ar: 0.934%
        # OC
        reference_narcotic = settings.AMBIANT_PRESSURE_SURFACE * (
            settings.N2_NARCOTIC_VALUE * settings.DEFAULT_AIR_FN2 +
            settings.O2_NARCOTIC_VALUE * settings.DEFAULT_AIR_FO2 +
            settings.AR_NARCOTIC_VALUE * settings.DEFAULT_AIR_FAR)
        # OC mode
        narcotic_tank = (self.f_n2 * settings.N2_NARCOTIC_VALUE +
                         self.f_o2 * settings.O2_NARCOTIC_VALUE +
                         self.f_he * settings.HE_NARCOTIC_VALUE)

        p_absolute = (
            (depth_to_pressure(end) + settings.AMBIANT_PRESSURE_SURFACE) *
            reference_narcotic / narcotic_tank)
        mod = pressure_to_depth(p_absolute - settings.AMBIANT_PRESSURE_SURFACE)
        return mod
Exemple #6
0
    def get_mod_for_given_end(self, end):
        """Calculate a mod based on given end and based on gaz inside the tank.

        .. note::
            end calculation is based on narcotic index for all gases.

            By default, dipplanner considers that oxygen is narcotic
            (same narcotic index than nitrogen)

            All narcotic indexes can by changed in the config file,
            in the [advanced] section

        :param int end: equivalent narcotic depth in meter

        :returns: mod: depth in meter based on given end
        :rtype int:

        .. todo:: get and return float instead of int ?
        """
        # calculate the reference narcotic effect of air
        # Air consists of: Nitrogen N2: 78.08%,
        #                  Oxygen O2: 20.95%,
        #                  Argon Ar: 0.934%
        # OC
        reference_narcotic = settings.AMBIANT_PRESSURE_SURFACE * (
            settings.N2_NARCOTIC_VALUE * settings.DEFAULT_AIR_FN2 +
            settings.O2_NARCOTIC_VALUE * settings.DEFAULT_AIR_FO2 +
            settings.AR_NARCOTIC_VALUE * settings.DEFAULT_AIR_FAR)
        # OC mode
        narcotic_tank = (self.f_n2 * settings.N2_NARCOTIC_VALUE +
                         self.f_o2 * settings.O2_NARCOTIC_VALUE +
                         self.f_he * settings.HE_NARCOTIC_VALUE)

        p_absolute = ((depth_to_pressure(end) +
                       settings.AMBIANT_PRESSURE_SURFACE) *
                      reference_narcotic / narcotic_tank)
        mod = pressure_to_depth(p_absolute - settings.AMBIANT_PRESSURE_SURFACE)
        return mod
Exemple #7
0
    def get_p_absolute(self, method=settings.METHOD_FOR_DEPTH_CALCULATION):
        """Return the absolute pression in bar.

        (1atm = 1ATA = 1.01325 bar = 14.70psi)

        Simple method : 10m = +1 bar
        Complex method : use real density of water, T°, etc...

        :param str method: [OPTIONAL] 'simple' or 'complex'
                           if not given uses the defaults in settings.

        :returns: absolute pressure in bar
        :rtype: float

        :raises ValueError: when providing a bad method
        """
        if method == 'simple':
            return float(self.depth) / 10 + settings.AMBIANT_PRESSURE_SURFACE
        elif method == 'complex':
            return (depth_to_pressure(self.depth) +
                    settings.AMBIANT_PRESSURE_SURFACE)
        else:
            raise ValueError("invalid method of calculation")
Exemple #8
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(0), 0.0, 5,
                            'Wrong depth pressure at 0m : %s'
                            % depth_to_pressure(0))
Exemple #9
0
    def asc_desc(self, start, finish, rate, f_he, f_n2, pp_o2):
        """Ascend/Descend profile.

        Calls Compartment.asc_desc to update compartments

        :param float start: start pressure of this segment in bar
                            (WARNING: not meter ! it's a pressure)
        :param float finish: finish pressure of this segment in bar
                             (WARNING: not meter ! it's a pressure)
        :param float rate: rate of ascent or descent in m/s
        :param float f_he: Fraction of inert gas Helium in inspired gas mix
        :param float f_n2: Fraction of inert gas Nitrogen in inspired gas mix
        :param float pp_o2: for CCR mode, partial pressure of oxygen in bar.
                            if == 0.0, then: open circuit

        :raises ModelStateException:
        """
        # rem: here we do not bother of PP_H2O like in constant_depth : WHY ?
        start_ambiant_pressure = start + settings.AMBIANT_PRESSURE_SURFACE
        finish_ambiant_pressure = finish + settings.AMBIANT_PRESSURE_SURFACE
        # here we have seg_time in min and rate in m/min
        # rate should be in bar/min (or bar/s), not m/min nor m/sec
        rate = tools.depth_to_pressure(rate)
        seg_time = abs((finish - start) / rate)
        if pp_o2 > 0.0:
            # CCR mode
            # Calculate inert gas partial pressure == pAmb - pO2 - pH2O
            p_inert_start = (start_ambiant_pressure - pp_o2 - self.pp_h2o)
            p_inert_finish = (finish_ambiant_pressure - pp_o2 - self.pp_h2o)
            # Check that it doesn't go less than zero.
            # Could be due to shallow deco or starting on high setpoint
            if p_inert_start < 0.0:
                p_inert_start = 0.0
            if p_inert_finish < 0.0:
                p_inert_finish = 0.0
            # Separate into He and N2 components, checking that we are not
            # on pure O2 (or we get an arithmetic error)
            if f_he + f_n2 > 0.0:
                pp_he_inspired = (p_inert_start * f_he) / (f_he + f_n2)
                pp_n2_inspired = (p_inert_start * f_n2) / (f_he + f_n2)
                # calculate rate of change of each inert gas
                rate_he = ((p_inert_finish * f_he) / (f_he + f_n2) -
                           pp_he_inspired) / (seg_time)
                rate_n2 = ((p_inert_finish * f_n2) / (f_he + f_n2) -
                           pp_n2_inspired) / (seg_time)
            else:
                pp_he_inspired = 0.0
                pp_n2_inspired = 0.0
                rate_he = 0.0
                rate_n2 = 0.0
            # update ox_tox, constant pp_o2
            self.ox_tox.add_o2(seg_time, pp_o2)
        else:
            # OC mode
            # calculate He and N2 components
            pp_he_inspired = (start_ambiant_pressure - self.pp_h2o) * f_he
            pp_n2_inspired = (start_ambiant_pressure - self.pp_h2o) * f_n2
            rate_he = rate * f_he
            rate_n2 = rate * f_n2
            # update ox_tox, use average pp_o2
            pp_o2_inspired_avg = (
                (start_ambiant_pressure - finish_ambiant_pressure) / 2 +
                finish_ambiant_pressure - self.pp_h2o) * (1.0 - f_he - f_n2)
            self.ox_tox.add_o2(seg_time, pp_o2_inspired_avg)

        for comp in self.tissues:
            comp.asc_desc(pp_he_inspired, pp_n2_inspired,
                          rate_he, rate_n2, seg_time)
Exemple #10
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
Exemple #11
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(40), 4.04172, 5,
         'Wrong depth pressure at 40m : %s' % depth_to_pressure(40))
Exemple #12
0
 def test_depth_to_pressure_50(self):
     self.assertAlmostEqual(pressure_to_depth(5.05215), 50, 5,
                            'Wrong depth pressure at 50m : %s'
                            % depth_to_pressure(5.05215))
Exemple #13
0
 def test_depth_to_pressure_90(self):
     self.assertAlmostEqual(pressure_to_depth(9.09387), 90, 5,
                            'Wrong depth pressure at 90m : %s'
                            % depth_to_pressure(9.09387))
Exemple #14
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(70), 7.07301, 5,
                            'Wrong depth pressure at 70m : %s'
                            % depth_to_pressure(70))
Exemple #15
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(100), 10.1043, 4,
         'Wrong depth pressure at 100m : %s' % depth_to_pressure(100))
Exemple #16
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(90), 9.09387, 5,
         'Wrong depth pressure at 90m : %s' % depth_to_pressure(90))
Exemple #17
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(80), 8.08344, 5,
         'Wrong depth pressure at 80m : %s' % depth_to_pressure(80))
Exemple #18
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(70), 7.07301, 5,
         'Wrong depth pressure at 70m : %s' % depth_to_pressure(70))
Exemple #19
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(60), 6.06258, 5,
         'Wrong depth pressure at 60m : %s' % depth_to_pressure(60))
Exemple #20
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(50), 5.05215, 5,
         'Wrong depth pressure at 50m : %s' % depth_to_pressure(50))
Exemple #21
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(30), 3.03129, 5,
                            'Wrong depth pressure at 30m : %s'
                            % depth_to_pressure(30))
Exemple #22
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(50), 5.05215, 5,
                            'Wrong depth pressure at 50m : %s'
                            % depth_to_pressure(50))
Exemple #23
0
 def test_depth_to_pressure_90(self):
     self.assertAlmostEqual(
         pressure_to_depth(9.09387), 90, 5,
         'Wrong depth pressure at 90m : %s' % depth_to_pressure(9.09387))
Exemple #24
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(90), 9.09387, 5,
                            'Wrong depth pressure at 90m : %s'
                            % depth_to_pressure(90))
Exemple #25
0
 def test_depth_to_pressure_80(self):
     self.assertAlmostEqual(
         pressure_to_depth(8.08344), 80, 5,
         'Wrong depth pressure at 80m : %s' % depth_to_pressure(8.08344))
Exemple #26
0
 def test_depth_to_pressure_70(self):
     self.assertAlmostEqual(pressure_to_depth(7.07301), 70, 5,
                            'Wrong depth pressure at 70m : %s'
                            % depth_to_pressure(7.07301))
Exemple #27
0
 def test_depth_to_pressure_70(self):
     self.assertAlmostEqual(
         pressure_to_depth(7.07301), 70, 5,
         'Wrong depth pressure at 70m : %s' % depth_to_pressure(7.07301))
Exemple #28
0
 def test_depth_to_pressure_30(self):
     self.assertAlmostEqual(pressure_to_depth(3.03129), 30, 5,
                            'Wrong depth pressure at 30m : %s'
                            % depth_to_pressure(3.03129))
Exemple #29
0
 def test_depth_to_pressure_60(self):
     self.assertAlmostEqual(
         pressure_to_depth(6.06258), 60, 5,
         'Wrong depth pressure at 60m : %s' % depth_to_pressure(6.06258))
Exemple #30
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))
Exemple #31
0
 def test_depth_to_pressure_50(self):
     self.assertAlmostEqual(
         pressure_to_depth(5.05215), 50, 5,
         'Wrong depth pressure at 50m : %s' % depth_to_pressure(5.05215))
Exemple #32
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))
Exemple #33
0
 def test_depth_to_pressure_40(self):
     self.assertAlmostEqual(
         pressure_to_depth(4.04172), 40, 5,
         'Wrong depth pressure at 40m : %s' % depth_to_pressure(4.04172))
Exemple #34
0
    def asc_desc(self, start, finish, rate, f_he, f_n2, pp_o2):
        """Ascend/Descend profile.

        Calls Compartment.asc_desc to update compartments

        :param float start: start pressure of this segment in bar
                            (WARNING: not meter ! it's a pressure)
        :param float finish: finish pressure of this segment in bar
                             (WARNING: not meter ! it's a pressure)
        :param float rate: rate of ascent or descent in m/s
        :param float f_he: Fraction of inert gas Helium in inspired gas mix
        :param float f_n2: Fraction of inert gas Nitrogen in inspired gas mix
        :param float pp_o2: for CCR mode, partial pressure of oxygen in bar.
                            if == 0.0, then: open circuit

        :raises ModelStateException:
        """
        # rem: here we do not bother of PP_H2O like in constant_depth : WHY ?
        start_ambiant_pressure = start + settings.AMBIANT_PRESSURE_SURFACE
        finish_ambiant_pressure = finish + settings.AMBIANT_PRESSURE_SURFACE
        # here we have seg_time in min and rate in m/min
        # rate should be in bar/min (or bar/s), not m/min nor m/sec
        rate = tools.depth_to_pressure(rate)
        seg_time = abs((finish - start) / rate)
        if pp_o2 > 0.0:
            # CCR mode
            # Calculate inert gas partial pressure == pAmb - pO2 - pH2O
            p_inert_start = (start_ambiant_pressure - pp_o2 - self.pp_h2o)
            p_inert_finish = (finish_ambiant_pressure - pp_o2 - self.pp_h2o)
            # Check that it doesn't go less than zero.
            # Could be due to shallow deco or starting on high setpoint
            if p_inert_start < 0.0:
                p_inert_start = 0.0
            if p_inert_finish < 0.0:
                p_inert_finish = 0.0
            # Separate into He and N2 components, checking that we are not
            # on pure O2 (or we get an arithmetic error)
            if f_he + f_n2 > 0.0:
                pp_he_inspired = (p_inert_start * f_he) / (f_he + f_n2)
                pp_n2_inspired = (p_inert_start * f_n2) / (f_he + f_n2)
                # calculate rate of change of each inert gas
                rate_he = ((p_inert_finish * f_he) /
                           (f_he + f_n2) - pp_he_inspired) / (seg_time)
                rate_n2 = ((p_inert_finish * f_n2) /
                           (f_he + f_n2) - pp_n2_inspired) / (seg_time)
            else:
                pp_he_inspired = 0.0
                pp_n2_inspired = 0.0
                rate_he = 0.0
                rate_n2 = 0.0
            # update ox_tox, constant pp_o2
            self.ox_tox.add_o2(seg_time, pp_o2)
        else:
            # OC mode
            # calculate He and N2 components
            pp_he_inspired = (start_ambiant_pressure - self.pp_h2o) * f_he
            pp_n2_inspired = (start_ambiant_pressure - self.pp_h2o) * f_n2
            rate_he = rate * f_he
            rate_n2 = rate * f_n2
            # update ox_tox, use average pp_o2
            pp_o2_inspired_avg = (
                (start_ambiant_pressure - finish_ambiant_pressure) / 2 +
                finish_ambiant_pressure - self.pp_h2o) * (1.0 - f_he - f_n2)
            self.ox_tox.add_o2(seg_time, pp_o2_inspired_avg)

        for comp in self.tissues:
            comp.asc_desc(pp_he_inspired, pp_n2_inspired, rate_he, rate_n2,
                          seg_time)
Exemple #35
0
 def test_depth_to_pressure_30(self):
     self.assertAlmostEqual(
         pressure_to_depth(3.03129), 30, 5,
         'Wrong depth pressure at 30m : %s' % depth_to_pressure(3.03129))
Exemple #36
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(20), 2.02086, 5,
                            'Wrong depth pressure at 20m : %s'
                            % depth_to_pressure(20))
Exemple #37
0
 def test_depth_to_pressure_20(self):
     self.assertAlmostEqual(
         pressure_to_depth(2.02086), 20, 5,
         'Wrong depth pressure at 20m : %s' % depth_to_pressure(2.02086))
Exemple #38
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(40), 4.04172, 5,
                            'Wrong depth pressure at 40m : %s'
                            % depth_to_pressure(40))
Exemple #39
0
 def test_depth_to_pressure_10(self):
     self.assertAlmostEqual(
         pressure_to_depth(1.01043), 10, 5,
         'Wrong depth pressure at 10m : %s' % depth_to_pressure(1.01043))
Exemple #40
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(60), 6.06258, 5,
                            'Wrong depth pressure at 60m : %s'
                            % depth_to_pressure(60))
Exemple #41
0
 def test_depth_to_pressure_0(self):
     self.assertAlmostEqual(
         pressure_to_depth(0.0), 0, 0,
         'Wrong depth pressure at 0m : %s' % depth_to_pressure(0.0))
Exemple #42
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(80), 8.08344, 5,
                            'Wrong depth pressure at 80m : %s'
                            % depth_to_pressure(80))
Exemple #43
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(20), 2.02086, 5,
         'Wrong depth pressure at 20m : %s' % depth_to_pressure(20))
Exemple #44
0
 def runTest(self):
     self.assertAlmostEqual(depth_to_pressure(100), 10.1043, 4,
                            'Wrong depth pressure at 100m : %s'
                            % depth_to_pressure(100))
Exemple #45
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(0), 0.0, 5,
         'Wrong depth pressure at 0m : %s' % depth_to_pressure(0))
Exemple #46
0
 def test_depth_to_pressure_80(self):
     self.assertAlmostEqual(pressure_to_depth(8.08344), 80, 5,
                            'Wrong depth pressure at 80m : %s'
                            % depth_to_pressure(8.08344))
Exemple #47
0
 def test_depth_to_pressure_10(self):
     self.assertAlmostEqual(pressure_to_depth(1.01043), 10, 5,
                            'Wrong depth pressure at 10m : %s'
                            % depth_to_pressure(1.01043))
Exemple #48
0
 def test_depth_to_pressure_60(self):
     self.assertAlmostEqual(pressure_to_depth(6.06258), 60, 5,
                            'Wrong depth pressure at 60m : %s'
                            % depth_to_pressure(6.06258))
Exemple #49
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
Exemple #50
0
 def test_depth_to_pressure_40(self):
     self.assertAlmostEqual(pressure_to_depth(4.04172), 40, 5,
                            'Wrong depth pressure at 40m : %s'
                            % depth_to_pressure(4.04172))
Exemple #51
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()

        *Keyword Arguments:*
            :target_depth: (float) -- in meter, target depth for the ascend

        Returns:
        <nothing>

        Raise:
        <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
            # TODO : int() or round() ?
            next_stop_depth = int(float(self.current_depth) /
                                  float(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
        elif next_stop_depth == settings.LAST_STOP_DEPTH:
            self.logger.warning("next_stop_depth==LAST_STOP_DEPTH !")
            next_stop_depth = target_depth  # TODO: bizarre...
        elif next_stop_depth < settings.LAST_STOP_DEPTH:
            next_stop_depth = settings.LAST_STOP_DEPTH

        start_depth = self.current_depth  # Initialise ascent
                                          # segment start 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 -- debug : %s, %s" %
                              (self.current_depth, target_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
                force_deco_stop = False  # Only used for first entry
                                         # into deco stop
                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
                    # TODO: start depth is not re-initialised after first use

                # 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.logger.debug("...set m-value gradient")
                    self.model.gradient.set_gf_slope_at_depth(
                        self.current_depth)
                    self.model.gradient.set_gf_at_depth(next_stop_depth)

                #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
                    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 : %ss" % 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
            elif in_ascent_cycle:
                #self.logger.debug("...in ascent cycle")
                # did not decompress, just ascend
                # TODO : if we enable this code always (not in elif,
                #        but direct) then
                #        model will ascend between deco stops, but ...
                #        this causes collateral damage to runtim calculations
                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)
                # TODO: Issue here is that this ascent time is not accounted
                #       for in any segments unless it was in an ascent cycle

            #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: %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
            # !!! BEGIN FORCE COMMENT (SEE BELOW)
            #elif next_stop_depth < settings.LAST_STOP_DEPTH:
            #  self.logger.debug("next_stop_depth (%s) < "
            #                    "settings.LAST_STOP_DEPTH (%s)" %
            #                    (next_stop_depth, settings.LAST_STOP_DEPTH))
            #  next_stop_depth = settings.LAST_STOP_DEPTH
            # !!! END FORCE COMMENT
            #TODO: j'ai commenté les lignes ci-dessus pour éviter
            #      une boucle infinie commprendre pourquoi elles existent...

            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))
Exemple #52
0
 def test_depth_to_pressure_20(self):
     self.assertAlmostEqual(pressure_to_depth(2.02086), 20, 5,
                            'Wrong depth pressure at 20m : %s'
                            % depth_to_pressure(2.02086))
Exemple #53
0
 def runTest(self):
     self.assertAlmostEqual(
         depth_to_pressure(30), 3.03129, 5,
         'Wrong depth pressure at 30m : %s' % depth_to_pressure(30))
Exemple #54
0
 def test_depth_to_pressure_0(self):
     self.assertAlmostEqual(pressure_to_depth(0.0), 0, 0,
                            'Wrong depth pressure at 0m : %s'
                            % depth_to_pressure(0.0))
Exemple #55
0
    def get_end_for_given_depth(self, depth, setpoint=0.0):
        """Calculate end (equivalent narcotic depth).

        based on given depth and based on gaz inside the tank

        .. note::
            end calculation is based on narcotic index for all gases.

            By default, dipplanner considers that oxygen is narcotic
            (same narcotic index than nitrogen)

            All narcotic indexes can by changed in the config file,
            in the [advanced] section

        Instead of Mvplan, dipplanner uses a 'calculation method' based on
        narcosis effet of all gas used, assuming there is no trace of
        other gases (like argon) in the breathing gas, but compare the narcotic
        effect with surface gas, wich is 'air' and contains
        a small amount of argon

        :param int depth: in meter
        :param float setpoint: ppo2 in bar. If 0: OC mode, if > 0.0, CCR mode.

        :returns: end -- equivalent narcotic depth in meter
        :rtype: float
        """
        p_absolute = (depth_to_pressure(depth) +
                      settings.AMBIANT_PRESSURE_SURFACE)
        # calculate the reference narcotic effect of air
        # Air consists of: Nitrogen N2: 78.08%,
        #                  Oxygen O2: 20.95%,
        #                  Argon Ar: 0.934%
        reference_narcotic = settings.AMBIANT_PRESSURE_SURFACE * (
            settings.N2_NARCOTIC_VALUE * settings.DEFAULT_AIR_FN2 +
            settings.O2_NARCOTIC_VALUE * settings.DEFAULT_AIR_FO2 +
            settings.AR_NARCOTIC_VALUE * settings.DEFAULT_AIR_FAR)

        if setpoint > 0:
            # CCR mode
            f_inert = self.f_he + self.f_n2
            if f_inert > 0:
                pp_inert = p_absolute - setpoint
            else:
                pp_inert = 0
            if pp_inert > 0:
                # sort of approximation here ?
                # calculate here a narcotic index based on the proportion
                # of innertgases in the dilluent tank
                # should (perhaps) calculate based on proportion
                # of innert gases inthe loop ?
                ppn2_inspired = (pp_inert * self.f_n2) / f_inert
                pphe_inspired = (pp_inert * self.f_he) / f_inert
                narcotic_index = (ppn2_inspired * settings.N2_NARCOTIC_VALUE +
                                  setpoint * settings.O2_NARCOTIC_VALUE +
                                  pphe_inspired * settings.HE_NARCOTIC_VALUE)

                self.logger.debug(
                    "pabs: %.3f, pp_inert: %.3f at %sm, "
                    "ppn2i:%s, pphei:%s, narco idx:%s", p_absolute, pp_inert,
                    depth, ppn2_inspired, pphe_inspired, narcotic_index)
            else:
                narcotic_index = 0
        else:
            # OC mode
            narcotic_index = p_absolute * (
                self.f_n2 * settings.N2_NARCOTIC_VALUE +
                self.f_o2 * settings.O2_NARCOTIC_VALUE +
                self.f_he * settings.HE_NARCOTIC_VALUE)

        end = pressure_to_depth(narcotic_index / reference_narcotic -
                                settings.AMBIANT_PRESSURE_SURFACE)
        if end < 0:
            end = 0
        return end