Skip to content

visualdynamics.core.merge

merge

Merging several objects of one type into one.

mergeable says whether — and, when not, why, which is what a test or a tooltip wants; the GUI simply does not offer a merge that would refuse. merge builds the combined object and touches nothing it was given.

The ground rules, per Brandon:

  • Only objects of the same concrete type merge. Channel tables merge when their rows agree — a multi-pass survey carries one table per pass, with the reference rows repeated verbatim and the response rows disjoint. Identical rows (numbering aside) collapse to one, the rest concatenate, and channel numbers renumber: they are the wiring of one acquisition, not the identity of a measurement point. The same point with the same role described two different ways is a disagreement, and refuses by naming the point.
  • A geometry merge must have disjoint node and coordinate-system ids — colliding numbers are a decision, not a default. Traceline and element ids only name themselves, so those renumber quietly.
  • FRFs (and every other frequency-domain array) refuse when two records share a (response, reference) pair: the same measurement twice is not one set. Time histories are the exception — a repeated DOF is another average, and lands in its own averaging column (block) — but only two time-data merges tell a true story: repeat runs (every channel shared) or one acquisition split across files (no channel shared, equal capture counts). A mixture — a roving survey's passes — refuses (see _time_refusal).
  • Units gate per object where units are object-wide (geometry, shapes): everything defined, or everything raw — mixing SI values with raw ones would add metres to numbers. Data-array units are per record and simply ride along.

Functions:

Name Description
mergeable

None when the objects can merge; otherwise the reason they cannot.

merge

The combined object; call mergeable first, this trusts it.

Classes

Functions:

mergeable

mergeable(objects: Sequence[Any]) -> str | None

None when the objects can merge; otherwise the reason they cannot.

Source code in src/visualdynamics/core/merge.py
def mergeable(objects: Sequence[Any]) -> str | None:
    """None when the objects can merge; otherwise the reason they cannot."""
    objects = list(objects)
    if len(objects) < 2:
        return 'merging takes at least two objects'
    first = type(objects[0])
    if any(type(obj) is not first for obj in objects):
        return 'only objects of the same type merge'
    if first is ChannelTable:
        return _table_refusal(objects)
    if issubclass(first, Geometry):
        return _geometry_refusal(objects)
    if issubclass(first, ShapeSet):
        return _shapes_refusal(objects)
    if issubclass(first, DataArray):
        return _data_refusal(objects)
    return f'{first.__name__} does not merge'

merge

merge(objects: Sequence[Any]) -> Any

The combined object; call mergeable first, this trusts it.

Source code in src/visualdynamics/core/merge.py
def merge(objects: Sequence[Any]) -> Any:
    """The combined object; call `mergeable` first, this trusts it."""
    objects = list(objects)
    reason = mergeable(objects)
    if reason is not None:
        raise ValueError(reason)
    if isinstance(objects[0], ChannelTable):
        return _merge_tables(objects)
    if isinstance(objects[0], Geometry):
        return _merge_geometry(objects)
    if isinstance(objects[0], ShapeSet):
        return _merge_shapes(objects)
    return _merge_data(objects)