예제 #1
0
    def from_tree(cls, tree, ctx):
        """
        Converts basic types representing YAML trees into an 'weldx.core.TimeSeries'.

        Parameters
        ----------
        tree :
            An instance of a basic Python type (possibly nested) that
            corresponds to a YAML subtree.
        ctx :
            An instance of the 'AsdfFile' object that is being constructed.

        Returns
        -------
        weldx.core.TimeSeries :
            An instance of the 'weldx.core.TimeSeries' type.

        """
        if "value" in tree:  # constant
            values = Q_(np.asarray(tree["value"]), tree["unit"])
            return TimeSeries(values)
        elif "values" in tree:
            time = tree["time"]
            interpolation = tree["interpolation"]
            values = Q_(tree["values"], tree["unit"])
            return TimeSeries(values, time, interpolation)

        return TimeSeries(tree["expression"])  # mathexpression
예제 #2
0
    def test_interp_time_warning():
        """Test if a warning is emitted when interpolating already interpolated data."""
        ts = TimeSeries(data=Q_([1, 2, 3], "m"), time=Q_([0, 1, 2], "s"))
        with pytest.warns(None) as recorded_warnings:
            ts_interp = ts.interp_time(Q_([0.25, 0.5, 0.75, 1], "s"))
        assert not any(w.category == UserWarning for w in recorded_warnings)

        with pytest.warns(UserWarning):
            ts_interp.interp_time(Q_([0.4, 0.6], "s"))
예제 #3
0
 def test_init_data_array(data, dims, coords, exception_type):
     """Test the `__init__` method with an xarray as data parameter."""
     da = xr.DataArray(data=data, dims=dims, coords=coords)
     if exception_type is not None:
         with pytest.raises(exception_type):
             TimeSeries(da)
     else:
         ts = TimeSeries(da)
         assert ts.data_array.dims[0] == "time"
예제 #4
0
    def from_yaml_tree(self, node: dict, tag: str, ctx):
        """Construct from tree."""
        if "value" in node:  # constant
            values = Q_(node["value"], node["units"])
            return TimeSeries(values)
        if "values" in node:
            time = node["time"]
            interpolation = node["interpolation"]
            values = Q_(node["values"], node["units"])
            return TimeSeries(values, time, interpolation)

        return TimeSeries(node["expression"])  # mathexpression
예제 #5
0
    def test_construction_discrete(data: pint.Quantity, time, interpolation, shape_exp):
        """Test the construction of the TimeSeries class."""
        # set expected values
        time_exp = time
        if isinstance(time_exp, pint.Quantity):
            time_exp = pd.TimedeltaIndex(time_exp.m, unit="s")

        exp_interpolation = interpolation
        if len(data.shape) == 0 and interpolation is None:
            exp_interpolation = "step"

        # create instance
        ts = TimeSeries(data=data, time=time, interpolation=interpolation)

        # check
        assert np.all(ts.data == data)
        assert np.all(ts.time == time_exp)
        assert ts.interpolation == exp_interpolation
        assert ts.shape == shape_exp
        assert data.is_compatible_with(ts.units)

        assert np.all(ts.data_array.data == data)
        assert ts.data_array.attrs["interpolation"] == exp_interpolation
        if time_exp is None:
            assert "time" not in ts.data_array
        else:
            assert np.all(ts.data_array.time == time_exp)
예제 #6
0
def test_local_coordinate_system_coords_timeseries(copy_arrays, lazy_load,
                                                   has_ref_time,
                                                   has_tdp_orientation):
    """Test reading and writing a LCS with a `TimeSeries` as coordinates to asdf."""
    # create inputs to lcs __init__
    me = ME("a*t", dict(a=Q_([[1, 0, 0]], "1/s")))
    ts = TimeSeries(data=me)

    ref_time = None
    if has_ref_time:
        ref_time = pd.Timestamp("13:37")

    time = None
    orientation = None
    if has_tdp_orientation:
        time = Q_([1, 2], "s")
        orientation = WXRotation.from_euler("x", [0, 90],
                                            degrees=True).as_matrix()

    # create lcs
    lcs = tf.LocalCoordinateSystem(orientation=orientation,
                                   coordinates=ts,
                                   time=time,
                                   time_ref=ref_time)

    # round trip and compare
    lcs_buffer = write_read_buffer({"lcs": lcs},
                                   open_kwargs={
                                       "copy_arrays": copy_arrays,
                                       "lazy_load": lazy_load
                                   })["lcs"]
    assert lcs_buffer == lcs
예제 #7
0
파일: util.py 프로젝트: BAMWelDX/weldx
def lcs_coords_from_ts(
    ts: TimeSeries, time: Union[pd.DatetimeIndex, pint.Quantity]
) -> xr.DataArray:
    """Create translation coordinates from a TimeSeries at specific timesteps.

    Parameters
    ----------
    ts:
        TimeSeries that describes the coordinate motion as a 3D vector.
    time
        Timestamps used for interpolation.
        TODO: add support for pd.DateTimeindex as well

    Returns
    -------
    xarray.DataArray :
        A DataArray with correctly labeled dimensions to be used for LCS creation.

    """
    ts_data = ts.interp_time(time=time)
    # assign vector coordinates and convert to mm
    ts_data = ts_data.rename({"dim_1": "c"}).assign_coords({"c": ["x", "y", "z"]})
    ts_data.data = ts_data.data.to("mm").magnitude
    ts_data["time"] = pd.TimedeltaIndex(ts_data["time"].data)
    return ts_data
예제 #8
0
파일: util.py 프로젝트: BAMWelDX/weldx
def sine(
    f: pint.Quantity,
    amp: pint.Quantity,
    bias: pint.Quantity = None,
    phase: pint.Quantity = Q_(0, "rad"),
) -> TimeSeries:
    """Create a simple sine TimeSeries from quantity parameters.

    f(t) = amp*sin(f*t+phase)+bias

    Parameters
    ----------
    f : pint.Quantity
        Frequency of the sine (in Hz)
    amp : pint.Quantity
        Sine amplitude
    bias : pint.Quantity
        function bias
    phase : pint.Quantity
        phase shift

    Returns
    -------
    TimeSeries

    """
    if bias is None:
        bias = 0.0 * amp.u
    expr_string = "a*sin(o*t+p)+b"
    parameters = {"a": amp, "b": bias, "o": Q_(2 * np.pi, "rad") * f, "p": phase}
    expr = MathematicalExpression(expression=expr_string, parameters=parameters)
    return TimeSeries(expr)
예제 #9
0
    def __post_init__(self):
        """Set defaults and convert parameter inputs."""
        if self.tag is None:
            self.tag = "GMAW"

        self.parameters = {
            k: (v if isinstance(v, TimeSeries) else TimeSeries(v))
            for k, v in self.parameters.items()
        }
예제 #10
0
    def test_construction_expression(data, shape_exp, unit_exp):
        """Test the construction of the TimeSeries class."""
        ts = TimeSeries(data=data)

        # check
        assert ts.data == data
        assert ts.time is None
        assert ts.interpolation is None
        assert ts.shape == shape_exp
        assert ts.data_array is None
        assert U_(unit_exp).is_compatible_with(ts.units)
예제 #11
0
파일: test_core.py 프로젝트: vhirtham/weldx
    def test_construction_expression(data, shape_exp, unit_exp):
        """Test the construction of the TimeSeries class."""
        ts = TimeSeries(data=data)

        # check
        assert ts.data == data
        assert ts.time is None
        assert ts.interpolation is None
        assert ts.shape == shape_exp
        assert ts.data_array is None
        assert Q_(1, unit_exp).check(UREG.get_dimensionality(ts.units))
예제 #12
0
class ShapeValidatorTestClass:
    """Helper class to test the shape validator"""

    prop1: np.ndarray = np.ones((1, 2, 3))
    prop2: np.ndarray = np.ones((3, 2, 1))
    prop3: np.ndarray = np.ones((2, 4, 6, 8, 10))
    prop4: np.ndarray = np.ones((1, 3, 5, 7, 9))
    prop5: float = 3.141
    quantity: pint.Quantity = Q_(10, "m")
    timeseries: TimeSeries = TimeSeries(Q_(10, "m"))
    nested_prop: dict = field(default_factory=lambda: {
        "p1": np.ones((10, 8, 6, 4, 2)),
        "p2": np.ones((9, 7, 5, 3, 1)),
    })
    time_prop: pd.DatetimeIndex = pd.timedelta_range("0s", freq="s", periods=9)
    optional_prop: np.ndarray = None
