# Alternative Implementations¶

The decompression calculations in DecoTengu are divided into various parts

- descent
- bottom time
- finding first stop
- free ascent to first stop (or surface)
- decompression ascent to surface when decompression required
- tissues saturation calculations

Each part can be replaced with an alternative, independent implementation. The decotengu.alt module provides some of such alternatives

- tabular calculator - calculate tissues saturation using precomputed values of exponential function (useful when exponential function is too expensive on a given hardware)
- deco stop stepper - naive algorithm to find length of decompression stop using 1 minute intervals
- decompression calculations using fixed point arithmetic
- first decompression stop binary search algorithm

## Tabular Calculations¶

To calculate saturation of inert gas in tissue compartments, the decompression model uses Schreiner equation, which calls exponential function. Such call can be too costly or even impossible on some of the hardware architectures, i.e. microcontrollers lacking FPU. To make decompression software available on such hardware architectures, the exponential function values can be precomputed and stored in a table. DecoTengu library allows to experiment with dive decompression calculations using such technique, which we call tabular calculation.

The tabular calculation uses group homomorphism of exponential function

\[e^{x + y} = e^x * e^y\]

Part of Schreiner equation (see *Schreiner Equation*) is calculation of
inert gas exposure with exponential function

\[e^{-k * t}\]

We can precompute the values for above function for 1 minute and for 6 seconds intervals and store them in a table for each gas decay constant \(k\). Then, exponential function results can be calculated with formula

\[e^{-k * t} = (e_{1min}) ^ {n_{1min}} * (e_{6s}) ^ {n_{6s}}\]

where

- \(e_{1min}\)
- Exponential function value \(e^{-k * 1}\).
- \(e_{6s}\)
- Exponential function value \(e^{-k * 0.1}\) (\(0.1\) is 6 seconds).
- \(n_{1min}\)
- Number of one minute time intervals \(t\ div\ 1\) in time \(t\).
- \(n_{6s}\)
- Number of 6 second time intervals \((t\ mod\ 1) * 10\) in time \(t\ mod\ 1\).

The precalculated values of exponential function imply configuration constraints, which are discussed in the following section.

### Configuration Constraints¶

The main configuration constraint for DecoTengu tabular calculation is the number of precomputed values of exponential function. This is driven by limited amount of computer memory.

To keep the number of precomputed values of exponential function constant, we need to make ascent and descent rates constant. Therefore, first configuration constraint sets ascent and descent rates to 10m/min.

The second configuration constraint is the smallest depth change for which tissue saturation can be calculated. By default it is 1m (or 6s at 10m/min), which forces us to round up current depth value, i.e. from 31.2m to 32m.

### Implementation¶

The values of exponential function are precomputed and stored by tabular
calculator class `decotengu.alt.tab.TabExp`.

The `decotengu.alt.tab.TabExp` class divides time into
one minute segments and 6 seconds segments and caclulates exponential
function value using formula specified at the begining of this section.

The helper function `decotengu.alt.tab.tab_engine()` takes
decompression engine object as an argument and overrides engine
configuration and methods, so decompression calculations can be performed
with tabular calculator.

### Example¶

To calculate dive decompression information using tabular calculator
override decompression engine object with
`decotengu.alt.tab.tab_engine()` function.

Create the decompression engine first

```
>>> import decotengu
>>> from decotengu.alt.tab import tab_engine
>>> engine = decotengu.create()
>>> engine.add_gas(0, 21)
```

Override the engine

```
>>> tab_engine(engine)
>>> print(engine.model._exp)
<decotengu.alt.tab.TabExp object at ...>
```

Perform calculations

```
>>> profile = list(engine.calculate(35, 40))
>>> for stop in engine.deco_table:
... print(stop)
DecoStop(depth=18.0, time=1.0)
DecoStop(depth=15.0, time=1.0)
DecoStop(depth=12.0, time=4.0)
DecoStop(depth=9.0, time=6.0)
DecoStop(depth=6.0, time=9.0)
DecoStop(depth=3.0, time=22.0)
```

## Decimal Calculations¶

DecoTengu allows to experiment with accuracy of decompression calculations by providing context manager for overriding float data type with a decimal data type.

By default, float type is used for decompression calculations. The contex manager implemented in decotengu.alt.decimal module enables programmer to change the default and experiment with fixed point arithmetics.

**NOTE 1:** The implementation uses local context manager provided by
Python’s decimal module. In the future, it should be probably allowed to
use custom decimal types without decimal module dependency.

**NOTE 2:** At the moment, only tabular tissue calculator can be used with
decimal type override.

### Example¶

As an example, we will compare a dive to 90 meters for 20 minutes when using float type and decimal type with precision 9.

