Skip to content

hybrid_system

Hybrid system core types.

FlowFunction

Bases: Protocol

Continuous dynamics callback.

EventSurfaceFunction

Bases: Protocol

Scalar event-surface callback.

ResetFunction

Bases: Protocol

State reset callback.

CrossingDirection

Bases: IntEnum

Direction in which an event surface must cross zero.

SurfaceEntryPolicy

Bases: StrEnum

Behavior when a transition surface is zero on location entry.

ContinuousDynamics(flow, label=None) dataclass

Reusable continuous dynamics definition.

Parameters:

Name Type Description Default
flow Callable[..., Derivative]

Dynamics function returning the state derivative. Scalar derivative returns are accepted only for single-state systems, both during solver evaluation and when derivatives are captured on the returned trace grid.

required
label str | None

Optional display label.

None

Location(dynamics, *, label=None, parameters=None) dataclass

Location(
    dynamics: ContinuousDynamics,
    *,
    label: str | None = None,
    parameters: Parameters | None = None,
)
Location(
    dynamics: Callable[..., Derivative],
    *,
    label: str | None = None,
    parameters: Parameters | None = None,
)

Discrete hybrid-automaton location.

Parameters:

Name Type Description Default
dynamics ContinuousDynamics | Callable[..., Derivative]

Continuous dynamics or bare flow callback active here.

required
label str | None

Optional display label.

None
parameters Parameters | None

Location-local parameter map.

None
Source code in src/flowcean/hybrid/hybrid_system.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
def __init__(
    self,
    dynamics: ContinuousDynamics | Callable[..., Derivative],
    *,
    label: str | None = None,
    parameters: Parameters | None = None,
) -> None:
    if isinstance(dynamics, ContinuousDynamics):
        continuous_dynamics = dynamics
    elif callable(dynamics):
        continuous_dynamics = ContinuousDynamics(dynamics)
    else:
        message = (
            "Location requires ContinuousDynamics or a flow callback."
        )
        raise TypeError(message)
    if parameters is not None and not isinstance(parameters, Mapping):
        message = "parameters must be a mapping."
        raise TypeError(message)
    object.__setattr__(self, "dynamics", continuous_dynamics)
    object.__setattr__(self, "label", label)
    object.__setattr__(self, "parameters", dict(parameters or {}))

EventSurface(fn, direction=CrossingDirection.EITHER, label=None) dataclass

Scalar event surface defining a simulated transition event.

Flowcean transitions fire when fn reaches zero in direction. This is event-surface semantics, not Boolean guard-region semantics.

Parameters:

Name Type Description Default
fn Callable[..., float]

Root function; transitions when it crosses zero.

required
direction CrossingDirection

Crossing direction. Defaults to either direction.

EITHER
label str | None

Optional display label.

None

Reset(fn, label=None) dataclass

State reset applied on a transition.

Parameters:

Name Type Description Default
fn Callable[..., State]

Reset function applied at the event time.

required
label str | None

Optional display label.

None

Transition(source, target, event, reset=None, *, entry_policy=SurfaceEntryPolicy.ERROR) dataclass

Discrete event-triggered transition between locations.

event is a scalar zero-crossing surface.

Parameters:

Name Type Description Default
source Location

Source location.

required
target Location

Target location.

required
event EventSurface | Callable[..., float]

Event surface that triggers the transition.

required
reset Reset | Callable[..., State] | None

Optional reset applied upon transition.

None
entry_policy SurfaceEntryPolicy

Behavior when the event surface is exactly zero upon entry to the source location.

ERROR
Source code in src/flowcean/hybrid/hybrid_system.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
def __init__(
    self,
    source: Location,
    target: Location,
    event: EventSurface | Callable[..., float],
    reset: Reset | Callable[..., State] | None = None,
    *,
    entry_policy: SurfaceEntryPolicy = SurfaceEntryPolicy.ERROR,
) -> None:
    if not isinstance(source, Location):
        message = "source must be a Location."
        raise TypeError(message)
    if not isinstance(target, Location):
        message = "target must be a Location."
        raise TypeError(message)
    if isinstance(event, EventSurface):
        event_surface = event
    elif callable(event):
        event_surface = EventSurface(event)
    else:
        message = "event must be an EventSurface or callable."
        raise TypeError(message)
    if isinstance(reset, Reset) or reset is None:
        transition_reset = reset
    elif callable(reset):
        transition_reset = Reset(reset)
    else:
        message = "reset must be a Reset, callable, or None."
        raise TypeError(message)
    if type(entry_policy) is not SurfaceEntryPolicy:
        message = "entry_policy must be a SurfaceEntryPolicy."
        raise TypeError(message)
    if source is target and transition_reset is None:
        message = (
            "A transition with identical source and target locations "
            "requires a reset."
        )
        error = ValueError(message)
        error.add_note(
            "The jump changes neither location nor state, so approximate "
            "root localization can redetect the crossing. Add a reset "
            "that defines a post-jump state, use another target, or "
            "remove the transition.",
        )
        raise error
    object.__setattr__(self, "source", source)
    object.__setattr__(self, "target", target)
    object.__setattr__(self, "event", event_surface)
    object.__setattr__(self, "reset", transition_reset)
    object.__setattr__(self, "entry_policy", entry_policy)

HybridSystem(locations, transitions, initial_location, initial_state, parameters=dict()) dataclass

Hybrid system with locations and transitions.

Parameters:

Name Type Description Default
locations Sequence[Location]

Location objects in this system.

required
transitions Sequence[Transition]

Transition list defining event surfaces and resets.

required
initial_location Location

Starting location object.

required
initial_state State

Initial state vector.

required
parameters Parameters

Global parameter map passed to callbacks.

dict()

transitions_from(location)

Return transitions leaving the given location.

Source code in src/flowcean/hybrid/hybrid_system.py
322
323
324
325
326
327
328
def transitions_from(self, location: Location) -> list[Transition]:
    """Return transitions leaving the given location."""
    return [
        transition
        for transition in self.transitions
        if transition.source is location
    ]

Event(time, source_location, target_location, event_surface, reset, state_before, state_after, microstep) dataclass

Transition event information for a trace.

Trace(t, x, location, events, u=None, dx=None) dataclass

Simulation trace with time, state, and location labels.

as_dict()

Return a dictionary view of the trace.

Source code in src/flowcean/hybrid/hybrid_system.py
397
398
399
400
401
402
403
404
405
406
def as_dict(self) -> dict[str, object]:
    """Return a dictionary view of the trace."""
    return {
        "t": self.t,
        "x": self.x,
        "location": self.location,
        "events": self.events,
        "u": self.u,
        "dx": self.dx,
    }

display_label(obj)

Return the human-readable label for a hybrid-system object.

Source code in src/flowcean/hybrid/hybrid_system.py
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
def display_label(obj: object) -> str:
    """Return the human-readable label for a hybrid-system object."""
    if isinstance(obj, Location):
        return (
            obj.label
            or obj.dynamics.label
            or _callback_label(obj.dynamics.flow)
            or repr(obj)
        )
    if isinstance(obj, ContinuousDynamics):
        return obj.label or _callback_label(obj.flow) or repr(obj)
    if isinstance(obj, EventSurface):
        return obj.label or _callback_label(obj.fn) or repr(obj)
    if isinstance(obj, Reset):
        return obj.label or _callback_label(obj.fn) or repr(obj)
    return repr(obj)