예제 #13
0
파일: util.py 프로젝트: CagtayFabry/weldx
def sine(
    f: QuantityLike,
    amp: QuantityLike,
    bias: QuantityLike = None,
    phase: QuantityLike = None,
) -> TimeSeries:
    """Create a simple sine TimeSeries from quantity parameters.

    f(t) = amp*sin(f*t+phase)+bias

    Parameters
    ----------
    f :
        Frequency of the sine (in Hz)
    amp :
        Sine amplitude
    bias :
        function bias
    phase :
        phase shift

    Returns
    -------
    weldx.TimeSeries :

    """
    if phase is None:
        phase = Q_(0, "rad")

    if bias is None:
        amp = Q_(amp)
        bias = 0.0 * amp.u
    expr_string = "a*sin(o*t+p)+b"
    parameters = {
        "a": amp,
        "b": bias,
        "o": Q_(2 * np.pi, "rad") * Q_(f),
        "p": phase
    }
    expr = MathematicalExpression(expression=expr_string,
                                  parameters=parameters)
    return TimeSeries(expr)
예제 #14
0
파일: test_core.py 프로젝트: vhirtham/weldx
    def test_construction_discrete(data, time, interpolation, shape_exp):
        """Test the construction of the TimeSeries class."""
        # set expected values
        if isinstance(time, pint.Quantity):
            time_exp = pd.TimedeltaIndex(time.magnitude, unit="s")
        else:
            time_exp = time

        # create instance
        ts = TimeSeries(data=data, time=time, interpolation=interpolation)

        # check
        assert np.all(ts.data == data)
        assert np.all(ts.time == time_exp)
        assert ts.interpolation == interpolation
        assert ts.shape == shape_exp
        assert data.check(UREG.get_dimensionality(ts.units))

        assert np.all(ts.data_array.data == data)
        assert ts.data_array.attrs["interpolation"] == interpolation
        if time_exp is None:
            assert "time" not in ts.data_array
        else:
            assert np.all(ts.data_array.time == time_exp)
예제 #15
0

@pytest.mark.parametrize(
    "test_input",
    [
        ShapeValidatorTestClass(prop4=np.ones((2, 3, 5, 7,
                                               9))),  # mismatch a - prop5
        ShapeValidatorTestClass(prop2=np.ones((5, 2,
                                               1))),  # mismatch n - prop1
        ShapeValidatorTestClass(optional_prop=np.ones((3, 2,
                                                       9))),  # wrong optional
        ShapeValidatorTestClass(
            time_prop=pd.date_range("2020", freq="D", periods=3)),
        ShapeValidatorTestClass(
            quantity=Q_([0, 3], "s")),  # mismatch shape [1]
        ShapeValidatorTestClass(timeseries=TimeSeries(Q_(
            [0, 3], "m"), Q_([0, 1], "s"))  # mismatch shape [1]
                                ),
    ],
)
def test_shape_validator_exceptions(test_input):
    with pytest.raises(ValidationError):
        write_read_buffer({"root": test_input})


@pytest.mark.parametrize(
    "test",
    [
        UnitValidatorTestClass(),
        UnitValidatorTestClass(length_prop=Q_(1, "inch")),
    ],
)
예제 #16
0
from weldx.asdf.util import write_read_buffer
from weldx.constants import Q_
from weldx.core import TimeSeries
from weldx.welding.processes import GmawProcess


