|
10 | 10 | negate |
11 | 11 | feedback |
12 | 12 | connect |
| 13 | +combine_tf |
| 14 | +split_tf |
13 | 15 |
|
14 | 16 | """ |
15 | 17 |
|
|
63 | 65 | from .importxferfcnastf |
64 | 66 | from .iosysimportInputOutputSystem |
65 | 67 |
|
66 | | -__all__= ['series','parallel','negate','feedback','append','connect'] |
| 68 | +__all__= ['series','parallel','negate','feedback','append','connect', |
| 69 | +'combine_tf','split_tf'] |
67 | 70 |
|
68 | 71 |
|
69 | 72 | defseries(sys1,*sysn,**kwargs): |
@@ -507,3 +510,212 @@ def connect(sys, Q, inputv, outputv): |
507 | 510 | Ytrim[i,y-1]=1. |
508 | 511 |
|
509 | 512 | returnYtrim*sys*Utrim |
| 513 | + |
| 514 | +defcombine_tf(tf_array): |
| 515 | +"""Combine array-like of transfer functions into MIMO transfer function. |
| 516 | +
|
| 517 | + Parameters |
| 518 | + ---------- |
| 519 | + tf_array : list of list of TransferFunction or array_like |
| 520 | + Transfer matrix represented as a two-dimensional array or list-of-lists |
| 521 | + containing TransferFunction objects. The TransferFunction objects can |
| 522 | + have multiple outputs and inputs, as long as the dimensions are |
| 523 | + compatible. |
| 524 | +
|
| 525 | + Returns |
| 526 | + ------- |
| 527 | + TransferFunction |
| 528 | + Transfer matrix represented as a single MIMO TransferFunction object. |
| 529 | +
|
| 530 | + Raises |
| 531 | + ------ |
| 532 | + ValueError |
| 533 | + If timesteps of transfer functions do not match. |
| 534 | + ValueError |
| 535 | + If ``tf_array`` has incorrect dimensions. |
| 536 | + ValueError |
| 537 | + If the transfer functions in a row have mismatched output or input |
| 538 | + dimensions. |
| 539 | +
|
| 540 | + Examples |
| 541 | + -------- |
| 542 | + Combine two transfer functions |
| 543 | +
|
| 544 | + >>> s = control.TransferFunction.s |
| 545 | + >>> control.combine_tf([ |
| 546 | + ... [1 / (s + 1)], |
| 547 | + ... [s / (s + 2)], |
| 548 | + ... ]) |
| 549 | + TransferFunction([[array([1])], [array([1, 0])]], |
| 550 | + [[array([1, 1])], [array([1, 2])]]) |
| 551 | +
|
| 552 | + Combine NumPy arrays with transfer functions |
| 553 | +
|
| 554 | + >>> control.combine_tf([ |
| 555 | + ... [np.eye(2), np.zeros((2, 1))], |
| 556 | + ... [np.zeros((1, 2)), control.TransferFunction([1], [1, 0])], |
| 557 | + ... ]) |
| 558 | + TransferFunction([[array([1.]), array([0.]), array([0.])], |
| 559 | + [array([0.]), array([1.]), array([0.])], |
| 560 | + [array([0.]), array([0.]), array([1])]], |
| 561 | + [[array([1.]), array([1.]), array([1.])], |
| 562 | + [array([1.]), array([1.]), array([1.])], |
| 563 | + [array([1.]), array([1.]), array([1, 0])]]) |
| 564 | + """ |
| 565 | +# Find common timebase or raise error |
| 566 | +dt_list= [] |
| 567 | +try: |
| 568 | +forrowintf_array: |
| 569 | +fortfninrow: |
| 570 | +dt_list.append(getattr(tfn,"dt",None)) |
| 571 | +exceptOSError: |
| 572 | +raiseValueError("`tf_array` has too few dimensions.") |
| 573 | +dt_set=set(dt_list) |
| 574 | +dt_set.discard(None) |
| 575 | +iflen(dt_set)>1: |
| 576 | +raiseValueError("Timesteps of transfer functions are " |
| 577 | +f"mismatched:{dt_set}") |
| 578 | +eliflen(dt_set)==0: |
| 579 | +dt=None |
| 580 | +else: |
| 581 | +dt=dt_set.pop() |
| 582 | +# Convert all entries to transfer function objects |
| 583 | +ensured_tf_array= [] |
| 584 | +forrowintf_array: |
| 585 | +ensured_row= [] |
| 586 | +fortfninrow: |
| 587 | +ensured_row.append(_ensure_tf(tfn,dt)) |
| 588 | +ensured_tf_array.append(ensured_row) |
| 589 | +# Iterate over |
| 590 | +num= [] |
| 591 | +den= [] |
| 592 | +forrow_index,rowinenumerate(ensured_tf_array): |
| 593 | +forj_outinrange(row[0].noutputs): |
| 594 | +num_row= [] |
| 595 | +den_row= [] |
| 596 | +forcolinrow: |
| 597 | +ifcol.noutputs!=row[0].noutputs: |
| 598 | +raiseValueError( |
| 599 | +"Mismatched number of transfer function outputs in " |
| 600 | +f"row{row_index}." |
| 601 | + ) |
| 602 | +forj_ininrange(col.ninputs): |
| 603 | +num_row.append(col.num[j_out][j_in]) |
| 604 | +den_row.append(col.den[j_out][j_in]) |
| 605 | +num.append(num_row) |
| 606 | +den.append(den_row) |
| 607 | +forrow_index,rowinenumerate(num): |
| 608 | +iflen(row)!=len(num[0]): |
| 609 | +raiseValueError( |
| 610 | +"Mismatched number transfer function inputs in row " |
| 611 | +f"{row_index} of numerator." |
| 612 | + ) |
| 613 | +forrow_index,rowinenumerate(den): |
| 614 | +iflen(row)!=len(den[0]): |
| 615 | +raiseValueError( |
| 616 | +"Mismatched number transfer function inputs in row " |
| 617 | +f"{row_index} of denominator." |
| 618 | + ) |
| 619 | +returntf.TransferFunction(num,den,dt=dt) |
| 620 | + |
| 621 | +defsplit_tf(transfer_function): |
| 622 | +"""Split MIMO transfer function into NumPy array of SISO tranfer functions. |
| 623 | +
|
| 624 | + Parameters |
| 625 | + ---------- |
| 626 | + transfer_function : TransferFunction |
| 627 | + MIMO transfer function to split. |
| 628 | +
|
| 629 | + Returns |
| 630 | + ------- |
| 631 | + np.ndarray |
| 632 | + NumPy array of SISO transfer functions. |
| 633 | +
|
| 634 | + Examples |
| 635 | + -------- |
| 636 | + Split a MIMO transfer function |
| 637 | +
|
| 638 | + >>> G = control.TransferFunction( |
| 639 | + ... [ |
| 640 | + ... [[87.8], [-86.4]], |
| 641 | + ... [[108.2], [-109.6]], |
| 642 | + ... ], |
| 643 | + ... [ |
| 644 | + ... [[1, 1], [1, 1]], |
| 645 | + ... [[1, 1], [1, 1]], |
| 646 | + ... ], |
| 647 | + ... ) |
| 648 | + >>> control.split_tf(G) |
| 649 | + array([[TransferFunction(array([87.8]), array([1, 1])), |
| 650 | + TransferFunction(array([-86.4]), array([1, 1]))], |
| 651 | + [TransferFunction(array([108.2]), array([1, 1])), |
| 652 | + TransferFunction(array([-109.6]), array([1, 1]))]], dtype=object) |
| 653 | + """ |
| 654 | +tf_split_lst= [] |
| 655 | +fori_outinrange(transfer_function.noutputs): |
| 656 | +row= [] |
| 657 | +fori_ininrange(transfer_function.ninputs): |
| 658 | +row.append( |
| 659 | +tf.TransferFunction( |
| 660 | +transfer_function.num[i_out][i_in], |
| 661 | +transfer_function.den[i_out][i_in], |
| 662 | +dt=transfer_function.dt, |
| 663 | + ) |
| 664 | + ) |
| 665 | +tf_split_lst.append(row) |
| 666 | +returnnp.array(tf_split_lst,dtype=object) |
| 667 | + |
| 668 | +def_ensure_tf(arraylike_or_tf,dt=None): |
| 669 | +"""Convert an array-like to a transfer function. |
| 670 | +
|
| 671 | + Parameters |
| 672 | + ---------- |
| 673 | + arraylike_or_tf : TransferFunction or array_like |
| 674 | + Array-like or transfer function. |
| 675 | + dt : None, True or float, optional |
| 676 | + System timebase. 0 (default) indicates continuous |
| 677 | + time, True indicates discrete time with unspecified sampling |
| 678 | + time, positive number is discrete time with specified |
| 679 | + sampling time, None indicates unspecified timebase (either |
| 680 | + continuous or discrete time). If None, timestep is not validated. |
| 681 | +
|
| 682 | + Returns |
| 683 | + ------- |
| 684 | + TransferFunction |
| 685 | + Transfer function. |
| 686 | +
|
| 687 | + Raises |
| 688 | + ------ |
| 689 | + ValueError |
| 690 | + If input cannot be converted to a transfer function. |
| 691 | + ValueError |
| 692 | + If the timesteps do not match. |
| 693 | + """ |
| 694 | +# If the input is already a transfer function, return it right away |
| 695 | +ifisinstance(arraylike_or_tf,tf.TransferFunction): |
| 696 | +# If timesteps don't match, raise an exception |
| 697 | +if (dtisnotNone)and (arraylike_or_tf.dt!=dt): |
| 698 | +raiseValueError( |
| 699 | +f"`arraylike_or_tf.dt={arraylike_or_tf.dt}` does not match " |
| 700 | +f"argument `dt={dt}`." |
| 701 | + ) |
| 702 | +returnarraylike_or_tf |
| 703 | +ifnp.ndim(arraylike_or_tf)>2: |
| 704 | +raiseValueError( |
| 705 | +"Array-like must have less than two dimensions to be converted " |
| 706 | +"into a transfer function." |
| 707 | + ) |
| 708 | +# If it's not, then convert it to a transfer function |
| 709 | +arraylike_3d=np.atleast_3d(arraylike_or_tf) |
| 710 | +try: |
| 711 | +tfn=tf.TransferFunction( |
| 712 | +arraylike_3d, |
| 713 | +np.ones_like(arraylike_3d), |
| 714 | +dt, |
| 715 | + ) |
| 716 | +exceptTypeError: |
| 717 | +raiseValueError( |
| 718 | +"`arraylike_or_tf` must only contain array-likes or transfer " |
| 719 | +"functions." |
| 720 | + ) |
| 721 | +returntfn |