Source code for dipplanner.model.buhlmann.compartment

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2011-2012 Thomas Chiroux
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.
# If not, see <http://www.gnu.org/licenses/gpl.html>
#
# This module is part of dipplanner, a Dive planning Tool written in python
"""Defines a Buhlmann compartment
"""

__authors__ = [
    # alphabetical order by last name
    'Thomas Chiroux', ]

import math
import logging

# local imports
from dipplanner import settings
from dipplanner.model.buhlmann.model_exceptions import ModelStateException


[docs]class Compartment(object): """Buhlmann compartment class *Attributes:* * h_he: helium halftime * h_n2: nitrogen halftime * a_he: helium : a coefficient * a_n2: nitrogen : a coefficient * b_he: helium : b coefficient * b_n2: nitrogen : b coefficient * k_he: helium : k coefficient (calculated) * k_n2: nitrogen : k coefficient (calculated) * pp_he: partial pressure of helium * pp_n2: partial pressure of nitrogen """
[docs] def __init__(self, h_he=None, h_n2=None, a_he=None, b_he=None, a_n2=None, b_n2=None): """Constructor for Compartment can be called without params, in this case, does not initiate anything can be called with time params and coef and in this case initate the compartment time constants *Keyword arguments:* :h_he: (float) -- helium halftime :h_n2: (float) -- nitrogen halftime :a_he: (float) -- helium: a coefficient :b_he: (float) -- helium: b coefficient :a_n2: (float) -- nitrogen: a coefficient :b_n2: (float) -- nitrogen: b coefficient *Returns:* <nothing> *Raise:* see set_compartment_time_constants method """ #initiate class logger self.logger = logging.getLogger( "dipplanner.model.buhlmann.compartment.Compartment") self.logger.debug("creating an instance of Compartment") self.h_he = 0.0 self.h_n2 = 0.0 self.k_he = 0.0 self.k_n2 = 0.0 self.a_he = 0.0 self.b_he = 0.0 self.a_n2 = 0.0 self.b_n2 = 0.0 self.pp_he = 0.0 self.pp_n2 = 0.0 self.a_he_n2 = 0.0 self.b_he_n2 = 0.0 self.const_exp_const_depth_he = None self.const_exp_const_depth_n2 = None self.old_k_he = None self.old_seg_time = None if h_he is not None and h_n2 is not None and \ a_he is not None and b_he is not None and \ a_n2 is not None and b_n2 is not None: self.set_compartment_time_constants(h_he, h_n2, a_he, b_he, a_n2, b_n2)
[docs] def __deepcopy__(self, memo): """deepcopy method will be called by copy.deepcopy Used for "cloning" the object into another new object. *Keyword Arguments:* :memo: -- not used here *Returns:* Compartment -- Compartment object copy of itself *Raise:* <nothing> """ newobj = Compartment(self.h_he, self.h_n2, self.a_he, self.b_he, self.a_n2, self.b_n2) newobj.h_he = self.h_he newobj.h_n2 = self.h_n2 newobj.k_he = self.k_he newobj.k_n2 = self.k_n2 newobj.a_he = self.a_he newobj.b_he = self.b_he newobj.a_n2 = self.a_n2 newobj.b_n2 = self.b_n2 newobj.pp_he = self.pp_he newobj.pp_n2 = self.pp_n2 newobj.a_he_n2 = self.a_he_n2 newobj.b_he_n2 = self.b_he_n2 newobj.const_exp_const_depth_he = self.const_exp_const_depth_he newobj.const_exp_const_depth_n2 = self.const_exp_const_depth_n2 newobj.old_k_he = self.old_k_he newobj.old_seg_time = self.old_seg_time return newobj
def __repr__(self): """Returns a string representing the comp *Keyword Arguments:* <none> *Returns:* str -- string representation of the compartment ex: "He:0.0 N2:19.9991340314 gf:0.3 mv_at:2.98611129437 max_amb:15.8731803417 MV:6.6973840088" *Raise:* <nothing> """ return "He:%s N2:%s mv_at:%s MV:%s" % ( self.pp_he, self.pp_n2, self.get_m_value_at(settings.AMBIANT_PRESSURE_SURFACE), self.get_mv(settings.AMBIANT_PRESSURE_SURFACE))
[docs] def __str__(self): """Return a human readable name of the segment *Keyword Arguments:* <none> *Returns:* str -- string representation of the compartment ex: "He:0.0 N2:19.9991340314 gf:0.3 mv_at:2.98611129437 max_amb:15.8731803417 MV:6.6973840088" *Raise:* <nothing> """ return self.__repr__()
[docs] def __unicode__(self): """Return a human readable name of the segment in unicode *Keyword Arguments:* <none> *Returns:* ustr -- unicode string representation of the compartment ex: u"He:0.0 N2:19.9991340314 gf:0.3 mv_at:2.98611129437 max_amb:15.8731803417 MV:6.6973840088" *Raise:* <nothing> """ return u"%s" % self.__repr__()
[docs] def set_compartment_time_constants(self, h_he, h_n2, a_he, b_he, a_n2, b_n2): """Sets the compartment's time constants *Keyword arguments:* :h_he: (float) -- Helium Halftime :h_n2: (float) -- Nitrogen Halftime :a_he: (float) -- Helium : a coefficient :b_he: (float) -- Helium : b coefficient :a_n2: (float) -- Nitrogen : a coefficient :b_n2: (float) -- Nitrogen : b coefficient *Returns:* <nothing> *Raise:* <nothing> """ self.h_he = h_he self.h_n2 = h_n2 self.k_he = math.log(2) / (float(h_he) * 60) self.k_n2 = math.log(2) / (float(h_n2) * 60) self.a_he = float(a_he) / 10 self.b_he = float(b_he) self.a_n2 = float(a_n2) / 10 self.b_n2 = float(b_n2)
[docs] def set_pp(self, pp_he, pp_n2): """Sets partial pressures of He and N2 *Keyword arguments:* :pp_he: (float) -- partial pressure of Helium :pp_n2: (float) -- partial pressure of Nitrogen *Return:* <nothing> *Raise:* <nothing> """ #if pp_he < 0.0 or pp_n2 < 0.0: # raise ModelStateException("Error in argument: # negative pp is not allowed") #else: self.pp_he = pp_he self.pp_n2 = pp_n2 #calculate_p_a_b_inert #Calculate and returns a_he_n2 and b_he_n2 #based on current pp_he and pp_n2 of this compartment # calculate adjusted a, b coefficients based on those of He and N2 self.a_he_n2 = ((self.a_he * pp_he) + (self.a_n2 * pp_n2)) / \ (pp_he + pp_n2) self.b_he_n2 = ((self.b_he * pp_he) + (self.b_n2 * pp_n2)) / \ (pp_he + pp_n2)
[docs] def const_depth(self, pp_he_inspired, pp_n2_inspired, seg_time): """Constant depth calculations. Uses instananeous equation: P = Po + (Pi - Po)(1-e^-kt) store the new values in self.pp_he and self.pp_n2 *Keyword arguments:* :pp_he_inspired: (float) -- partial pressure of inspired helium :pp_n2_inspired: (float) -- partial pressure of inspired nitrogen :seg_time: (float) -- segment time in seconds *Return:* <nothing> *Raise:* ModelStateException -- if pp or time < 0 """ # below is an optimisation to reduce the # (1 - math.exp(-self.k_he*float(seg_time))) # calculation : only calculate when seg_time changes (k_he or k_n2 does # not change for a dive) if self.old_seg_time is None: self.old_seg_time = seg_time self.const_exp_const_depth_he = (1 - math.exp(-self.k_he * float(seg_time))) self.const_exp_const_depth_n2 = (1 - math.exp(-self.k_n2 * float(seg_time))) elif self.old_seg_time != seg_time: self.old_seg_time = seg_time self.const_exp_const_depth_he = (1 - math.exp(-self.k_he * float(seg_time))) self.const_exp_const_depth_n2 = (1 - math.exp(-self.k_n2 * float(seg_time))) if pp_he_inspired < 0 or pp_n2_inspired < 0 or seg_time < 0: raise ModelStateException( "Error in argument: negative value is not allowed") else: new_pp_he = self.pp_he + ((pp_he_inspired - self.pp_he) * self.const_exp_const_depth_he) new_pp_n2 = self.pp_n2 + ((pp_n2_inspired - self.pp_n2) * self.const_exp_const_depth_n2) #self.set_pp(new_pp_he, new_pp_n2) # below is an 'inline' version of set_pp for optimisation: self.pp_he = new_pp_he self.pp_n2 = new_pp_n2 #calculate_p_a_b_inert #Calculate and returns a_he_n2 and b_he_n2 #based on current pp_he and pp_n2 of this compartment # calculate adjusted a, b coefficients based on those of He and N2 self.a_he_n2 = ((self.a_he * new_pp_he) + (self.a_n2 * new_pp_n2)) / (new_pp_he + new_pp_n2) self.b_he_n2 = ((self.b_he * new_pp_he) + (self.b_n2 * new_pp_n2)) / (new_pp_he + new_pp_n2)
[docs] def asc_desc(self, pp_he_inspired, pp_n2_inspired, rate_he, rate_n2, seg_time): """Ascend or descent calculations. Uses equation : P=Pio+R(t -1/k)-[Pio-Po-(R/k)]e^-kt store the new values in self.pp_he and self.pp_n2 *Keyword arguments:* :pp_he_inspired: (float) -- partial pressure of inspired helium :pp_n2_inspired: (float) -- partial pressure of inspired nitrogen :rate_he: (float) -- rate of change of pp_he :rate_n2: (float) -- rate of change of pp_n2 :seg_time: (float) -- segment time in seconds *Return:* <nothing> *Raise:* ModelStateException -- if pp or time < 0 """ if pp_he_inspired < 0 or pp_n2_inspired < 0 or seg_time < 0: raise ModelStateException( "Error in argument: negative value is not allowed") else: new_pp_he = pp_he_inspired + rate_he * \ (float(seg_time) - (1.0 / self.k_he)) - \ (pp_he_inspired - self.pp_he - (rate_he / self.k_he)) * \ math.exp(-self.k_he * float(seg_time)) new_pp_n2 = pp_n2_inspired + rate_n2 * \ (float(seg_time) - (1.0 / self.k_n2)) - \ (pp_n2_inspired - self.pp_n2 - (rate_n2 / self.k_n2)) * \ math.exp(-self.k_n2 * float(seg_time)) #self.set_pp(new_pp_he, new_pp_n2) # below is an 'inline' version of set_pp for optimisation: self.pp_he = new_pp_he self.pp_n2 = new_pp_n2 #calculate_p_a_b_inert #Calculate and returns a_he_n2 and b_he_n2 #based on current pp_he and pp_n2 of this compartment # calculate adjusted a, b coefficients based on those of He and N2 self.a_he_n2 = ((self.a_he * new_pp_he) + (self.a_n2 * new_pp_n2)) / (new_pp_he + new_pp_n2) self.b_he_n2 = ((self.b_he * new_pp_he) + (self.b_n2 * new_pp_n2)) / (new_pp_he + new_pp_n2)
[docs] def get_m_value_at(self, pressure): """Gets M-Value for given ambient pressure using the Buhlmann equation Pm = Pa/b +a where: * Pm = M-Value pressure, * Pa = ambiant pressure * a,b co-efficients .. note:: Not used for decompression but for display of M-value limit line .. note:: this method does not use gradient factors. *Keyword arguments:* :pressure: (float) -- ambient pressure (in bar) *Return:* float -- M_value: maximum tolerated pressure in bar *Raise:* <nothing> """ return float(pressure) / self.b_he_n2 + self.a_he_n2
[docs] def get_max_amb(self, gf): """Gets Tolerated Absolute Pressure for the compartment for current pp of He and N2 *Keyword arguments:* :gf: (float) -- gradient factor : 0.1 to 1.0, typical 0.2 - 0.95 *Return:* float -- maximum tolerated pressure (absolute) in bar *Raise:* <nothing> """ return ((self.pp_he + self.pp_n2) - self.a_he_n2 * gf) / \ (gf / self.b_he_n2 - gf + 1.0)
[docs] def get_mv(self, p_amb): """Gets M-Value for a compartment, given an ambient pressure *Keyword arguments:* :p_amb: (float) -- ambiant pressure *Return:* float -- M-value *Raise:* <nothing> """ #self.logger.debug("comp m-value for %s : %s" % (p_amb, mv)) return (self.pp_he + self.pp_n2) / (float(p_amb) / self.b_he_n2 + self.a_he_n2)

Project Versions