@pytest.mark.parametrize(
    "inputs",
    [
        GmawProcess(
            "spray",
            "Fronius",
            "TPSi",
            dict(
                wire_feedrate=Q_(10, "m/min"),
                voltage=TimeSeries(Q_(40.0, "V")),
                impedance=Q_(10.0, "percent"),
                characteristic=Q_(5, "V/A"),
            ),
        ),
        GmawProcess(
            "spray",
            "CLOOS",
            "Quinto",
            dict(
                wire_feedrate=Q_(10, "m/min"),
                voltage=TimeSeries(Q_([40.0, 20.0], "V"), Q_([0.0, 10.0],
                                                             "s")),
                impedance=Q_(10.0, "percent"),
                characteristic=Q_(5, "V/A"),
            ),
예제 #17
0
def single_pass_weld_example(
    out_file: Optional[Union[str, BytesIO]] = "single_pass_weld_example.asdf",
) -> Optional[tuple[BytesIO, dict]]:
    """Create ASDF file containing all required fields of the single_pass_weld schema.

    Parameters
    ----------
    out_file :
        destination file, if None returns a BytesIO buffer.

    Returns
    -------
    buff, tree
        When writing to memory, return the buffer and the tree (as dictionary).

    """
    # Imports
    import asdf
    import numpy as np
    import pandas as pd
    from asdf.tags.core import Software

    import weldx.geometry as geo
    import weldx.measurement as msm

    # importing the weldx package with prevalent default abbreviations
    import weldx.transformations as tf
    from weldx.asdf.util import get_schema_path, write_buffer, write_read_buffer
    from weldx.constants import META_ATTR, Q_
    from weldx.core import MathematicalExpression, TimeSeries
    from weldx.tags.aws.process.gas_component import GasComponent
    from weldx.tags.aws.process.shielding_gas_for_procedure import (
        ShieldingGasForProcedure, )
    from weldx.tags.aws.process.shielding_gas_type import ShieldingGasType
    from weldx.tags.processes.process import GmawProcess
    from weldx.transformations.local_cs import LocalCoordinateSystem as lcs
    from weldx.transformations.rotation import WXRotation
    from weldx.welding.groove.iso_9692_1 import get_groove
    from weldx.welding.util import sine

    # Timestamp
    reference_timestamp = pd.Timestamp("2020-11-09 12:00:00")

    # Geometry
    # groove + trace = geometry
    groove = get_groove(
        groove_type="VGroove",
        workpiece_thickness=Q_(5, "mm"),
        groove_angle=Q_(50, "deg"),
        root_face=Q_(1, "mm"),
        root_gap=Q_(1, "mm"),
    )

    # define the weld seam length in mm
    seam_length = Q_(300, "mm")

    # create a linear trace segment a the complete weld seam trace
    trace_segment = geo.LinearHorizontalTraceSegment(seam_length)
    trace = geo.Trace(trace_segment)

    geometry = dict(groove_shape=groove, seam_length=seam_length)

    base_metal = dict(common_name="S355J2+N", standard="DIN EN 10225-2:2011")

    workpiece = dict(base_metal=base_metal, geometry=geometry)

    # Setup the Coordinate System Manager (CSM)
    # crete a new coordinate system manager with default base coordinate system
    csm = tf.CoordinateSystemManager("base")

    # add the workpiece coordinate system
    csm.add_cs(
        coordinate_system_name="workpiece",
        reference_system_name="base",
        lcs=trace.coordinate_system,
    )

    tcp_start_point = Q_([5.0, 0.0, 2.0], "mm")
    tcp_end_point = Q_([-5.0, 0.0, 2.0], "mm") + np.append(
        seam_length, Q_([0, 0], "mm"))

    v_weld = Q_(10, "mm/s")
    s_weld = (tcp_end_point - tcp_start_point)[0]  # length of the weld
    t_weld = s_weld / v_weld

    t_start = pd.Timedelta("0s")
    t_end = pd.Timedelta(str(t_weld.to_base_units()))

    rot = WXRotation.from_euler(seq="x", angles=180, degrees=True)

    coords = [tcp_start_point.magnitude, tcp_end_point.magnitude]

    tcp_wire = lcs(coordinates=coords, orientation=rot, time=[t_start, t_end])

    # add the workpiece coordinate system
    csm.add_cs(
        coordinate_system_name="tcp_wire",
        reference_system_name="workpiece",
        lcs=tcp_wire,
    )

    tcp_contact = lcs(coordinates=[0, 0, -10])

    # add the workpiece coordinate system
    csm.add_cs(
        coordinate_system_name="tcp_contact",
        reference_system_name="tcp_wire",
        lcs=tcp_contact,
    )

    TCP_reference = csm.get_cs("tcp_contact", "workpiece")

    # Measurements
    # time
    time = pd.timedelta_range(start="0s", end="10s", freq="2s")

    # current data
    I_ts = sine(f=Q_(10, "1/s"), amp=Q_(20, "A"), bias=Q_(300, "A"))
    current_data = TimeSeries(I_ts.interp_time(time).data, time)

    # voltage data
    U_ts = sine(f=Q_(10, "1/s"),
                amp=Q_(3, "V"),
                bias=Q_(40, "V"),
                phase=Q_(0.1, "rad"))
    voltage_data = TimeSeries(U_ts.interp_time(time).data, time)

    # define current source and transformations

    src_current = msm.SignalSource(
        name="Current Sensor",
        output_signal=msm.Signal(signal_type="analog", units="V", data=None),
        error=msm.Error(Q_(0.1, "percent")),
    )

    current_AD_transform = msm.SignalTransformation(
        name="AD conversion current measurement",
        error=msm.Error(Q_(0.01, "percent")),
        func=MathematicalExpression(
            "a * x + b", dict(a=Q_(32768.0 / 10.0, "1/V"), b=Q_(0.0, ""))),
        type_transformation="AD",
    )

    current_calib_transform = msm.SignalTransformation(
        name="Calibration current measurement",
        error=msm.Error(0.0),
        func=MathematicalExpression(
            "a * x + b", dict(a=Q_(1000.0 / 32768.0, "A"), b=Q_(0.0, "A"))),
    )

    # define voltage source and transformations

    src_voltage = msm.SignalSource(
        name="Voltage Sensor",
        output_signal=msm.Signal("analog", "V", data=None),
        error=msm.Error(Q_(0.1, "percent")),
    )

    voltage_AD_transform = msm.SignalTransformation(
        name="AD conversion voltage measurement",
        error=msm.Error(Q_(0.01, "percent")),
        func=MathematicalExpression(
            "a * x + b", dict(a=Q_(32768.0 / 10.0, "1/V"), b=Q_(0.0, ""))),
        type_transformation="AD",
    )

    voltage_calib_transform = msm.SignalTransformation(
        name="Calibration voltage measurement",
        error=msm.Error(0.0),
        func=MathematicalExpression(
            "a * x + b", dict(a=Q_(100.0 / 32768.0, "V"), b=Q_(0.0, "V"))),
    )

    # Define lab equipment

    HKS_sensor = msm.MeasurementEquipment(
        name="HKS P1000-S3",
        sources=[src_current, src_voltage],
    )
    BH_ELM = msm.MeasurementEquipment(
        name="Beckhoff ELM3002-0000",
        transformations=[current_AD_transform, voltage_AD_transform],
    )

    twincat_scope = Software(name="Beckhoff TwinCAT ScopeView",
                             version="3.4.3143")
    setattr(current_calib_transform, META_ATTR, dict(software=twincat_scope))
    setattr(voltage_calib_transform, META_ATTR, dict(software=twincat_scope))

    # Define current measurement chain

    welding_current_chain = msm.MeasurementChain.from_equipment(
        name="welding current measurement chain",
        equipment=HKS_sensor,
        source_name="Current Sensor",
    )
    welding_current_chain.add_transformation_from_equipment(
        equipment=BH_ELM,
        transformation_name="AD conversion current measurement",
    )
    welding_current_chain.add_transformation(
        transformation=current_calib_transform,
        data=current_data,
    )

    # Define voltage measurement chain

    welding_voltage_chain = msm.MeasurementChain.from_equipment(
        name="welding voltage measurement chain",
        equipment=HKS_sensor,
        source_name="Voltage Sensor",
    )
    welding_voltage_chain.add_transformation_from_equipment(
        equipment=BH_ELM,
        transformation_name="AD conversion voltage measurement",
    )
    welding_voltage_chain.add_transformation(
        transformation=voltage_calib_transform,
        data=voltage_data,
    )

    # Define measurements

    welding_current = msm.Measurement(
        name="welding current measurement",
        data=[current_data],
        measurement_chain=welding_current_chain,
    )

    welding_voltage = msm.Measurement(
        name="welding voltage measurement",
        data=[voltage_data],
        measurement_chain=welding_voltage_chain,
    )

    # GMAW Process
    params_pulse = dict(
        wire_feedrate=Q_(10.0, "m/min"),
        pulse_voltage=Q_(40.0, "V"),
        pulse_duration=Q_(5.0, "ms"),
        pulse_frequency=Q_(100.0, "Hz"),
        base_current=Q_(60.0, "A"),
    )
    process_pulse = GmawProcess(
        "pulse",
        "CLOOS",
        "Quinto",
        params_pulse,
        tag="CLOOS/pulse",
        meta={"modulation": "UI"},
    )

    gas_comp = [
        GasComponent("argon", Q_(82, "percent")),
        GasComponent("carbon dioxide", Q_(18, "percent")),
    ]
    gas_type = ShieldingGasType(gas_component=gas_comp, common_name="SG")

    gas_for_procedure = ShieldingGasForProcedure(
        use_torch_shielding_gas=True,
        torch_shielding_gas=gas_type,
        torch_shielding_gas_flowrate=Q_(20, "l / min"),
    )

    process = dict(
        welding_process=process_pulse,
        shielding_gas=gas_for_procedure,
        weld_speed=TimeSeries(v_weld),
        welding_wire={"diameter": Q_(1.2, "mm")},
    )

    # ASDF file
    tree = dict(
        reference_timestamp=reference_timestamp,
        equipment=[HKS_sensor, BH_ELM],
        measurements=[welding_current, welding_voltage],
        welding_current=welding_current_chain.get_signal(
            "Calibration current measurement").data,
        welding_voltage=welding_voltage_chain.get_signal(
            "Calibration voltage measurement").data,
        coordinate_systems=csm,
        TCP=TCP_reference,
        workpiece=workpiece,
        process=process,
    )
    tree[META_ATTR] = {"welder": "A.W. Elder"}

    model_path = get_schema_path("single_pass_weld-0.1.0.yaml")

    # pre-validate?
    write_read_buffer(
        tree,
        asdffile_kwargs=dict(custom_schema=str(model_path)),
    )

    if out_file:
        with asdf.AsdfFile(
                tree,
                custom_schema=str(model_path),
        ) as ff:
            ff.write_to(out_file, all_array_storage="inline")
    else:
        return (
            write_buffer(
                tree,
                asdffile_kwargs=dict(custom_schema=str(model_path)),
                write_kwargs=dict(all_array_storage="inline"),
            ),
            tree,
        )
예제 #18
0
 def test_construction_exceptions(
     data, time, interpolation, exception_type, test_name
 ):
     """Test the exceptions of the 'set_parameter' method."""
     with pytest.raises(exception_type):
         TimeSeries(data=data, time=time, interpolation=interpolation)
예제 #19
0
class TestTime:
    """Test the time class."""

    # test_init helper functions -------------------------------------------------------

    @staticmethod
    def _parse_time_type_test_input(
        type_input, ) -> tuple[Union[types_time_like, Time], bool]:
        """Return the time type and a bool that defines if the returned type is a delta.

        This is mainly used in generalized tests where a type like `Time` itself can
        represent deltas and absolute times. In this case one can use this function
        to extract the information from a tuple.

        """
        if isinstance(type_input, tuple):
            # to avoid wrong test setups due to spelling mistakes
            assert type_input[1] in ["timedelta", "datetime"]
            time_type = type_input[0]
            is_timedelta = type_input[1] == "timedelta"
        else:
            time_type = type_input
            is_timedelta = _is_timedelta(type_input)
        return time_type, is_timedelta

    @classmethod
    def _get_init_exp_values(
        cls,
        is_timedelta,
        time_ref,
        data_was_scalar,
        delta_val,
        abs_val,
    ):
        """Get the expected result values for the `__init__` test."""
        exp_is_absolute = time_ref is not None or not is_timedelta

        # expected reference time
        exp_time_ref = None
        if exp_is_absolute:
            exp_time_ref = Timestamp(
                time_ref if time_ref is not None else abs_val[0])

        # expected time delta values
        val = delta_val
        if exp_is_absolute:
            offset = 0
            if not is_timedelta:
                if time_ref is not None:
                    offset = Timestamp(abs_val[0]) - Timestamp(time_ref)
                    offset = offset.total_seconds()
                offset -= delta_val[0]

            val = [v + offset for v in delta_val]

        val = val[0] if data_was_scalar else val
        exp_timedelta = Timedelta(val, "s") if data_was_scalar else TDI(
            val, "s")

        # expected datetime
        exp_datetime = None
        if exp_is_absolute:
            time_ref = Timestamp(
                time_ref if time_ref is not None else abs_val[0])
            exp_datetime = time_ref + exp_timedelta

        return dict(
            is_absolute=exp_is_absolute,
            time_ref=exp_time_ref,
            timedelta=exp_timedelta,
            datetime=exp_datetime,
        )

    # test_init ------------------------------------------------------------------------

    @pytest.mark.parametrize("scl, arr", [(True, False), (True, True),
                                          (False, True)])
    @pytest.mark.parametrize("set_time_ref", [False, True])
    @pytest.mark.parametrize(
        "input_vals",
        [
            (str, "timedelta"),
            (Time, "timedelta"),
            (Q_, "timedelta"),
            TDI,
            Timedelta,
            np.timedelta64,
            (str, "datetime"),
            (Time, "datetime"),
            (Q_, "datetime"),
            DTI,
            Timestamp,
            np.datetime64,
        ],
    )
    def test_init(
        self,
        input_vals: Union[type, tuple[type, str]],
        set_time_ref: bool,
        scl: bool,
        arr: bool,
    ):
        """Test the `__init__` method of the time class.

        Parameters
        ----------
        input_vals :
            Either a compatible time type or a tuple of two values. The tuple is needed
            in case the tested time type can either represent relative time values as
            well as absolute ones. In this case, the first value is the type. The
            second value is a string specifying if the type represents absolute
            ("datetime") or relative ("timedelta") values.
        set_time_ref :
            If `True`, a reference time will be passed to the `__init__` method
        scl :
            If `True`, the data of the passed type consists of a single value.
        arr :
            If `True`, the data of the passed type is an array

        """
        input_type, is_timedelta = self._parse_time_type_test_input(input_vals)

        # skip matrix cases that do not work --------------------
        if arr and input_type in [Timedelta, Timestamp]:
            return
        if not arr and input_type in [DTI, TDI]:
            return

        # create input values -----------------------------------
        delta_val = [1, 2, 3]
        abs_val = [f"2000-01-01 16:00:0{v}" for v in delta_val]

        time = _initialize_time_type(input_type, delta_val, abs_val,
                                     is_timedelta, arr, scl)
        time_ref = "2000-01-01 15:00:00" if set_time_ref else None

        # create `Time` instance --------------------------------
        time_class_instance = Time(time, time_ref)

        # check results -----------------------------------------
        exp = self._get_init_exp_values(is_timedelta, time_ref, scl, delta_val,
                                        abs_val)

        assert time_class_instance.is_absolute == exp["is_absolute"]
        assert time_class_instance.reference_time == exp["time_ref"]
        assert np.all(time_class_instance.as_timedelta() == exp["timedelta"])
        if exp["is_absolute"]:
            assert np.all(time_class_instance.as_datetime() == exp["datetime"])
        else:
            with pytest.raises(TypeError):
                time_class_instance.as_datetime()

    # test_init_from_time_dependent_types ----------------------------------------------
    @staticmethod
    @pytest.mark.parametrize(
        "time_dep_type",
        [
            LocalCoordinateSystem(coordinates=np.zeros((2, 3)),
                                  time=["2s", "3s"]),
            LocalCoordinateSystem(coordinates=np.zeros((2, 3)),
                                  time=["2000", "2001"]),
            TimeSeries(Q_([2, 4, 1], "m"), TDI([1, 2, 3], "s")),
            TimeSeries(Q_([2, 4, 1], "m"), ["2001", "2002", "2003"]),
        ],
    )
    def test_init_from_time_dependent_types(time_dep_type):
        """Test initialization with types derived from `TimeDependent`."""
        t = Time(time_dep_type)
        assert np.all(t == time_dep_type.time)

    # test_init_exceptions -------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "time, time_ref, raises",
        [
            (TDI([3, 2, 1]), None, ValueError),
            (DTI(["2010", "2000"]), None, ValueError),
            (["2010", "2000"], None, ValueError),
            (Q_([3, 2, 1], "s"), None, ValueError),
            (np.array([3, 2, 1], dtype="timedelta64[s]"), None, ValueError),
            (None, None, TypeError),
            (5, None, TypeError),
            ("string", None, TypeError),
            (Q_(10, "m"), None, DimensionalityError),
        ],
    )
    def test_init_exception(time, time_ref, raises):
        """Test initialization of the `Time` class with all supported types."""
        with pytest.raises(raises):
            Time(time, time_ref)

    # test_add_timedelta ---------------------------------------------------------------

    @pytest.mark.parametrize("other_on_rhs", [True, False])
    @pytest.mark.parametrize("time_class_is_array", [False, True])
    @pytest.mark.parametrize("other_is_array", [False, True])
    @pytest.mark.parametrize("unit", ["s", "h"])
    @pytest.mark.parametrize(
        "other_type",
        [
            (str, "timedelta"),
            (Time, "timedelta"),
            (Q_, "timedelta"),
            TDI,
            Timedelta,
            np.timedelta64,
            (str, "datetime"),
            (Time, "datetime"),
            (Q_, "datetime"),
            DTI,
            Timestamp,
            np.datetime64,
        ],
    )
    def test_add_timedelta(
        self,
        other_type,
        other_on_rhs: bool,
        unit: str,
        time_class_is_array: bool,
        other_is_array: bool,
    ):
        """Test the `__add__` method if the `Time` class represents a time delta.

        Parameters
        ----------
        other_type :
            The type of the other object
        other_on_rhs :
            If `True`, the other type is on the rhs of the + sign and on the lhs
            otherwise
        unit :
            The time unit to use
        time_class_is_array :
            If `True`, the `Time` instance contains 3 time values and 1 otherwise
        other_is_array :
            If `True`, the other time object contains 3 time values and 1 otherwise

        """
        other_type, is_timedelta = self._parse_time_type_test_input(other_type)

        # skip array cases where the type does not support arrays
        if other_type in [Timedelta, Timestamp] and other_is_array:
            return
        if not other_is_array and other_type in [DTI, TDI]:
            return

        # skip __radd__ cases where we got conflicts with the other types' __add__
        if not other_on_rhs and other_type in (
                Q_,
                np.ndarray,
                np.timedelta64,
                np.datetime64,
                DTI,
                TDI,
        ):
            return

        # setup rhs
        delta_val = [4, 6, 8]
        if unit == "s":
            abs_val = [f"2000-01-01 10:00:0{v}" for v in delta_val]
        else:
            abs_val = [f"2000-01-01 1{v}:00:00" for v in delta_val]
        other = _initialize_time_type(
            other_type,
            delta_val,
            abs_val,
            is_timedelta,
            other_is_array,
            not other_is_array,
            unit,
        )

        # setup lhs
        time_class_values = [1, 2, 3] if time_class_is_array else [1]
        time_class = Time(Q_(time_class_values, unit))

        # setup expected values
        add = delta_val if other_is_array else delta_val[0]
        exp_val = np.array(time_class_values) + add
        exp_val += 0 if is_timedelta else time_class_values[0] - exp_val[0]

        exp_time_ref = None if is_timedelta else abs_val[0]
        exp = Time(Q_(exp_val, unit), exp_time_ref)

        # calculate and evaluate result
        res = time_class + other if other_on_rhs else other + time_class

        assert res.reference_time == exp.reference_time
        assert np.all(res.as_timedelta() == exp.as_timedelta())
        assert np.all(res == exp)

    # test_add_datetime ----------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize("other_on_rhs", [True, False])
    @pytest.mark.parametrize("time_class_is_array", [False, True])
    @pytest.mark.parametrize("other_is_array", [False, True])
    @pytest.mark.parametrize(
        "other_type",
        [
            str,
            Time,
            Q_,
            TDI,
            Timedelta,
            np.timedelta64,
        ],
    )
    def test_add_datetime(
        other_type,
        other_on_rhs: bool,
        time_class_is_array: bool,
        other_is_array: bool,
    ):
        """Test the `__add__` method if the `Time` class represents a datetime.

        Parameters
        ----------
        other_type :
            The type of the other object
        other_on_rhs :
            If `True`, the other type is on the rhs of the + sign and on the lhs
            otherwise
        time_class_is_array :
            If `True`, the `Time` instance contains 3 time values and 1 otherwise
        other_is_array :
            If `True`, the other time object contains 3 time values and 1 otherwise

        """
        # skip array cases where the type does not support arrays
        if other_type in [Timedelta, Timestamp] and other_is_array:
            return
        if not other_is_array and other_type in [DTI, TDI]:
            return

        # skip __radd__ cases where we got conflicts with the other types' __add__
        if not other_on_rhs and other_type in (Q_, np.ndarray, np.timedelta64,
                                               TDI):
            return

        # setup rhs
        delta_val = [4, 6, 8]

        other = _initialize_time_type(
            other_type,
            delta_val,
            None,
            True,
            other_is_array,
            not other_is_array,
            "s",
        )

        # setup lhs
        time_class_values = [1, 2, 3] if time_class_is_array else [1]
        time_class = Time(Q_(time_class_values, "s"), "2000-01-01 10:00:00")

        # setup expected values
        add = delta_val if other_is_array else delta_val[0]
        exp_val = np.array(time_class_values) + add

        exp_time_ref = time_class.reference_time
        exp = Time(Q_(exp_val, "s"), exp_time_ref)

        # calculate and evaluate result
        res = time_class + other if other_on_rhs else other + time_class

        assert res.reference_time == exp.reference_time
        assert np.all(res.as_timedelta() == exp.as_timedelta())
        assert np.all(res == exp)

    # test_sub -------------------------------------------------------------------------

    @staticmethod
    def _date_diff(date_1: str, date_2: str, unit: str) -> int:
        """Calculate the diff between two dates in the specified unit."""
        return int(
            Time(Timestamp(date_1) -
                 Timestamp(date_2)).as_quantity().m_as(unit))

    @pytest.mark.parametrize("other_on_rhs", [False, True])
    @pytest.mark.parametrize("time_class_is_array", [False, True])
    @pytest.mark.parametrize("other_is_array", [False, True])
    @pytest.mark.parametrize("unit", ["s", "h"])
    @pytest.mark.parametrize("time_class_is_timedelta", [False, True])
    @pytest.mark.parametrize(
        "other_type",
        [
            (str, "timedelta"),
            (Time, "timedelta"),
            (Q_, "timedelta"),
            TDI,
            Timedelta,
            np.timedelta64,
            (str, "datetime"),
            (Time, "datetime"),
            (Q_, "datetime"),
            DTI,
            Timestamp,
            np.datetime64,
        ],
    )
    def test_sub(
        self,
        other_type,
        other_on_rhs: bool,
        unit: str,
        time_class_is_array: bool,
        time_class_is_timedelta: bool,
        other_is_array: bool,
    ):
        """Test the `__sub__` method of the `Time` class.

        Parameters
        ----------
        other_type :
            The type of the other object
        other_on_rhs :
            If `True`, the other type is on the rhs of the + sign and on the lhs
            otherwise
        unit :
            The time unit to use
        time_class_is_array :
            If `True`, the `Time` instance contains 3 time values and 1 otherwise
        time_class_is_timedelta :
            If `True`, the `Time` instance represents a time delta and a datetime
            otherwise
        other_is_array :
            If `True`, the other time object contains 3 time values and 1 otherwise

        """
        other_type, other_is_timedelta = self._parse_time_type_test_input(
            other_type)
        if other_on_rhs:
            lhs_is_array = time_class_is_array
            lhs_is_timedelta = time_class_is_timedelta
            rhs_is_array = other_is_array
            rhs_is_timedelta = other_is_timedelta
        else:
            lhs_is_array = other_is_array
            lhs_is_timedelta = other_is_timedelta
            rhs_is_array = time_class_is_array
            rhs_is_timedelta = time_class_is_timedelta

        # skip array cases where the type does not support arrays or scalars
        if other_type in [Timedelta, Timestamp] and other_is_array:
            return
        if not other_is_array and other_type in [DTI, TDI]:
            return

        # skip __rsub__ cases where we got conflicts with the other types' __sub__
        if not other_on_rhs and other_type in (
                Q_,
                np.ndarray,
                np.timedelta64,
                np.datetime64,
                DTI,
                TDI,
        ):
            return

        # skip cases where an absolute time is on the rhs, since pandas does
        # not support this case (and it does not make sense)
        if lhs_is_timedelta and not rhs_is_timedelta:
            return

        # skip cases where the lhs is a scalar and the rhs is an array because it will
        # always involve non monotonically increasing array values, which is forbidden.
        if rhs_is_array and not lhs_is_array:
            return

        # test values
        vals_lhs = [3, 5, 9] if lhs_is_array else [3]
        vals_rhs = [1, 2, 3] if rhs_is_array else [1]

        # setup rhs
        other_val = vals_rhs if other_on_rhs else vals_lhs
        if unit == "s":
            abs_val = [f"2000-01-01 10:00:0{v}" for v in other_val]
        else:
            abs_val = [f"2000-01-01 1{v}:00:00" for v in other_val]
        other = _initialize_time_type(
            other_type,
            other_val,
            abs_val,
            other_is_timedelta,
            other_is_array,
            not other_is_array,
            unit,
        )

        # setup lhs
        time_class_values = vals_lhs if other_on_rhs else vals_rhs
        time_class_time_ref = None if time_class_is_timedelta else "2000-01-01 11:00:00"
        time_class = Time(Q_(time_class_values, unit), time_class_time_ref)

        # setup expected values
        sub = vals_rhs if other_is_array else vals_rhs[0]
        exp_val = np.array(vals_lhs) - sub
        if not other_is_timedelta:
            if time_class_is_timedelta:
                exp_val -= time_class_values[0] + exp_val[0]
            else:
                d = self._date_diff(time_class_time_ref, abs_val[0],
                                    unit) + vals_rhs[0]
                exp_val += d if other_on_rhs else (d + exp_val[0]) * -1

        exp_time_ref = None
        if not other_is_timedelta and time_class_is_timedelta:
            exp_time_ref = abs_val[0]
        elif other_is_timedelta and not time_class_is_timedelta:
            exp_time_ref = time_class_time_ref
        exp = Time(Q_(exp_val, unit), exp_time_ref)

        # calculate and evaluate result
        res = time_class - other if other_on_rhs else other - time_class

        assert res.reference_time == exp.reference_time
        assert np.all(res.as_timedelta() == exp.as_timedelta())
        assert np.all(res == exp)

    # test_pandas_index ----------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "arg, expected",
        [
            # timedeltas
            (TDI([42], unit="ns"), TDI([42], unit="ns")),
            (pd.timedelta_range("0s", "20s",
                                10), pd.timedelta_range("0s", "20s", 10)),
            (np.timedelta64(42), TDI([42], unit="ns")),
            (np.array([-10, 0, 20
                       ]).astype("timedelta64[ns]"), TDI([-10, 0, 20], "ns")),
            (Q_(42, "ns"), TDI([42], unit="ns")),
            ("10s", TDI(["10s"])),
            (["5ms", "10s", "2D"], TDI(["5 ms", "10s", "2D"])),
            # datetimes
            (np.datetime64(50, "Y"), DTI(["2020-01-01"])),
            ("2020-01-01", DTI(["2020-01-01"])),
            (
                np.array(["2012-10-02", "2012-10-05", "2012-10-11"],
                         dtype="datetime64[ns]"),
                DTI(["2012-10-02", "2012-10-05", "2012-10-11"]),
            ),
        ],
    )
    def test_pandas_index(arg, expected):
        """Test conversion to appropriate pd.TimedeltaIndex or pd.DatetimeIndex."""
        t = Time(arg)
        assert np.all(t.as_pandas_index() == expected)
        assert np.all(t.as_pandas_index() == t.index)
        assert np.all(t.as_timedelta_index() == t.timedelta)

    # test_as_quantity -----------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "arg, unit, expected",
        [
            ("1s", "s", 1),
            ("1s", "ms", 1000),
            ("1s", "us", 1000000),
            ("1s", "ns", 1000000000),
            (TDI([1, 2, 3], "s"), "s", [1, 2, 3]),
            (TDI([1, 2, 3], "s"), "ms", np.array([1, 2, 3]) * 1e3),
            (TDI([1, 2, 3], "s"), "us", np.array([1, 2, 3]) * 1e6),
            (TDI([1, 2, 3], "s"), "ns", np.array([1, 2, 3]) * 1e9),
            ("2020-01-01", "s", 0),
        ],
    )
    def test_quantity(arg, unit, expected):
        """Test conversion to pint.Quantity with different scales."""
        t = Time(arg)
        q = Time(arg).as_quantity(unit)
        expected = Q_(expected, unit)
        assert np.allclose(q, expected)
        if t.is_absolute:
            assert t.reference_time == q.time_ref
        if unit == "s":
            assert np.all(q == t.quantity)

    # test_convert_util ----------------------------------------------------------------

    @staticmethod
    def test_convert_util():
        """Test basic conversion functions from/to xarray/pint."""
        t = pd.date_range("2020", periods=10, freq="1s")
        ts = t[0]

        arr = xr.DataArray(
            np.arange(10),
            dims=["time"],
            coords={"time": t - ts},
        )
        arr.time.weldx.time_ref = ts
        time = Time(arr)

        assert len(time) == len(t)
        assert time.equals(Time(t))

        time_q = time.as_quantity()
        assert np.all(time_q == Q_(range(10), "s"))
        assert time_q.time_ref == ts

        arr2 = time.as_data_array()
        assert arr.time.identical(arr2.time)

    # test_duration --------------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "values, exp_duration",
        [
            ("1s", "0s"),
            ("2000-01-01", "0s"),
            (Q_([3, 5, 7, 8], "s"), "5s"),
            (["2000-01-03", "2000-01-05", "2000-01-07", "2000-01-08"
              ], "5days"),
        ],
    )
    def test_duration(values, exp_duration):
        """Test the duration property."""
        t = Time(values)
        assert t.duration.all_close(exp_duration)

    # test_resample --------------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "number_or_interval, exp_values",
        [
            # test clipping
            ("10s", [3, 9]),
            # test edge case
            (2, [3, 9]),
            ("6s", [3, 9]),
            # test even time deltas
            (4, [3, 5, 7, 9]),
            ("2s", [3, 5, 7, 9]),
            # test uneven time deltas
            ("5s", [3, 8, 9]),
            ("2.2s", [3, 5.2, 7.4, 9]),
        ],
    )
    def test_resample(number_or_interval, exp_values):
        """Test resample method."""
        t_ref = "2000-01-01"
        t_delta = Time(Q_([3, 5, 8, 9], "s"))
        t_abs = t_delta + t_ref

        exp_delta = Time(Q_(exp_values, "s"))
        exp_abs = exp_delta + t_ref

        result_delta = t_delta.resample(number_or_interval)
        result_abs = t_abs.resample(number_or_interval)

        assert result_delta.all_close(exp_delta)
        assert result_abs.all_close(exp_abs)

    # test_resample_exceptions ---------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "values,number_or_interval, raises",
        [
            ("4s", 2, RuntimeError),
            ("2000-02-01", 2, RuntimeError),
            (["4s", "10s"], "no time", TypeError),
            (["4s", "10s"], 1, ValueError),
            (["4s", "10s"], "0s", ValueError),
            (["4s", "10s"], "-2s", ValueError),
        ],
    )
    def test_resample_exceptions(values, number_or_interval, raises):
        """Test possible exceptions of the resample method."""
        with pytest.raises(raises):
            Time(values).resample(number_or_interval)

    # test_union -----------------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "list_of_objects, time_exp",
        [
            (
                [
                    date_range("2020-02-02", periods=4, freq="2D"),
                    date_range("2020-02-01", periods=4, freq="2D"),
                    date_range("2020-02-03", periods=2, freq="3D"),
                ],
                date_range("2020-02-01", periods=8, freq="1D"),
            ),
            ([TDI([1, 5]), TDI([2, 6, 7]),
              TDI([1, 3, 7])], TDI([1, 2, 3, 5, 6, 7])),
        ],
    )
    @pytest.mark.parametrize("test_instance", [True, False])
    def test_union(test_instance, list_of_objects, time_exp):
        """Test input types for Time.union function.

        Parameters
        ----------
        list_of_objects:
            List with input objects
        time_exp:
            Expected result time

        """
        if test_instance:
            instance = Time(list_of_objects[0])
            assert np.all(instance.union(list_of_objects[1:]) == time_exp)
        else:
            assert np.all(Time.union(list_of_objects) == time_exp)
