|
13 | 13 | from ._cffi_api_util import CffiApiUtil |
14 | 14 | from .IDSS import IDSS |
15 | 15 | from .IBus import IBus |
| 16 | +from .IObj import GICLine |
16 | 17 | try: |
17 | 18 | import numpy as np |
18 | 19 | from matplotlib import pyplot as plt |
@@ -762,6 +763,43 @@ def dss_profile_plot(DSS, params): |
762 | 763 | ax2.autoscale_view() |
763 | 764 |
|
764 | 765 |
|
| 766 | + |
| 767 | +def get_gic_line_data(DSS: IDSS, bus_coords, single_ph_line_style=1, three_ph_line_style=1): |
| 768 | + branch_objects = DSS.Obj.GICLine |
| 769 | + line_count = len(branch_objects)# if not idxs else len(idxs) |
| 770 | + lines = np.empty(shape=(line_count, 2, 2), dtype=np.float64) |
| 771 | + lines.fill(np.nan) |
| 772 | + values = np.empty(shape=(line_count, ), dtype=np.float64) |
| 773 | + values.fill(np.nan) |
| 774 | + lines_styles = np.zeros(shape=(line_count,), dtype=np.int8) |
| 775 | + offset = 0 |
| 776 | + # skip = set() |
| 777 | + |
| 778 | + # GIC lines are not exposed nicely in the classic API, so we'll use the new Obj API |
| 779 | + gic_line: GICLine |
| 780 | + for gic_line in DSS.Obj.GICLine: |
| 781 | + if not gic_line.enabled: |
| 782 | + continue |
| 783 | + |
| 784 | + b1 = remove_nodes(gic_line.bus1) |
| 785 | + b2 = remove_nodes(gic_line.bus2) |
| 786 | + fr = bus_coords.get(b1) |
| 787 | + to = bus_coords.get(b2) |
| 788 | + |
| 789 | + if fr is None or to is None: |
| 790 | + # skip.add(idx) |
| 791 | + continue |
| 792 | + |
| 793 | + lines[offset, 0] = fr |
| 794 | + lines[offset, 1] = to |
| 795 | + |
| 796 | + lines_styles[offset] = single_ph_line_style if gic_line.phases == 1 else three_ph_line_style |
| 797 | + max_current = DSS._lib.Obj_CktElement_MaxCurrent(gic_line._ptr, 1) |
| 798 | + values[offset] = max_current |
| 799 | + offset += 1 |
| 800 | + |
| 801 | + return lines[:offset], values[:offset], lines_styles[:offset] |
| 802 | + |
765 | 803 | def dss_circuit_plot(DSS: IDSS, params={}, fig=None, ax=None, is3d=False): |
766 | 804 | quantity = str_to_pq.get(params.get('Quantity', None), pqNone) |
767 | 805 | dots = params.get('Dots', False) |
@@ -825,116 +863,131 @@ def dss_circuit_plot(DSS: IDSS, params={}, fig=None, ax=None, is3d=False): |
825 | 863 |
|
826 | 864 | quantity_suffix = '' |
827 | 865 |
|
828 | | - if quantity in (pqVoltage,): |
829 | | - colors = [] |
830 | | - for v in lines_values: |
831 | | - if v > norm_min_volts or np.isnan(v): |
832 | | - colors.append(color1) |
833 | | - elif v > emerg_min_volts: |
834 | | - colors.append(color2) |
835 | | - else: |
836 | | - colors.append(color3) |
837 | | - |
838 | | - |
839 | | - for ls in set(lines_styles): |
840 | | - line_idx = [i for i, c in enumerate(lines_styles) if c == ls and i not in isolated_idxs and i not in switch_idxs] |
841 | | - if not is3d: |
842 | | - edgecolors = [colors[i] for i in line_idx] |
843 | | - ax.add_collection(LineCollection(lines_lines[line_idx, :], linewidths=1, linestyle=LINES_STYLE_CODE.get(ls, 'solid'), color=edgecolors, capstyle='round')) |
844 | | - if dots: |
845 | | - ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=edgecolors, s=9, lw=1) |
846 | | - ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=edgecolors, s=9, lw=1) |
847 | | - |
848 | | - # if is3d: |
849 | | - # ax.add_collection(Line3DCollection(lines_lines, linewidths=1, linestyle='-', color=[colors[i] for i in line_idx], capstyle='round')) |
850 | | - # ax.set_xlim(np.min(lines_lines_3d[:, :, 0]), np.max(lines_lines_3d[:, :, 0])) |
851 | | - # ax.set_ylim(np.min(lines_lines_3d[:, :, 1]), np.max(lines_lines_3d[:, :, 1])) |
852 | | - |
853 | | - quantity_max_value = 0 |
854 | | - elif quantity in (pqLosses,): |
855 | | - |
856 | | - if quantity_max_value == 0: |
857 | | - # quantity_max_value = max(lines_values) * 1e-3 |
858 | | - # For compatibility with the official version, loop through all lines instead |
859 | | - # of the actual plotted lines |
860 | | - element = DSS.ActiveCircuit.ActiveCktElement |
861 | | - quantity_max_value = max( |
862 | | - abs(element.Losses[0] / line.Length) |
863 | | - for line in DSS.ActiveCircuit.Lines |
864 | | - if element.Enabled |
865 | | - ) * 0.001 |
866 | | - |
867 | | - lines_values = np.clip(3 * 1e-3 * lines_values / quantity_max_value, 0.5, max_lw) |
868 | | - if not is3d: |
| 866 | + if lines_lines is not None and len(lines_lines) > 0: |
| 867 | + if quantity in (pqVoltage,): |
| 868 | + colors = [] |
| 869 | + for v in lines_values: |
| 870 | + if v > norm_min_volts or np.isnan(v): |
| 871 | + colors.append(color1) |
| 872 | + elif v > emerg_min_volts: |
| 873 | + colors.append(color2) |
| 874 | + else: |
| 875 | + colors.append(color3) |
| 876 | + |
| 877 | + |
869 | 878 | for ls in set(lines_styles): |
870 | 879 | line_idx = [i for i, c in enumerate(lines_styles) if c == ls and i not in isolated_idxs and i not in switch_idxs] |
871 | | - # edgecolors = [colors[i] for i in line_idx] |
872 | | - ax.add_collection(LineCollection(lines_lines[line_idx, :], linewidths=lines_values[line_idx], linestyle=LINES_STYLE_CODE.get(ls, 'solid'), color=color1, capstyle='round')) |
873 | | - if dots: |
874 | | - ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
875 | | - ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
876 | | - |
877 | | - elif quantity in (pqCurrent, pqCapacity): |
878 | | - line_idx = [i for i in range(lines_lines.shape[0]) if i not in isolated_idxs and i not in switch_idxs] |
879 | | - colors = [color3 if v > 100 and not np.isnan(v) else color1 for v in lines_values[line_idx]] |
880 | | - |
881 | | - if quantity_max_value == 0: |
882 | | - quantity_max_value = max(lines_values) |
883 | | - |
884 | | - lines_values = np.clip(3 * lines_values / quantity_max_value, 0.5, max_lw) |
885 | | - if not is3d: |
886 | | - ax.add_collection(LineCollection(lines_lines[line_idx, :], linewidths=lines_values[line_idx], linestyle='-', color=colors, capstyle='round')) |
887 | | - if dots: |
888 | | - ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=colors, s=9, lw=1) |
889 | | - ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=colors, s=9, lw=1) |
890 | | - |
891 | | - elif quantity != pqNone: |
892 | | - if quantity == pqPower: |
893 | | - quantity_suffix = ' kW' |
| 880 | + if not is3d: |
| 881 | + edgecolors = [colors[i] for i in line_idx] |
| 882 | + ax.add_collection(LineCollection(lines_lines[line_idx, :], linewidths=1, linestyle=LINES_STYLE_CODE.get(ls, 'solid'), color=edgecolors, capstyle='round')) |
| 883 | + if dots: |
| 884 | + ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=edgecolors, s=9, lw=1) |
| 885 | + ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=edgecolors, s=9, lw=1) |
| 886 | + |
| 887 | + # if is3d: |
| 888 | + # ax.add_collection(Line3DCollection(lines_lines, linewidths=1, linestyle='-', color=[colors[i] for i in line_idx], capstyle='round')) |
| 889 | + # ax.set_xlim(np.min(lines_lines_3d[:, :, 0]), np.max(lines_lines_3d[:, :, 0])) |
| 890 | + # ax.set_ylim(np.min(lines_lines_3d[:, :, 1]), np.max(lines_lines_3d[:, :, 1])) |
| 891 | + |
| 892 | + quantity_max_value = 0 |
| 893 | + elif quantity in (pqLosses,): |
| 894 | + |
894 | 895 | if quantity_max_value == 0: |
895 | | - #lines_values *= 1e-3 |
896 | | - |
| 896 | + # quantity_max_value = max(lines_values) * 1e-3 |
897 | 897 | # For compatibility with the official version, loop through all lines instead |
898 | 898 | # of the actual plotted lines |
899 | 899 | element = DSS.ActiveCircuit.ActiveCktElement |
900 | | - |
901 | 900 | quantity_max_value = max( |
902 | | - element.TotalPowers[0] |
903 | | - for _ in DSS.ActiveCircuit.Lines |
| 901 | + abs(element.Losses[0] / line.Length) |
| 902 | + for line in DSS.ActiveCircuit.Lines |
904 | 903 | if element.Enabled |
905 | | - ) #* 0.001 |
906 | | - else: |
907 | | - #TODO:may need workaround about GeneralPlotQuantity |
908 | | - quantity_max_value = max(lines_values) |
| 904 | + ) * 0.001 |
909 | 905 |
|
910 | | - for ls in set(lines_styles): |
911 | | - line_idx = [i for i, c in enumerate(lines_styles) if c == ls and i not in isolated_idxs and i not in switch_idxs] |
| 906 | + lines_values = np.clip(3 * 1e-3 * lines_values / quantity_max_value, 0.5, max_lw) |
| 907 | + if not is3d: |
| 908 | + for ls in set(lines_styles): |
| 909 | + line_idx = [i for i, c in enumerate(lines_styles) if c == ls and i not in isolated_idxs and i not in switch_idxs] |
| 910 | + # edgecolors = [colors[i] for i in line_idx] |
| 911 | + ax.add_collection(LineCollection(lines_lines[line_idx, :], linewidths=lines_values[line_idx], linestyle=LINES_STYLE_CODE.get(ls, 'solid'), color=color1, capstyle='round')) |
| 912 | + if dots: |
| 913 | + ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
| 914 | + ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
| 915 | + |
| 916 | + elif quantity in (pqCurrent, pqCapacity): |
| 917 | + line_idx = [i for i in range(lines_lines.shape[0]) if i not in isolated_idxs and i not in switch_idxs] |
| 918 | + colors = [color3 if v > 100 and not np.isnan(v) else color1 for v in lines_values[line_idx]] |
| 919 | + |
| 920 | + if quantity_max_value == 0: |
| 921 | + quantity_max_value = max(lines_values) |
| 922 | + |
| 923 | + lines_values = np.clip(3 * lines_values / quantity_max_value, 0.5, max_lw) |
912 | 924 | if not is3d: |
913 | | - ax.add_collection(LineCollection( |
914 | | - lines_lines[line_idx, :], |
915 | | - linewidths=np.clip(0.5 + 3 * lines_values[line_idx] / quantity_max_value, 0.5, max_lw), |
916 | | - linestyle=LINES_STYLE_CODE.get(ls, 'solid'), |
917 | | - color=color1, |
918 | | - capstyle='round' |
919 | | - )) |
| 925 | + ax.add_collection(LineCollection(lines_lines[line_idx, :], linewidths=lines_values[line_idx], linestyle='-', color=colors, capstyle='round')) |
920 | 926 | if dots: |
921 | | - ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
922 | | - ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
923 | | - else: |
924 | | - #TODO: handle 1 and 3 phase, etc.? |
925 | | - if not is3d: |
926 | | - ax.add_collection(LineCollection(lines_lines, linewidths=1, linestyle='-', color=color1, capstyle='round')) |
927 | | - # else: |
928 | | - # ax.add_collection(Line3DCollection(lines_lines, linewidths=1, linestyle='-', color=color1, capstyle='round')) |
929 | | - # ax.set_xlim(np.min(lines_lines[:, :, 0]), np.max(lines_lines[:, :, 0])) |
930 | | - # ax.set_ylim(np.min(lines_lines[:, :, 1]), np.max(lines_lines[:, :, 1])) |
| 927 | + ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=colors, s=9, lw=1) |
| 928 | + ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=colors, s=9, lw=1) |
| 929 | + |
| 930 | + elif quantity != pqNone: |
| 931 | + if quantity == pqPower: |
| 932 | + quantity_suffix = ' kW' |
| 933 | + if quantity_max_value == 0: |
| 934 | + #lines_values *= 1e-3 |
| 935 | + |
| 936 | + # For compatibility with the official version, loop through all lines instead |
| 937 | + # of the actual plotted lines |
| 938 | + element = DSS.ActiveCircuit.ActiveCktElement |
| 939 | + |
| 940 | + quantity_max_value = max( |
| 941 | + element.TotalPowers[0] |
| 942 | + for _ in DSS.ActiveCircuit.Lines |
| 943 | + if element.Enabled |
| 944 | + ) #* 0.001 |
| 945 | + else: |
| 946 | + #TODO:may need workaround about GeneralPlotQuantity |
| 947 | + quantity_max_value = max(lines_values) |
| 948 | + |
| 949 | + for ls in set(lines_styles): |
| 950 | + line_idx = [i for i, c in enumerate(lines_styles) if c == ls and i not in isolated_idxs and i not in switch_idxs] |
| 951 | + if not is3d: |
| 952 | + ax.add_collection(LineCollection( |
| 953 | + lines_lines[line_idx, :], |
| 954 | + linewidths=np.clip(0.5 + 3 * lines_values[line_idx] / quantity_max_value, 0.5, max_lw), |
| 955 | + linestyle=LINES_STYLE_CODE.get(ls, 'solid'), |
| 956 | + color=color1, |
| 957 | + capstyle='round' |
| 958 | + )) |
| 959 | + if dots: |
| 960 | + ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
| 961 | + ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
| 962 | + else: |
| 963 | + #TODO: handle 1 and 3 phase, etc.? |
| 964 | + if not is3d: |
| 965 | + ax.add_collection(LineCollection(lines_lines, linewidths=1, linestyle='-', color=color1, capstyle='round')) |
| 966 | + # else: |
| 967 | + # ax.add_collection(Line3DCollection(lines_lines, linewidths=1, linestyle='-', color=color1, capstyle='round')) |
| 968 | + # ax.set_xlim(np.min(lines_lines[:, :, 0]), np.max(lines_lines[:, :, 0])) |
| 969 | + # ax.set_ylim(np.min(lines_lines[:, :, 1]), np.max(lines_lines[:, :, 1])) |
931 | 970 |
|
932 | 971 | transformers_lines, *_ = get_branch_data(DSS, DSS.ActiveCircuit.Transformers, bus_coords) |
933 | 972 |
|
934 | 973 | if not is3d: |
935 | 974 | lc_transformers = LineCollection(transformers_lines, linewidth=3, linestyle='solid', color='gray') |
936 | 975 | ax.add_collection(lc_transformers) |
937 | 976 |
|
| 977 | + lines_lines, lines_values, lines_styles, *_ = get_gic_line_data(DSS, bus_coords, single_ph_line_style=single_ph_line_style, three_ph_line_style=three_ph_line_style) |
| 978 | + if len(lines_lines) != 0: |
| 979 | + if quantity_max_value == 0: |
| 980 | + quantity_max_value = max(lines_values) |
| 981 | + |
| 982 | + lines_values = np.clip(3 * lines_values / quantity_max_value, 0.5, max_lw) |
| 983 | + for ls in set(lines_styles): |
| 984 | + line_idx = [i for i, c in enumerate(lines_styles) if c == ls] |
| 985 | + ax.add_collection(LineCollection(lines_lines[line_idx, :], linewidths=lines_values[line_idx], linestyle=LINES_STYLE_CODE.get(ls, 'solid'), color=color1, capstyle='round')) |
| 986 | + if dots: |
| 987 | + ax.scatter(lines_lines[line_idx, 0, 0].ravel(), lines_lines[line_idx, 0, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
| 988 | + ax.scatter(lines_lines[line_idx, 1, 0].ravel(), lines_lines[line_idx, 1, 1].ravel(), marker='o', facecolors='none', edgecolors=color1, s=9, lw=1) |
| 989 | + |
| 990 | + |
938 | 991 |
|
939 | 992 | # 'Daisysize' |
940 | 993 | # 'Markercode', 'Nodewidth' # NodeMarkerCode |
@@ -1802,12 +1855,10 @@ def dss_plot(DSS, params): |
1802 | 1855 | dss_plot_funcs.get(ptype)(DSS, params) |
1803 | 1856 |
|
1804 | 1857 | except Exception as ex: |
1805 | | - from traceback import print_exc |
| 1858 | + from traceback import format_exc |
1806 | 1859 | # print('DSS: Error while plotting. Parameters:', params, file=sys.stderr) |
1807 | | - # print_exc() |
1808 | | - |
1809 | 1860 | DSS._errorPtr[0] = 777 |
1810 | | - DSS._lib.Error_Set_Description(f"Error in the plot backend: {ex}".encode()) |
| 1861 | + DSS._lib.Error_Set_Description(f"Error in the plot backend: {ex}\n{format_exc()}".encode()) |
1811 | 1862 | return 777 |
1812 | 1863 |
|
1813 | 1864 | return 0 |
|
0 commit comments