Let’s calculate dive profile using float type

```
>>> from decotengu import create
>>> from decotengu.alt.tab import tab_engine
>>> engine = create()
>>> deco_table = engine.deco_table
>>> tab_engine(engine)
>>> engine.model.gf_low = 0.2
>>> engine.model.gf_high = 0.75
>>> engine.add_gas(0, 13, 50)
>>> engine.add_gas(33, 36)
>>> engine.add_gas(21, 50)
>>> engine.add_gas(9, 80)
>>> profile = tuple(engine.calculate(90, 20, descent=False))
>>> last = profile[-1]
```

and dive profile using decimal type with precision 9

```
>>> from decotengu.alt.decimal import DecimalContext
>>> from decimal import Decimal
>>> with DecimalContext(prec=9) as ctx:
... engine = create()
... deco_table_dec = engine.deco_table
... tab_engine(engine)
... engine.model.gf_low = Decimal(0.2)
... engine.model.gf_high = Decimal(0.75)
... engine.add_gas(Decimal(0), Decimal(13), Decimal(50))
... engine.add_gas(Decimal(33), Decimal(36), Decimal(0))
... engine.add_gas(Decimal(21), Decimal(50), Decimal(0))
... engine.add_gas(Decimal(9), Decimal(80), Decimal(0))
... profile_dec = tuple(engine.calculate(Decimal(90), Decimal(20), descent=False))
>>> last_dec = profile_dec[-1]
```

Check the total time of dive decompression phase

```
>>> deco_table.total
103.0
>>> deco_table_dec.total
Decimal('103.00000')
```

Calculate maximum absolute error of saturation of inert gas in a tissue at the surface

```
>>> max_error = max(abs(v1[0] - float(v2[0]) + v1[1] - float(v2[1])) for v1, v2 in zip(last.data.tissues, last_dec.data.tissues))
>>> round(max_error, 10)
1.06134e-05
```

## First Decompression Stop Binary Search¶

In his `Gradient Factor Decompression Program in Fortran`,
Erik C. Baker uses binary search algorithm to find start of decompression
zone.

DecoTengu provides similar implementation, with the exception that Baker’s code tries to identify start of decompression zone with ‘desired accuracy’ and DecoTengu tries to find depth of first decompression stop.

The algorithm finding first decompression stop calculates absolute pressure of first decompression stop. The first decompression stop is at shallowest depth, which is outside dive decompression zone. The stop is at depth divisible by 3, it is measured in meters and its absolute pressure is measured in bars.

The calculation is performed between absolute pressure of current depth and absolute pressure of target depth. The target depth is the surface or any other depth divisible by 3 (i.e. gas mix switch depth).

The algorithm tries multiple ascent time values such that ascent by proposed time value is finished at depth divisible by 3 and checks if ascent does not violate ascent ceiling. The largest such time value defines the first decompression stop.

The time values are proposed using binary search algorithm. We assume knowledge of this algorithm.

The algorithm returns absolute pressure of detph of first decompression stop or the target depth. It returns the starting depth if a diver is already in decompression zone.

The algorithm finding first decompression stop is

- Let \(t_{3m}\) be time required to ascend by 3 meters.
- Let \(t\) be time required to ascend from current depth to target depth.
- Let \(dt = t\) mod \(t_{3m}\).
- Let \(n = t\) div \(t_{3m}\).
- Using binary search find \(k\) such that \(0 \le k \le n\) and ascent by time \(k * t_{3m} + dt\) is possible without violating ascent ceiling.
- If \(k = 0\), then return absolute pressure of starting depth.
- Otherwise, return absolute pressure of depth after ascent by time \(k * t_{3m} + dt\).

The complexity of the algorithm is \(O(log(n))\), where \(n\) is current depth divided by number 3. It depends on complexity of binary search algorithm.

The algorithm is implemented by
`decotengu.alt.bisect.BisectFindFirstStop` class.

## Naive Algorithms¶

There are various algorithms, which can be used for decompression
calculations. The `decotengu.alt.naive` module implements some of them,
i.e. calculation of decompression stop length using 1 minute intervals.

The algorithms are quite inefficient, usually \(O(n)\) while algorithm of complexity \(O(log(n))\) could be used. They are implemented in DecoTengu for comparison purposes only.

### Decompression Stop Stepper¶

The decompression stop stepper is simple algorithm to calculate length of a decompression stop.

Decompression stop length is calculated by increasing length of the stop by one minute until it is possible to ascend to next stop or to the surface.

The algorithm is implemented by `decotengu.alt.naive.DecoStopStepper`
class.

The complexity of the algorithm is \(O(n)\), where \(n\) is length of decompression stop in minutes.