예제 #20
0
class TestTimeSeries:
    """Tests for the TimeSeries class."""

    # Fixtures, aliases and shared variables -------------------------------------------

    ME = MathematicalExpression
    DTI = pd.DatetimeIndex
    TDI = pd.TimedeltaIndex
    TS = TimeSeries

    time_discrete = pd.TimedeltaIndex([0, 1, 2, 3, 4], unit="s")
    value_constant = Q_(1, "m")
    values_discrete = Q_(np.array([10, 11, 12, 14, 16]), "mm")
    me_expr_str = "a*t + b"
    me_params = {"a": Q_(2, "m/s"), "b": Q_(-2, "m")}

    me_params_vec = {"a": Q_([2, 0, 1], "m/s"), "b": Q_([-2, 3, 0], "m")}

    ts_constant = TimeSeries(value_constant)
    ts_disc_step = TimeSeries(values_discrete, time_discrete, "step")
    ts_disc_linear = TimeSeries(values_discrete, time_discrete, "linear")
    ts_expr = TimeSeries(ME(me_expr_str, me_params))
    ts_expr_vec = TimeSeries(ME(me_expr_str, me_params_vec))

    # test_construction_discrete -------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "data, time, interpolation, shape_exp",
        [
            (Q_(1, "m"), None, None, (1,)),
            (Q_([3, 7, 1], "m"), TDI([0, 1, 2], unit="s"), "step", (3,)),
            (Q_([3, 7, 1], ""), Q_([0, 1, 2], "s"), "step", (3,)),
        ],
    )
    def test_construction_discrete(data: pint.Quantity, time, interpolation, shape_exp):
        """Test the construction of the TimeSeries class."""
        # set expected values
        time_exp = time
        if isinstance(time_exp, pint.Quantity):
            time_exp = pd.TimedeltaIndex(time_exp.m, unit="s")

        exp_interpolation = interpolation
        if len(data.shape) == 0 and interpolation is None:
            exp_interpolation = "step"

        # create instance
        ts = TimeSeries(data=data, time=time, interpolation=interpolation)

        # check
        assert np.all(ts.data == data)
        assert np.all(ts.time == time_exp)
        assert ts.interpolation == exp_interpolation
        assert ts.shape == shape_exp
        assert data.is_compatible_with(ts.units)

        assert np.all(ts.data_array.data == data)
        assert ts.data_array.attrs["interpolation"] == exp_interpolation
        if time_exp is None:
            assert "time" not in ts.data_array
        else:
            assert np.all(ts.data_array.time == time_exp)

    # test_construction_expression -----------------------------------------------------

    params_scalar = {"a": Q_(2, "1/s"), "b": Q_(-2, "")}
    params_vec = {"a": Q_([[2, 3, 4]], "m/s"), "b": Q_([[-2, 3, 1]], "m")}

    @staticmethod
    @pytest.mark.parametrize(
        "data,  shape_exp, unit_exp",
        [
            (ME("a*t + b", params_scalar), (1,), ""),
            (ME("a*t + b", params_vec), (1, 3), "m"),
        ],
    )
    def test_construction_expression(data, shape_exp, unit_exp):
        """Test the construction of the TimeSeries class."""
        ts = TimeSeries(data=data)

        # check
        assert ts.data == data
        assert ts.time is None
        assert ts.interpolation is None
        assert ts.shape == shape_exp
        assert ts.data_array is None
        assert U_(unit_exp).is_compatible_with(ts.units)

    # test_init_data_array -------------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "data, dims, coords, exception_type",
        [
            (Q_([1, 2, 3], "m"), "time", dict(time=TDI([1, 2, 3])), None),
            (Q_([1, 2, 3], "m"), "a", dict(a=TDI([1, 2, 3])), KeyError),
            (Q_([[1, 2]], "m"), ("a", "time"), dict(a=[2], time=TDI([1, 2])), None),
            (Q_([1, 2, 3], "m"), "time", None, KeyError),
            (Q_([1, 2, 3], "m"), "time", dict(time=[1, 2, 3]), TypeError),
            ([1, 2, 3], "time", dict(time=TDI([1, 2, 3])), TypeError),
        ],
    )
    def test_init_data_array(data, dims, coords, exception_type):
        """Test the `__init__` method with an xarray as data parameter."""
        da = xr.DataArray(data=data, dims=dims, coords=coords)
        if exception_type is not None:
            with pytest.raises(exception_type):
                TimeSeries(da)
        else:
            ts = TimeSeries(da)
            assert ts.data_array.dims[0] == "time"

    # test_construction_exceptions -----------------------------------------------------

    values_def = Q_([5, 7, 3, 6, 8], "m")
    time_def = Q_([0, 1, 2, 3, 4], "s")
    me_too_many_vars = ME("a*t + b", {})
    me_param_units = ME("a*t + b", {"a": Q_(2, "1/s"), "b": Q_(-2, "m")})

    @staticmethod
    @pytest.mark.parametrize(
        "data, time, interpolation, exception_type, test_name",
        [
            (values_def, time_def, "int", ValueError, "# unknown interpolation"),
            (values_def, time_def.magnitude, "step", TypeError, "# invalid time type"),
            (me_too_many_vars, None, None, Exception, "# too many free variables"),
            (me_param_units, None, None, Exception, "# incompatible parameter units"),
            ("a string", None, None, TypeError, "# wrong data type"),
        ],
        ids=get_test_name,
    )
    def test_construction_exceptions(
        data, time, interpolation, exception_type, test_name
    ):
        """Test the exceptions of the 'set_parameter' method."""
        with pytest.raises(exception_type):
            TimeSeries(data=data, time=time, interpolation=interpolation)

    # test_comparison -------------------------------------

    time_wrong_values = TDI([0, 1, 2, 3, 5], unit="s")
    values_discrete_wrong = Q_(np.array([10, 11, 12, 15, 16]), "mm")
    values_unit_wrong = Q_(np.array([10, 11, 12, 14, 16]), "s")
    values_unit_prefix_wrong = Q_(np.array([10, 11, 12, 14, 16]), "m")
    params_wrong_values = {"a": Q_(2, "1/s"), "b": Q_(-1, "")}
    params_wrong_unit = {"a": Q_(2, "g/s"), "b": Q_(-2, "g")}
    params_wrong_unit_prefix = {"a": Q_(2, "m/ms"), "b": Q_(-2, "m")}

    @staticmethod
    @pytest.mark.parametrize(
        "ts, ts_other, result_exp",
        [
            (ts_constant, TS(value_constant), True),
            (ts_disc_step, TS(values_discrete, time_discrete, "step"), True),
            (ts_expr, TS(ME(me_expr_str, me_params)), True),
            (ts_constant, ts_disc_step, False),
            (ts_constant, ts_expr, False),
            (ts_disc_step, ts_expr, False),
            (ts_constant, 1, False),
            (ts_disc_step, 1, False),
            (ts_expr, 1, False),
            (ts_constant, "wrong", False),
            (ts_disc_step, "wrong", False),
            (ts_expr, "wrong", False),
            (ts_constant, TS(Q_(1337, "m")), False),
            (ts_constant, TS(Q_(1, "mm")), False),
            (ts_constant, TS(Q_(1, "s")), False),
            (ts_disc_step, TS(values_discrete, time_wrong_values, "step"), False),
            (ts_disc_step, TS(values_discrete_wrong, time_discrete, "step"), False),
            (ts_disc_step, TS(values_unit_prefix_wrong, time_discrete, "step"), False),
            (ts_disc_step, TS(values_discrete, time_discrete, "linear"), False),
            (ts_expr, TS(ME("a*t + 2*b", me_params)), False),
            (ts_expr, TS(ME(me_expr_str, params_wrong_values)), False),
            (ts_expr, TS(ME(me_expr_str, params_wrong_unit)), False),
            (ts_expr, TS(ME(me_expr_str, params_wrong_unit_prefix)), False),
        ],
    )
    def test_comparison(ts, ts_other, result_exp):
        """Test the TimeSeries comparison methods."""
        assert (ts == ts_other) is result_exp
        assert (ts != ts_other) is not result_exp

    # test_interp_time -----------------------------------------------------------------

    time_single = pd.TimedeltaIndex([2.1], "s")
    time_single_q = Q_(2.1, "s")
    time_mul = pd.TimedeltaIndex([-3, 0.7, 1.1, 1.9, 2.5, 3, 4, 7], "s")
    time_mul_q = Q_([-3, 0.7, 1.1, 1.9, 2.5, 3, 4, 7], "s")
    results_exp_vec = [
        [-8, 3, -3],
        [-0.6, 3, 0.7],
        [0.2, 3, 1.1],
        [1.8, 3, 1.9],
        [3, 3, 2.5],
        [4, 3, 3],
        [6, 3, 4],
        [12, 3, 7],
    ]

    @staticmethod
    @pytest.mark.parametrize(
        "ts, time, magnitude_exp, unit_exp",
        [
            (ts_constant, time_single, 1, "m"),
            (ts_constant, time_single_q, 1, "m"),
            (ts_constant, time_mul, [1, 1, 1, 1, 1, 1, 1, 1], "m"),
            (
                ts_constant,
                time_mul + pd.Timestamp("2020"),
                [1, 1, 1, 1, 1, 1, 1, 1],
                "m",
            ),
            (ts_constant, time_mul_q, [1, 1, 1, 1, 1, 1, 1, 1], "m"),
            (ts_disc_step, time_single, 12, "mm"),
            (ts_disc_step, time_single_q, 12, "mm"),
            (ts_disc_step, time_mul, [10, 10, 11, 11, 12, 14, 16, 16], "mm"),
            (ts_disc_step, time_mul_q, [10, 10, 11, 11, 12, 14, 16, 16], "mm"),
            (ts_disc_linear, time_single, 12.2, "mm"),
            (ts_disc_linear, time_single_q, 12.2, "mm"),
            (ts_disc_linear, time_mul, [10, 10.7, 11.1, 11.9, 13, 14, 16, 16], "mm"),
            (ts_disc_linear, time_mul_q, [10, 10.7, 11.1, 11.9, 13, 14, 16, 16], "mm"),
            (ts_expr, time_single, 2.2, "m"),
            (ts_expr, time_single_q, 2.2, "m"),
            (ts_expr, time_mul, [-8, -0.6, 0.2, 1.8, 3, 4, 6, 12], "m"),
            (ts_expr, time_mul_q, [-8, -0.6, 0.2, 1.8, 3, 4, 6, 12], "m"),
            (ts_expr_vec, time_single, [[2.2, 3, 2.1]], "m"),
            (ts_expr_vec, time_single_q, [[2.2, 3, 2.1]], "m"),
            (ts_expr_vec, time_mul, results_exp_vec, "m"),
        ],
    )
    def test_interp_time(ts, time, magnitude_exp, unit_exp):
        """Test the interp_time function."""
        result = ts.interp_time(time)

        assert np.all(np.isclose(result.data.magnitude, magnitude_exp))
        assert result.units == U_(unit_exp)

        time = Time(time)
        if len(time) == 1:
            assert result.time is None
        else:
            assert np.all(Time(result.time, result._reference_time) == time)

    # test_interp_time_warning ---------------------------------------------------------

    @staticmethod
    def test_interp_time_warning():
        """Test if a warning is emitted when interpolating already interpolated data."""
        ts = TimeSeries(data=Q_([1, 2, 3], "m"), time=Q_([0, 1, 2], "s"))
        with pytest.warns(None) as recorded_warnings:
            ts_interp = ts.interp_time(Q_([0.25, 0.5, 0.75, 1], "s"))
        assert not any(w.category == UserWarning for w in recorded_warnings)

        with pytest.warns(UserWarning):
            ts_interp.interp_time(Q_([0.4, 0.6], "s"))

    # test_interp_time_exceptions ------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize("ts", [ts_constant, ts_disc_step, ts_disc_linear, ts_expr])
    @pytest.mark.parametrize(
        "time,  exception_type, test_name",
        [
            # (DTI(["2010-10-10"]), ValueError, "# wrong type #1"),
            ("a string", TypeError, "# wrong type #2"),
            ([1, 2, 3], TypeError, "# wrong type #3"),
            (1, TypeError, "# wrong type #4"),
            (Q_(2, "s/m"), Exception, "# wrong type #5"),
        ],
        ids=get_test_name,
    )
    def test_interp_time_exceptions(ts, time, exception_type, test_name):
        """Test the exceptions of the 'set_parameter' method."""
        with pytest.raises(exception_type):
            ts.interp_time(time)
