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))
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
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
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))
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
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
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")
def runTest(self): self.assertAlmostEqual(depth_to_pressure(0), 0.0, 5, 'Wrong depth pressure at 0m : %s' % depth_to_pressure(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)
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
def runTest(self): self.assertAlmostEqual( depth_to_pressure(40), 4.04172, 5, 'Wrong depth pressure at 40m : %s' % depth_to_pressure(40))
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))
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))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(70), 7.07301, 5, 'Wrong depth pressure at 70m : %s' % depth_to_pressure(70))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(100), 10.1043, 4, 'Wrong depth pressure at 100m : %s' % depth_to_pressure(100))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(90), 9.09387, 5, 'Wrong depth pressure at 90m : %s' % depth_to_pressure(90))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(80), 8.08344, 5, 'Wrong depth pressure at 80m : %s' % depth_to_pressure(80))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(70), 7.07301, 5, 'Wrong depth pressure at 70m : %s' % depth_to_pressure(70))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(60), 6.06258, 5, 'Wrong depth pressure at 60m : %s' % depth_to_pressure(60))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(50), 5.05215, 5, 'Wrong depth pressure at 50m : %s' % depth_to_pressure(50))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(30), 3.03129, 5, 'Wrong depth pressure at 30m : %s' % depth_to_pressure(30))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(50), 5.05215, 5, 'Wrong depth pressure at 50m : %s' % depth_to_pressure(50))
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))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(90), 9.09387, 5, 'Wrong depth pressure at 90m : %s' % depth_to_pressure(90))
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))
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))
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))
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))
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))
def ascend(self, target_depth): """Ascend to target depth, decompressing if necessary. If inFinalAscent then gradient factors start changing, and automatic gas selection is made. This method is called by do_dive() :param float target_depth: in meter, target depth for the ascend :raises ModelException: <Exceptions from model> """ force_deco_stop = False in_deco_cycle = False deco_stop_time = 0 if self.in_final_ascent and settings.USE_OC_DECO: self.set_deco_gas(self.current_depth) if self.current_depth < target_depth: # going backwards ! raise ProcessingError("Not allowed to ascend while descending !") # Set initial stop to be the next integral stop depth if self.current_depth % settings.STOP_DEPTH_INCREMENT > 0: # we are not on a stop depth already : go to the next stop depth next_stop_depth = ( int(self.current_depth / settings.STOP_DEPTH_INCREMENT) * settings.STOP_DEPTH_INCREMENT) else: next_stop_depth = int(self.current_depth - settings.STOP_DEPTH_INCREMENT) self.logger.debug("next_stop_depth: %s", next_stop_depth) # hack in case we are overshooting or hit last stop or any of # the other bizzar combinations ... if (next_stop_depth < target_depth or self.current_depth < settings.LAST_STOP_DEPTH): next_stop_depth = target_depth self.logger.debug("new next_stop_depth: %s", next_stop_depth) elif next_stop_depth == settings.LAST_STOP_DEPTH: self.logger.warning("next_stop_depth==LAST_STOP_DEPTH !") next_stop_depth = target_depth self.logger.debug("new next_stop_depth: %s", next_stop_depth) elif next_stop_depth < settings.LAST_STOP_DEPTH: next_stop_depth = settings.LAST_STOP_DEPTH self.logger.debug("new next_stop_depth: %s", next_stop_depth) # Initialise ascent segment start depth start_depth = self.current_depth in_ascent_cycle = True # Start in free ascent # Initialise gradient factor for next (in this case first) stop depth self.model.gradient.set_gf_at_depth(next_stop_depth) # Remember maxM-Value and controlling compartment max_mv = self.model.m_value(depth_to_pressure(self.current_depth)) control = self.model.control_compartment() while self.current_depth > target_depth: self.logger.debug("ascent from: %s, to: %s", self.current_depth, next_stop_depth) # can we move to the proposed next stop depth ? model_ceiling = self.model.ceiling() self.logger.debug("model ceiling: %s", model_ceiling) while force_deco_stop or next_stop_depth < model_ceiling: in_deco_cycle = True # Only used for first entry into deco stop force_deco_stop = False if in_ascent_cycle: # Finalise last ascent cycle as we are now decomp if start_depth > self.current_depth: # add ascent segment self.logger.debug( "Add AscDesc(1): start_depth:%s " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, settings.ASCENT_RATE, self.current_tank, self.pp_o2)) in_ascent_cycle = False # set m-value gradient under the following conditions: # - if not in multilevel mode, then set it as soon as # we do a decompression cycle # - otherwise wait until we are finally # surfacing before setting it if ((not settings.MULTILEVEL_MODE or self.in_final_ascent) and (not self.model.gradient.gf_set)): self.model.gradient.set_gf_slope_at_depth( self.current_depth) self.model.gradient.set_gf_at_depth(next_stop_depth) self.logger.debug("...set m-value gradient: %s", self.model.gradient.gf) # calculate stop_time # if (deco_stop_time == 0 and # self.run_time % settings.STOP_TIME_INCREMENT > 0): # stop_time = ( # int(self.run_time / settings.STOP_TIME_INCREMENT) * # settings.STOP_TIME_INCREMENT + # settings.STOP_TIME_INCREMENT - self.run_time) # print("+++++ ", stop_time) # if stop_time == 0: # stop_time = settings.STOP_TIME_INCREMENT # in second # else: stop_time = settings.STOP_TIME_INCREMENT # in second # execute the stop self.model.const_depth(depth_to_pressure(self.current_depth), stop_time, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) deco_stop_time += stop_time # sanity check for infinite loop if deco_stop_time > 300000: raise InfiniteDeco("Infinite deco error") model_ceiling = self.model.ceiling() # finished decompression loop if in_deco_cycle: self.logger.debug("...in deco cycle") # finalise the last deco cycle self.run_time += deco_stop_time self.logger.debug( "update run time with deco time: %ss at %sm (runtime:%s)", deco_stop_time, self.current_depth, self.run_time) if settings.FORCE_ALL_STOPS: force_deco_stop = True # write deco segment deco_segment = SegmentDeco(self.current_depth, deco_stop_time, self.current_tank, self.pp_o2) deco_segment.mv_max = max_mv deco_segment.gf_used = self.model.gradient.gf deco_segment.control_compartment = control self.output_segments.append(deco_segment) in_deco_cycle = False deco_stop_time = 0 if in_ascent_cycle: self.logger.debug("...in ascent cycle, do asc from %s to %s", self.current_depth, next_stop_depth) self.model.asc_desc(depth_to_pressure(self.current_depth), depth_to_pressure(next_stop_depth), -settings.ASCENT_RATE, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.run_time += abs( (float(self.current_depth) - float(next_stop_depth)) / float(settings.ASCENT_RATE)) self.logger.debug("update run time : %ss", self.run_time) else: self.logger.debug("...in deco cycle, do asc from %s to %s", self.current_depth, next_stop_depth) self.model.asc_desc(depth_to_pressure(self.current_depth), depth_to_pressure(next_stop_depth), -settings.DECO_ASCENT_RATE, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.run_time += abs( (float(self.current_depth) - float(next_stop_depth)) / float(settings.DECO_ASCENT_RATE)) self.logger.debug("update run time : %ss", self.run_time) self.output_segments.append( SegmentAscDesc(self.current_depth, next_stop_depth, settings.DECO_ASCENT_RATE, self.current_tank, self.pp_o2)) # now we moved up the the next depth self.current_depth = next_stop_depth max_mv = self.model.m_value(depth_to_pressure(self.current_depth)) control = self.model.control_compartment() # Check and switch deco gas temp_tank = self.current_tank # remember in case we switch if self.set_deco_gas(self.current_depth): # True if we changed gas if in_ascent_cycle: self.logger.debug( "Add AscDesc(2): start_depth:%s, " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, settings.ASCENT_RATE, temp_tank, self.pp_o2)) start_depth = self.current_depth # set next rounded stop depth next_stop_depth = int( self.current_depth) - settings.STOP_DEPTH_INCREMENT self.logger.debug("next stop depth: %s, target depth: %s", next_stop_depth, target_depth) # check in cas we are overshooting or hit last stop if (next_stop_depth < target_depth or self.current_depth < settings.LAST_STOP_DEPTH): self.logger.debug("next_stop_depth (%s) < target_depth (%s)", next_stop_depth, target_depth) next_stop_depth = target_depth elif self.current_depth < settings.LAST_STOP_DEPTH: self.logger.debug("current_depth (%s) < LAST_STOP_DEPTH (%s)", self.current_depth, settings.LAST_STOP_DEPTH) next_stop_depth = target_depth elif (next_stop_depth < settings.LAST_STOP_DEPTH and next_stop_depth > 0): self.logger.debug( "next_stop_depth (%s) < " "settings.LAST_STOP_DEPTH (%s)", next_stop_depth, settings.LAST_STOP_DEPTH) next_stop_depth = target_depth if self.model.gradient.gf_set: # update gf for next stop self.model.gradient.set_gf_at_depth(next_stop_depth) # are we still in ascent segment ? if in_ascent_cycle: self.logger.debug( "Add AscDesc(3): start_depth:%s, " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, -settings.ASCENT_RATE, self.current_tank, self.pp_o2))
def 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))
def ascend(self, target_depth): """Ascend to target depth, decompressing if necessary. If inFinalAscent then gradient factors start changing, and automatic gas selection is made. This method is called by do_dive() :param float target_depth: in meter, target depth for the ascend :raises ModelException: <Exceptions from model> """ force_deco_stop = False in_deco_cycle = False deco_stop_time = 0 if self.in_final_ascent and settings.USE_OC_DECO: self.set_deco_gas(self.current_depth) if self.current_depth < target_depth: # going backwards ! raise ProcessingError("Not allowed to ascend while descending !") # Set initial stop to be the next integral stop depth if self.current_depth % settings.STOP_DEPTH_INCREMENT > 0: # we are not on a stop depth already : go to the next stop depth next_stop_depth = (int(self.current_depth / settings.STOP_DEPTH_INCREMENT) * settings.STOP_DEPTH_INCREMENT) else: next_stop_depth = int(self.current_depth - settings.STOP_DEPTH_INCREMENT) self.logger.debug("next_stop_depth: %s", next_stop_depth) # hack in case we are overshooting or hit last stop or any of # the other bizzar combinations ... if (next_stop_depth < target_depth or self.current_depth < settings.LAST_STOP_DEPTH): next_stop_depth = target_depth self.logger.debug("new next_stop_depth: %s", next_stop_depth) elif next_stop_depth == settings.LAST_STOP_DEPTH: self.logger.warning("next_stop_depth==LAST_STOP_DEPTH !") next_stop_depth = target_depth self.logger.debug("new next_stop_depth: %s", next_stop_depth) elif next_stop_depth < settings.LAST_STOP_DEPTH: next_stop_depth = settings.LAST_STOP_DEPTH self.logger.debug("new next_stop_depth: %s", next_stop_depth) # Initialise ascent segment start depth start_depth = self.current_depth in_ascent_cycle = True # Start in free ascent # Initialise gradient factor for next (in this case first) stop depth self.model.gradient.set_gf_at_depth(next_stop_depth) # Remember maxM-Value and controlling compartment max_mv = self.model.m_value(depth_to_pressure(self.current_depth)) control = self.model.control_compartment() while self.current_depth > target_depth: self.logger.debug("ascent from: %s, to: %s", self.current_depth, next_stop_depth) # can we move to the proposed next stop depth ? model_ceiling = self.model.ceiling() self.logger.debug("model ceiling: %s", model_ceiling) while force_deco_stop or next_stop_depth < model_ceiling: in_deco_cycle = True # Only used for first entry into deco stop force_deco_stop = False if in_ascent_cycle: # Finalise last ascent cycle as we are now decomp if start_depth > self.current_depth: # add ascent segment self.logger.debug("Add AscDesc(1): start_depth:%s " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, settings.ASCENT_RATE, self.current_tank, self.pp_o2)) in_ascent_cycle = False # set m-value gradient under the following conditions: # - if not in multilevel mode, then set it as soon as # we do a decompression cycle # - otherwise wait until we are finally # surfacing before setting it if ((not settings.MULTILEVEL_MODE or self.in_final_ascent) and (not self.model.gradient.gf_set)): self.model.gradient.set_gf_slope_at_depth( self.current_depth) self.model.gradient.set_gf_at_depth(next_stop_depth) self.logger.debug("...set m-value gradient: %s", self.model.gradient.gf) # calculate stop_time # if (deco_stop_time == 0 and # self.run_time % settings.STOP_TIME_INCREMENT > 0): # stop_time = ( # int(self.run_time / settings.STOP_TIME_INCREMENT) * # settings.STOP_TIME_INCREMENT + # settings.STOP_TIME_INCREMENT - self.run_time) # print("+++++ ", stop_time) # if stop_time == 0: # stop_time = settings.STOP_TIME_INCREMENT # in second # else: stop_time = settings.STOP_TIME_INCREMENT # in second # execute the stop self.model.const_depth(depth_to_pressure(self.current_depth), stop_time, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) deco_stop_time += stop_time # sanity check for infinite loop if deco_stop_time > 300000: raise InfiniteDeco("Infinite deco error") model_ceiling = self.model.ceiling() # finished decompression loop if in_deco_cycle: self.logger.debug("...in deco cycle") # finalise the last deco cycle self.run_time += deco_stop_time self.logger.debug( "update run time with deco time: %ss at %sm (runtime:%s)", deco_stop_time, self.current_depth, self.run_time) if settings.FORCE_ALL_STOPS: force_deco_stop = True # write deco segment deco_segment = SegmentDeco(self.current_depth, deco_stop_time, self.current_tank, self.pp_o2) deco_segment.mv_max = max_mv deco_segment.gf_used = self.model.gradient.gf deco_segment.control_compartment = control self.output_segments.append(deco_segment) in_deco_cycle = False deco_stop_time = 0 if in_ascent_cycle: self.logger.debug("...in ascent cycle, do asc from %s to %s", self.current_depth, next_stop_depth) self.model.asc_desc(depth_to_pressure(self.current_depth), depth_to_pressure(next_stop_depth), -settings.ASCENT_RATE, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.run_time += abs((float(self.current_depth) - float(next_stop_depth)) / float(settings.ASCENT_RATE)) self.logger.debug("update run time : %ss", self.run_time) else: self.logger.debug("...in deco cycle, do asc from %s to %s", self.current_depth, next_stop_depth) self.model.asc_desc(depth_to_pressure(self.current_depth), depth_to_pressure(next_stop_depth), -settings.DECO_ASCENT_RATE, self.current_tank.f_he, self.current_tank.f_n2, self.pp_o2) self.run_time += abs((float(self.current_depth) - float(next_stop_depth)) / float(settings.DECO_ASCENT_RATE)) self.logger.debug("update run time : %ss", self.run_time) self.output_segments.append( SegmentAscDesc(self.current_depth, next_stop_depth, settings.DECO_ASCENT_RATE, self.current_tank, self.pp_o2)) # now we moved up the the next depth self.current_depth = next_stop_depth max_mv = self.model.m_value(depth_to_pressure(self.current_depth)) control = self.model.control_compartment() # Check and switch deco gas temp_tank = self.current_tank # remember in case we switch if self.set_deco_gas(self.current_depth): # True if we changed gas if in_ascent_cycle: self.logger.debug("Add AscDesc(2): start_depth:%s, " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, settings.ASCENT_RATE, temp_tank, self.pp_o2)) start_depth = self.current_depth # set next rounded stop depth next_stop_depth = int( self.current_depth) - settings.STOP_DEPTH_INCREMENT self.logger.debug("next stop depth: %s, target depth: %s", next_stop_depth, target_depth) # check in cas we are overshooting or hit last stop if (next_stop_depth < target_depth or self.current_depth < settings.LAST_STOP_DEPTH): self.logger.debug("next_stop_depth (%s) < target_depth (%s)", next_stop_depth, target_depth) next_stop_depth = target_depth elif self.current_depth < settings.LAST_STOP_DEPTH: self.logger.debug("current_depth (%s) < LAST_STOP_DEPTH (%s)", self.current_depth, settings.LAST_STOP_DEPTH) next_stop_depth = target_depth elif (next_stop_depth < settings.LAST_STOP_DEPTH and next_stop_depth > 0): self.logger.debug("next_stop_depth (%s) < " "settings.LAST_STOP_DEPTH (%s)", next_stop_depth, settings.LAST_STOP_DEPTH) next_stop_depth = target_depth if self.model.gradient.gf_set: # update gf for next stop self.model.gradient.set_gf_at_depth(next_stop_depth) # are we still in ascent segment ? if in_ascent_cycle: self.logger.debug("Add AscDesc(3): start_depth:%s, " "current_depth:%s", start_depth, self.current_depth) self.output_segments.append( SegmentAscDesc(start_depth, self.current_depth, -settings.ASCENT_RATE, self.current_tank, self.pp_o2))
def 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))
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))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(20), 2.02086, 5, 'Wrong depth pressure at 20m : %s' % depth_to_pressure(20))
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))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(40), 4.04172, 5, 'Wrong depth pressure at 40m : %s' % depth_to_pressure(40))
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))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(60), 6.06258, 5, 'Wrong depth pressure at 60m : %s' % depth_to_pressure(60))
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))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(80), 8.08344, 5, 'Wrong depth pressure at 80m : %s' % depth_to_pressure(80))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(20), 2.02086, 5, 'Wrong depth pressure at 20m : %s' % depth_to_pressure(20))
def runTest(self): self.assertAlmostEqual(depth_to_pressure(100), 10.1043, 4, 'Wrong depth pressure at 100m : %s' % depth_to_pressure(100))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(0), 0.0, 5, 'Wrong depth pressure at 0m : %s' % depth_to_pressure(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))
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))
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))
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
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))
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))
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))
def runTest(self): self.assertAlmostEqual( depth_to_pressure(30), 3.03129, 5, 'Wrong depth pressure at 30m : %s' % depth_to_pressure(30))
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))
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