Dive segment

Dive segment: one leg of a dive profile.

A DiveSegment is an immutable value — start/end pressure, duration, gas, and a SegmentKind — with linear pressure interpolation inside it. Profiles are sequences of segments; models integrate them; planners emit them. Segments carry no results (tissue states, ceilings): those live in the Dive layer.

class diveplan.core.dive_segment.SegmentKind[source]

Bases: object

Namespace of segment-kind enums, grouped by direction of travel.

The three sub-enums (Descent, Ascent, Constant) categorize a segment for algorithms: planners emit DECO_ASCENT legs and STOP holds, gas accounting bills deco kinds at the deco SAC, and GAS_SWITCH marks the one constant-depth kind allowed a zero duration.

Membership in a parent group tests naturally: segment.kind in SegmentKind.Constant. SegmentKind.Members is the union of all kinds for annotations, and DESCENT/ASCENT/CONSTANT are the conventional defaults for each group.

class Descent(*values)[source]

Bases: Enum

Downward traverses (start pressure below end pressure).

class Ascent(*values)[source]

Bases: Enum

Upward traverses: planned deco legs vs. forced (direct) ascents.

class Constant(*values)[source]

Bases: Enum

Constant-depth segments: bottom time, deco stops, gas switches.

static from_name(name)[source]

Look up a segment kind by member name, e.g. "DESCENT", "STOP".

Member names are unique across the Descent/Ascent/Constant sub-enums, so a bare name is unambiguous. Used by DiveSegment deserialization.

Raises:

ValueError – If no segment kind has that name.

Parameters:

name (str)

Return type:

Descent | Ascent | Constant

class diveplan.core.dive_segment.DiveSegment(start_pressure, end_pressure, duration, gas, *, ascent_kind=Ascent.FORCED_ASCENT, constant_kind=Constant.BOTTOM)[source]

Bases: object

One leg of a dive profile: pressures, duration, gas, and kind.

Immutable value object (__slots__ + setattr guards) — profiles copy cheaply because segments never change. Pressure varies linearly between the endpoints; the interpolation helpers (pressure_at_time and friends) are exact under that assumption. The kind is derived from the pressure direction, refined by ascent_kind/constant_kind.

Parameters:
property average_pressure: Pressure

Mean of the start and end pressures.

property absolute_pressure_change: Pressure

Magnitude of the pressure change from start to end (always non-negative).

property pressure_rate: float

Signed rate of pressure change in mbar per second.

Positive when descending, negative when ascending, and zero for a constant-depth segment (including a possibly zero-duration gas switch, which would otherwise divide by zero).

pressure_at_time(t)[source]

Pressure at time t into the segment. Linear interpolation.

Parameters:

t (timedelta)

Return type:

Pressure

pressure_at_fraction(fraction)[source]

Pressure at fraction (0 to 1) into the segment. Linear interpolation.

Parameters:

fraction (float)

Return type:

Pressure

time_at_pressure(pressure)[source]

Time at which a given pressure is reached. Linear interpolation.

Parameters:

pressure (Pressure)

Return type:

timedelta

time_at_fraction(fraction)[source]

Time at which a given fraction (0 to 1) is reached. Linear interpolation.

Parameters:

fraction (float)

Return type:

timedelta

fraction_at_pressure(pressure)[source]

Fraction (0 to 1) at which a given pressure is reached. Linear interpolation.

Parameters:

pressure (Pressure)

Return type:

float

fraction_at_time(t)[source]

Fraction (0 to 1) at time t into the segment. Linear interpolation.

Parameters:

t (timedelta)

Return type:

float

split_at_time(t)[source]

Split into two segments at time t — useful for injecting a gas switch mid-segment.

Parameters:

t (timedelta)

Return type:

tuple[DiveSegment, DiveSegment]

split_at_fraction(fraction)[source]

Split into two segments at fraction (0 to 1) — useful for injecting a gas switch mid-segment.

Parameters:

fraction (float)

Return type:

tuple[DiveSegment, DiveSegment]

merge_with(other, *, force=False)[source]

Merge with another segment if they are continuous (end pressure of self matches start pressure of other). If force=True, merges regardless of continuity (use with caution — may produce unrealistic segments). The resulting segment takes the start pressure of self and end pressure of other, with duration combined. Gas and kind are taken from self if continuous. Ascent and constant kinds are preserved if self is ascent or constant, otherwise default to SegmentKind.ASCENT or SegmentKind.CONSTANT.

Known limitation: the merged segment always keeps self.gas. When force=True is used to merge two segments with different gases (e.g. across a gas discontinuity), other.gas is silently discarded. The caller is responsible for only force-merging segments where dropping the other gas is acceptable.

Parameters:
  • other (DiveSegment) – The other segment to merge with.

  • force (bool) – Whether to merge regardless of continuity. Defaults to False.

Raises:

ValueError – If segments are not fully continuous and force is False.

Return type:

DiveSegment

Returns:

The merged dive segment.

is_pressure_continuous_with(other)[source]

End pressure of self matches start pressure of other.

Parameters:

other (DiveSegment)

Return type:

bool

is_gas_continuous_with(other)[source]

Same gas on both segments (Nones treated as unknown — not continuous).

Parameters:

other (DiveSegment)

Return type:

bool

is_rate_continuous_with(other)[source]

Same pressure rate across the boundary — segments form an unbroken linear traverse.

Parameters:

other (DiveSegment)

Return type:

bool

is_continuous_with(other, *, check_gas=False, check_rate=False)[source]

Pressure-continuous by default. Optionally also checks gas and/or rate continuity. All three true = fully continuous (no seam between segments).

Parameters:
Return type:

bool

is_fully_continuous_with(other)[source]

Convenience — checks pressure, gas, and rate together.

Parameters:

other (DiveSegment)

Return type:

bool

iter_pressures(interval, *, include_end=True)[source]

Yield (elapsed, pressure) at each interval through the segment. The start point is always yielded. The end point is yielded if include_end=True and it doesn’t coincide with the last interval tick.

Example

for t, p in segment.iter_pressures(timedelta(seconds=30)):

tissue_model.update(p, timedelta(seconds=30))

Parameters:
Return type:

Iterator[tuple[timedelta, Pressure]]

to_dict()[source]

Serialize to a JSON-compatible dict.

Pressures are stored as integer mbar (the ground truth), duration in seconds, and the kind by its member name.

Return type:

dict[str, Any]

classmethod from_dict(data)[source]

Reconstruct a DiveSegment from to_dict() output.

Raises:
  • ValueError – If the stored kind contradicts the pressure geometry (e.g. kind “DESCENT” but start pressure >= end pressure), or the kind name is unknown.

  • KeyError – If a required field is missing.

Parameters:

data (Mapping[str, Any])

Return type:

DiveSegment

to_json(indent=None)[source]

Serialize to a JSON string.

Parameters:

indent (int | None)

Return type:

str

classmethod from_json(data)[source]

Reconstruct a DiveSegment from a JSON string (see from_dict()).

Parameters:

data (str)

Return type:

DiveSegment