예제 #21
0
        sd = csm.get_data(data_name)
        sd_buffer = csm_buffer.get_data(data_name)
        assert sd == sd_buffer


# --------------------------------------------------------------------------------------
# TimeSeries
# --------------------------------------------------------------------------------------


@pytest.mark.parametrize("copy_arrays", [True, False])
@pytest.mark.parametrize("lazy_load", [True, False])
@pytest.mark.parametrize(
    "ts",
    [
        TimeSeries(Q_(42, "m")),
        TimeSeries(Q_(42.0, "m")),
        TimeSeries(Q_([42, 23, 12], "m"), time=pd.TimedeltaIndex([0, 2, 4])),
        TimeSeries(Q_([42, 23, 12], "m"), time=pd.TimedeltaIndex([0, 2, 5])),
        TimeSeries(ME("a*t+b", parameters={
            "a": Q_(2, "1/s"),
            "b": Q_(5, "")
        })),
        TimeSeries(
            Q_([1, 2, 3], "m"),
            time=pd.date_range(start="2020-01-01", freq="1D", periods=3),
        ),
        TimeSeries(
            Q_([1, 2, 3], "m"),
            time=pd.DatetimeIndex(["2020", "2021", "2024"]),
        ),
예제 #22
0
파일: test_core.py 프로젝트: vhirtham/weldx
class TestTimeSeries:
    """Tests for the TimeSeries class."""

    # Fixtures, aliases and shared variables -------------------------------------------

    ME = MathematicalExpression
    DTI = pd.DatetimeIndex
    TDI = pd.TimedeltaIndex
    TS = TimeSeries

    time_discrete = pd.TimedeltaIndex([0, 1, 2, 3, 4], unit="s")
    value_constant = Q_(1, "m")
    values_discrete = Q_(np.array([10, 11, 12, 14, 16]), "mm")
    me_expr_str = "a*t + b"
    me_params = {"a": Q_(2, "m/s"), "b": Q_(-2, "m")}

    me_params_vec = {"a": Q_([[2, 0, 1]], "m/s"), "b": Q_([[-2, 3, 0]], "m")}

    ts_constant = TimeSeries(value_constant)
    ts_disc_step = TimeSeries(values_discrete, time_discrete, "step")
    ts_disc_linear = TimeSeries(values_discrete, time_discrete, "linear")
    ts_expr = TimeSeries(ME(me_expr_str, me_params))
    ts_expr_vec = TimeSeries(ME(me_expr_str, me_params_vec))

    # test_construction_discrete -------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "data, time, interpolation, shape_exp",
        [
            (Q_(1, "m"), None, None, (1, )),
            (Q_([3, 7, 1], "m"), TDI([0, 1, 2], unit="s"), "step", (3, )),
            (Q_([3, 7, 1], ""), Q_([0, 1, 2], "s"), "step", (3, )),
        ],
    )
    def test_construction_discrete(data, time, interpolation, shape_exp):
        """Test the construction of the TimeSeries class."""
        # set expected values
        if isinstance(time, pint.Quantity):
            time_exp = pd.TimedeltaIndex(time.magnitude, unit="s")
        else:
            time_exp = time

        # create instance
        ts = TimeSeries(data=data, time=time, interpolation=interpolation)

        # check
        assert np.all(ts.data == data)
        assert np.all(ts.time == time_exp)
        assert ts.interpolation == interpolation
        assert ts.shape == shape_exp
        assert data.check(UREG.get_dimensionality(ts.units))

        assert np.all(ts.data_array.data == data)
        assert ts.data_array.attrs["interpolation"] == interpolation
        if time_exp is None:
            assert "time" not in ts.data_array
        else:
            assert np.all(ts.data_array.time == time_exp)

    # test_construction_expression -----------------------------------------------------

    params_scalar = {"a": Q_(2, "1/s"), "b": Q_(-2, "")}
    params_vec = {"a": Q_([[2, 3, 4]], "m/s"), "b": Q_([[-2, 3, 1]], "m")}

    @staticmethod
    @pytest.mark.parametrize(
        "data,  shape_exp, unit_exp",
        [
            (ME("a*t + b", params_scalar), (1, ), ""),
            (ME("a*t + b", params_vec), (1, 3), "m"),
        ],
    )
    def test_construction_expression(data, shape_exp, unit_exp):
        """Test the construction of the TimeSeries class."""
        ts = TimeSeries(data=data)

        # check
        assert ts.data == data
        assert ts.time is None
        assert ts.interpolation is None
        assert ts.shape == shape_exp
        assert ts.data_array is None
        assert Q_(1, unit_exp).check(UREG.get_dimensionality(ts.units))

    # test_construction_exceptions -----------------------------------------------------

    values_def = Q_([5, 7, 3, 6, 8], "m")
    time_def = Q_([0, 1, 2, 3, 4], "s")
    me_too_many_vars = ME("a*t + b", {})
    me_param_units = ME("a*t + b", {"a": Q_(2, "1/s"), "b": Q_(-2, "m")})
    me_time_vec = ME("a*t + b", {
        "a": Q_([2, 3, 4], "1/s"),
        "b": Q_([-2, 3, 1], "")
    })

    @staticmethod
    @pytest.mark.parametrize(
        "data, time, interpolation, exception_type, test_name",
        [
            (values_def, time_def, "int", ValueError,
             "# unknown interpolation"),
            (values_def, time_def, None, ValueError,
             "# wrong interp. parameter type"),
            (values_def, time_def.magnitude, "step", ValueError,
             "# invalid time type"),
            (me_too_many_vars, None, None, Exception,
             "# too many free variables"),
            (me_param_units, None, None, Exception,
             "# incompatible parameter units"),
            (me_time_vec, None, None, Exception,
             "# not compatible with time vectors"),
            ("a string", None, None, TypeError, "# wrong data type"),
        ],
        ids=get_test_name,
    )
    def test_construction_exceptions(data, time, interpolation, exception_type,
                                     test_name):
        """Test the exceptions of the 'set_parameter' method."""
        with pytest.raises(exception_type):
            TimeSeries(data=data, time=time, interpolation=interpolation)

    # test_comparison -------------------------------------

    time_wrong_values = TDI([0, 1, 2, 3, 5], unit="s")
    values_discrete_wrong = Q_(np.array([10, 11, 12, 15, 16]), "mm")
    values_unit_wrong = Q_(np.array([10, 11, 12, 14, 16]), "s")
    values_unit_prefix_wrong = Q_(np.array([10, 11, 12, 14, 16]), "m")
    params_wrong_values = {"a": Q_(2, "1/s"), "b": Q_(-1, "")}
    params_wrong_unit = {"a": Q_(2, "g/s"), "b": Q_(-2, "g")}
    params_wrong_unit_prefix = {"a": Q_(2, "m/ms"), "b": Q_(-2, "m")}

    @staticmethod
    @pytest.mark.parametrize(
        "ts, ts_other, result_exp",
        [
            (ts_constant, TS(value_constant), True),
            (ts_disc_step, TS(values_discrete, time_discrete, "step"), True),
            (ts_expr, TS(ME(me_expr_str, me_params)), True),
            (ts_constant, ts_disc_step, False),
            (ts_constant, ts_expr, False),
            (ts_disc_step, ts_expr, False),
            (ts_constant, 1, False),
            (ts_disc_step, 1, False),
            (ts_expr, 1, False),
            (ts_constant, "wrong", False),
            (ts_disc_step, "wrong", False),
            (ts_expr, "wrong", False),
            (ts_constant, TS(Q_(1337, "m")), False),
            (ts_constant, TS(Q_(1, "mm")), False),
            (ts_constant, TS(Q_(1, "s")), False),
            (ts_disc_step, TS(values_discrete, time_wrong_values,
                              "step"), False),
            (ts_disc_step, TS(values_discrete_wrong, time_discrete,
                              "step"), False),
            (ts_disc_step, TS(values_unit_prefix_wrong, time_discrete,
                              "step"), False),
            (ts_disc_step, TS(values_discrete, time_discrete,
                              "linear"), False),
            (ts_expr, TS(ME("a*t + 2*b", me_params)), False),
            (ts_expr, TS(ME(me_expr_str, params_wrong_values)), False),
            (ts_expr, TS(ME(me_expr_str, params_wrong_unit)), False),
            (ts_expr, TS(ME(me_expr_str, params_wrong_unit_prefix)), False),
        ],
    )
    def test_comparison(ts, ts_other, result_exp):
        """Test the TimeSeries comparison methods."""
        assert (ts == ts_other) is result_exp
        assert (ts != ts_other) is not result_exp

    # test_interp_time -----------------------------------------------------------------

    time_single = pd.TimedeltaIndex([2.1], "s")
    time_single_q = Q_(2.1, "s")
    time_mul = pd.TimedeltaIndex([-3, 0.7, 1.1, 1.9, 2.5, 3, 4, 7], "s")
    time_mul_q = Q_([-3, 0.7, 1.1, 1.9, 2.5, 3, 4, 7], "s")
    results_exp_vec = [
        [-8, 3, -3],
        [-0.6, 3, 0.7],
        [0.2, 3, 1.1],
        [1.8, 3, 1.9],
        [3, 3, 2.5],
        [4, 3, 3],
        [6, 3, 4],
        [12, 3, 7],
    ]

    @staticmethod
    @pytest.mark.parametrize(
        "ts, time, magnitude_exp, unit_exp",
        [
            (ts_constant, time_single, 1, "m"),
            (ts_constant, time_single_q, 1, "m"),
            (ts_constant, time_mul, [1, 1, 1, 1, 1, 1, 1, 1], "m"),
            (ts_constant, time_mul_q, [1, 1, 1, 1, 1, 1, 1, 1], "m"),
            (ts_disc_step, time_single, 12, "mm"),
            (ts_disc_step, time_single_q, 12, "mm"),
            (ts_disc_step, time_mul, [10, 10, 11, 11, 12, 14, 16, 16], "mm"),
            (ts_disc_step, time_mul_q, [10, 10, 11, 11, 12, 14, 16, 16], "mm"),
            (ts_disc_linear, time_single, 12.2, "mm"),
            (ts_disc_linear, time_single_q, 12.2, "mm"),
            (ts_disc_linear, time_mul, [10, 10.7, 11.1, 11.9, 13, 14, 16, 16
                                        ], "mm"),
            (ts_disc_linear, time_mul_q,
             [10, 10.7, 11.1, 11.9, 13, 14, 16, 16], "mm"),
            (ts_expr, time_single, 2.2, "m"),
            (ts_expr, time_single_q, 2.2, "m"),
            (ts_expr, time_mul, [-8, -0.6, 0.2, 1.8, 3, 4, 6, 12], "m"),
            (ts_expr, time_mul_q, [-8, -0.6, 0.2, 1.8, 3, 4, 6, 12], "m"),
            (ts_expr_vec, time_single, [[2.2, 3, 2.1]], "m"),
            (ts_expr_vec, time_single_q, [[2.2, 3, 2.1]], "m"),
            (ts_expr_vec, time_mul, results_exp_vec, "m"),
        ],
    )
    def test_interp_time(ts, time, magnitude_exp, unit_exp):
        """Test the interp_time function."""
        result = ts.interp_time(time)

        assert np.all(np.isclose(result.data.magnitude, magnitude_exp))
        assert Q_(1, str(result.data.units)) == Q_(1, unit_exp)

        if isinstance(time, pint.Quantity):
            assert np.all(result.time == ut.to_pandas_time_index(time))
        else:
            assert np.all(result.time == time)

    # test_interp_time_exceptions ------------------------------------------------------

    @staticmethod
    @pytest.mark.parametrize(
        "ts", [ts_constant, ts_disc_step, ts_disc_linear, ts_expr])
    @pytest.mark.parametrize(
        "time,  exception_type, test_name",
        [
            (DTI(["2010-10-10"]), ValueError, "# wrong type #1"),
            ("a string", ValueError, "# wrong type #2"),
            ([1, 2, 3], ValueError, "# wrong type #3"),
            (1, ValueError, "# wrong type #4"),
            (Q_(2, "s/m"), Exception, "# wrong type #5"),
        ],
        ids=get_test_name,
    )
    def test_interp_time_exceptions(ts, time, exception_type, test_name):
        """Test the exceptions of the 'set_parameter' method."""
        with pytest.raises(exception_type):
            ts.interp_time(time)
예제 #23
0
                              })
    csm_file = data["cs_hierarchy"]
    assert csm_root == csm_file


# --------------------------------------------------------------------------------------
# TimeSeries
# --------------------------------------------------------------------------------------


@pytest.mark.parametrize("copy_arrays", [True, False])
@pytest.mark.parametrize("lazy_load", [True, False])
@pytest.mark.parametrize(
    "ts",
    [
        TimeSeries(Q_(42, "m")),
        TimeSeries(Q_(42.0, "m")),
        TimeSeries(Q_([42, 23, 12], "m"), time=pd.TimedeltaIndex([0, 2, 4])),
        TimeSeries(Q_([42, 23, 12], "m"), time=pd.TimedeltaIndex([0, 2, 5])),
        TimeSeries(ME("a*t+b", parameters={
            "a": Q_(2, "1/s"),
            "b": Q_(5, "")
        })),
    ],
)
def test_time_series_discrete(ts, copy_arrays, lazy_load):
    ts_file = _write_read_buffer({"ts": ts},
                                 open_kwargs={
                                     "copy_arrays": copy_arrays,
                                     "lazy_load": lazy_load
                                 })["ts"]