diff --git a/_unittest/test_45_FilterSolutions/__init__.py b/_unittest/test_45_FilterSolutions/__init__.py new file mode 100644 index 00000000000..9c4476773da --- /dev/null +++ b/_unittest/test_45_FilterSolutions/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. diff --git a/_unittest/test_45_FilterSolutions/resources/__init__.py b/_unittest/test_45_FilterSolutions/resources/__init__.py new file mode 100644 index 00000000000..d2b2bdb76b9 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from .resources import read_resource_file diff --git a/_unittest/test_45_FilterSolutions/resources/bridge_t.ckt b/_unittest/test_45_FilterSolutions/resources/bridge_t.ckt new file mode 100644 index 00000000000..9a692c73fd1 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/bridge_t.ckt @@ -0,0 +1,27 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C2 2 3 7.12E-12 +C3 3 0 3.873E-12 +C4 3 4 1.233E-11 +Rq4 3 4 5E+10 +L1 2 4 4.424E-09 +C6 4 5 3.916E-12 +Rq6 4 5 5E+10 +C7 5 0 9.429E-12 +C8 5 6 4.684E-12 +Rq8 5 6 5E+10 +L5 4 6 8.388E-09 +R9 6 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) -70 0 +.PLOT AC VP(6) -200 200 +.PLOT AC VG(6) 0 2.5E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/bridge_t_high.ckt b/_unittest/test_45_FilterSolutions/resources/bridge_t_high.ckt new file mode 100644 index 00000000000..a45cb02ca4e --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/bridge_t_high.ckt @@ -0,0 +1,33 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L2 2 3 8.17E-09 +L3 3 4 8.595E-09 +C3 4 0 2.366E-12 +L4 3 5 8.123E-09 +L5 5 6 4.129E-09 +C5 6 0 2.523E-12 +L6 5 7 1.459E-09 +R7 7 0 50 +C9 2 8 7.438E-13 +L10 8 0 1.071E-08 +C11 8 9 1.496E-12 +C8 2 9 1.574E-12 +C13 9 10 1.287E-12 +L14 10 0 1.004E-08 +C15 10 11 3.582E-12 +C12 9 11 3.642E-12 +R16 11 0 50 +* +* Compensation Elements +* +L1 2 12 2.28E-07 +C1 12 0 1.111E-13 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(7) VDB(11) -200 0 +.PLOT AC VP(7) VP(11) -200 200 +.PLOT AC VG(7) VG(11) 0 1.8E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(7) V(11) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/bridge_t_low.ckt b/_unittest/test_45_FilterSolutions/resources/bridge_t_low.ckt new file mode 100644 index 00000000000..b7126ceba00 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/bridge_t_low.ckt @@ -0,0 +1,39 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L3 2 3 3.406E-08 +C4 3 0 2.366E-12 +L5 3 4 1.693E-08 +L2 2 4 1.609E-08 +L7 4 5 1.969E-08 +C8 5 0 2.523E-12 +L9 5 6 7.071E-09 +L6 4 6 6.955E-09 +R10 6 0 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C11 2 7 3.1E-12 +L12 7 8 1.071E-08 +C12 8 0 2.947E-12 +C13 7 9 3.118E-12 +Rq13 7 9 5E+10 +L14 9 10 1.004E-08 +C14 10 0 6.135E-12 +C15 9 11 1.736E-11 +Rq15 9 11 5E+10 +R16 11 0 50 +* +* Compensation Elements +* +L1 2 12 2.28E-07 +C1 12 0 1.111E-13 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) VDB(11) -200 0 +.PLOT AC VP(6) VP(11) -200 200 +.PLOT AC VG(6) VG(11) 0 1.8E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) V(11) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/c_lead_inductor.ckt b/_unittest/test_45_FilterSolutions/resources/c_lead_inductor.ckt new file mode 100644 index 00000000000..0494a776955 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/c_lead_inductor.ckt @@ -0,0 +1,23 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +Ln1C1 2 2517 1E-09 +C1 2517 2519 1.967E-12 +Ln2C1 2519 0 1E-09 +L2 2 3 1.288E-08 +Ln1C3 3 3517 1E-09 +C3 3517 3519 6.366E-12 +Ln2C3 3519 0 1E-09 +L4 3 4 1.288E-08 +Ln1C5 4 4517 1E-09 +C5 4517 4519 1.967E-12 +Ln2C5 4519 0 1E-09 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -140 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 1.2E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) -0.1 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/c_node_capacitor.ckt b/_unittest/test_45_FilterSolutions/resources/c_node_capacitor.ckt new file mode 100644 index 00000000000..d438dd52aee --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/c_node_capacitor.ckt @@ -0,0 +1,20 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +Cn1C1 2 0 1E-09 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +Cn1C3 3 0 1E-09 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +Cn1C5 4 0 1E-09 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -200 -50 +.PLOT AC VP(4) -90 0 +.PLOT AC VG(4) 0 3E-11 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) -0.1 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/c_node_compensate.ckt b/_unittest/test_45_FilterSolutions/resources/c_node_compensate.ckt new file mode 100644 index 00000000000..341985a9476 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/c_node_compensate.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/capacitor_ls.ckt b/_unittest/test_45_FilterSolutions/resources/capacitor_ls.ckt new file mode 100644 index 00000000000..ee03e5d60bb --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/capacitor_ls.ckt @@ -0,0 +1,20 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 3 1.967E-12 +LXcs1 3 0 1E-09 +L2 2 4 1.288E-08 +C3 4 5 6.366E-12 +LXcs3 5 0 1E-09 +L4 4 6 1.288E-08 +C5 6 7 1.967E-12 +LXcs5 7 0 1E-09 +R6 6 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) -160 0 +.PLOT AC VP(6) -200 200 +.PLOT AC VG(6) 0 1E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/capacitor_q.ckt b/_unittest/test_45_FilterSolutions/resources/capacitor_q.ckt new file mode 100644 index 00000000000..2a7355017b6 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/capacitor_q.ckt @@ -0,0 +1,20 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +Rq1 2 0 8090 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +Rq3 3 0 2500 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +Rq5 4 0 8090 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/capacitor_rp.ckt b/_unittest/test_45_FilterSolutions/resources/capacitor_rp.ckt new file mode 100644 index 00000000000..2eead0f6a32 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/capacitor_rp.ckt @@ -0,0 +1,20 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +Rp1 2 0 1000 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +Rp3 3 0 1000 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +Rp5 4 0 1000 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/capacitor_rs.ckt b/_unittest/test_45_FilterSolutions/resources/capacitor_rs.ckt new file mode 100644 index 00000000000..c7bc0d4e4ef --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/capacitor_rs.ckt @@ -0,0 +1,20 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 3 1.967E-12 +Rs1 3 0 1 +L2 2 4 1.288E-08 +C3 4 5 6.366E-12 +Rs3 5 0 1 +L4 4 6 1.288E-08 +C5 6 7 1.967E-12 +Rs5 7 0 1 +R6 6 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) -80 0 +.PLOT AC VP(6) -200 200 +.PLOT AC VG(6) 0 8E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/complex.ckt b/_unittest/test_45_FilterSolutions/resources/complex.ckt new file mode 100644 index 00000000000..516f71d6897 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/complex.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 {TABLE(F, 1E+08, 1, 1E+09, 1, 1E+10, 1) + J*TABLE(F, 1E+08, 0, 1E+09, 0, 1E+10, 0)} +C1 2 0 9.836E-11 +L2 2 3 2.575E-10 +C3 3 0 3.183E-10 +L4 3 4 2.575E-10 +C5 4 0 9.836E-11 +R6 4 0 {TABLE(F, 1E+08, 1, 1E+09, 1, 1E+10, 1) + J*TABLE(F, 1E+08, 0, 1E+09, 0, 1E+10, 0)} +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/current_source.ckt b/_unittest/test_45_FilterSolutions/resources/current_source.ckt new file mode 100644 index 00000000000..0ff573d7e52 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/current_source.ckt @@ -0,0 +1,17 @@ +* +I1 1 0 AC 1 PULSE 0 0.04 0 1.592E-13 0 +R0 1 0 50 +C1 1 0 1.967E-12 +L2 1 2 1.288E-08 +C3 2 0 6.366E-12 +L4 2 3 1.288E-08 +C5 3 0 1.967E-12 +R6 3 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC IDB(R6) -80 0 +.PLOT AC IP(R6) -200 200 +.PLOT AC VG(R6) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN I(R6) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_1.ckt b/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_1.ckt new file mode 100644 index 00000000000..7d071286181 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_1.ckt @@ -0,0 +1,42 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L2 2 3 3.173E-08 +C2 3 0 1.017E-11 +C3 2 4 2.697E-12 +L4 4 5 2.505E-08 +L5 5 0 6.149E-09 +C6 5 0 1.099E-11 +C7 5 6 3.016E-12 +L8 6 7 2.241E-08 +L9 7 0 1.165E-08 +C10 7 0 5.801E-12 +C11 7 8 1.349E-11 +L12 8 9 5.011E-09 +R13 9 0 50 +L14 2 10 2.492E-09 +C14 10 0 7.983E-13 +C15 2 11 1.011E-12 +L16 11 12 9.391E-09 +L17 12 0 2.305E-09 +C18 12 0 4.12E-12 +C19 12 13 1.13E-12 +L20 13 14 8.4E-09 +L21 14 0 4.366E-09 +C22 14 0 2.175E-12 +C23 14 15 5.055E-12 +L24 15 16 1.878E-09 +R25 16 0 50 +* +* Compensation Elements +* +L1 2 17 8.356E-08 +C1 17 0 3.031E-13 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) VDB(16) -200 0 +.PLOT AC VP(9) VP(16) -200 200 +.PLOT AC VG(9) VG(16) -1E-09 7E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) V(16) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_2.ckt b/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_2.ckt new file mode 100644 index 00000000000..ba392bebff7 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_2.ckt @@ -0,0 +1,42 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L2 2 3 3.998E-08 +C2 3 0 1.737E-11 +C3 2 4 4.12E-12 +L4 4 5 2.459E-08 +L5 5 0 9.393E-09 +C6 5 0 1.079E-11 +C7 5 6 4.607E-12 +L8 6 7 2.199E-08 +L9 7 0 1.779E-08 +C10 7 0 5.694E-12 +C11 7 8 2.06E-11 +L12 8 9 4.918E-09 +R13 9 0 50 +L14 2 10 1.458E-09 +C14 10 0 6.335E-13 +C15 2 11 1.03E-12 +L16 11 12 6.148E-09 +L17 12 0 2.348E-09 +C18 12 0 2.697E-12 +C19 12 13 1.152E-12 +L20 13 14 5.499E-09 +L21 14 0 4.449E-09 +C22 14 0 1.424E-12 +C23 14 15 5.15E-12 +L24 15 16 1.23E-09 +R25 16 0 50 +* +* Compensation Elements +* +L1 2 17 2.885E-08 +C1 17 0 8.78E-13 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) VDB(16) -200 0 +.PLOT AC VP(9) VP(16) -200 200 +.PLOT AC VG(9) VG(16) -1E-09 8E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) V(16) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_bs.ckt b/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_bs.ckt new file mode 100644 index 00000000000..70285e884ec --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/diplexer1_bp_bs.ckt @@ -0,0 +1,40 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C3 2 3 4.12E-13 +L4 3 4 6.148E-08 +L5 4 0 9.393E-10 +C6 4 0 2.697E-11 +C7 4 5 4.607E-13 +L8 5 6 5.499E-08 +L9 6 0 1.779E-09 +C10 6 0 1.424E-11 +C11 6 7 2.06E-12 +L12 7 8 1.23E-08 +R13 8 0 50 +L14 2 9 2.459E-08 +C14 2 9 1.03E-12 +L15 9 10 2.348E-09 +C15 10 0 1.079E-11 +L16 9 11 2.199E-08 +C16 9 11 1.152E-12 +L17 11 12 4.449E-09 +C17 12 0 5.694E-12 +L18 11 13 4.918E-09 +C18 11 13 5.15E-12 +R19 13 0 50 +* +* Compensation Elements +* +L1 2 14 1.745E-08 +C1 14 0 3.873E-12 +L2 2 15 6.54E-09 +C2 15 0 1.452E-12 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(8) VDB(13) -200 0 +.PLOT AC VP(8) VP(13) -200 200 +.PLOT AC VG(8) VG(13) -1E-09 9E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(8) V(13) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/diplexer1_hi_lo.ckt b/_unittest/test_45_FilterSolutions/resources/diplexer1_hi_lo.ckt new file mode 100644 index 00000000000..faf4df142de --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/diplexer1_hi_lo.ckt @@ -0,0 +1,28 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L2 2 3 1.359E-08 +C3 3 0 5.96E-12 +L4 3 4 1.215E-08 +C5 4 0 3.146E-12 +L6 4 5 2.717E-09 +R7 5 0 50 +C8 2 6 1.864E-12 +L9 6 0 4.25E-09 +C10 6 7 2.084E-12 +L11 7 0 8.052E-09 +C12 7 8 9.322E-12 +R13 8 0 50 +* +* Compensation Elements +* +L1 2 9 2.041E-07 +C1 9 0 1.241E-13 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(5) VDB(8) -200 0 +.PLOT AC VP(5) VP(8) -200 200 +.PLOT AC VG(5) VG(8) 0 1.4E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(5) V(8) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/diplexer2_bp_bs.ckt b/_unittest/test_45_FilterSolutions/resources/diplexer2_bp_bs.ckt new file mode 100644 index 00000000000..70285e884ec --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/diplexer2_bp_bs.ckt @@ -0,0 +1,40 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C3 2 3 4.12E-13 +L4 3 4 6.148E-08 +L5 4 0 9.393E-10 +C6 4 0 2.697E-11 +C7 4 5 4.607E-13 +L8 5 6 5.499E-08 +L9 6 0 1.779E-09 +C10 6 0 1.424E-11 +C11 6 7 2.06E-12 +L12 7 8 1.23E-08 +R13 8 0 50 +L14 2 9 2.459E-08 +C14 2 9 1.03E-12 +L15 9 10 2.348E-09 +C15 10 0 1.079E-11 +L16 9 11 2.199E-08 +C16 9 11 1.152E-12 +L17 11 12 4.449E-09 +C17 12 0 5.694E-12 +L18 11 13 4.918E-09 +C18 11 13 5.15E-12 +R19 13 0 50 +* +* Compensation Elements +* +L1 2 14 1.745E-08 +C1 14 0 3.873E-12 +L2 2 15 6.54E-09 +C2 15 0 1.452E-12 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(8) VDB(13) -200 0 +.PLOT AC VP(8) VP(13) -200 200 +.PLOT AC VG(8) VG(13) -1E-09 9E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(8) V(13) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/diplexer2_triplexer_1.ckt b/_unittest/test_45_FilterSolutions/resources/diplexer2_triplexer_1.ckt new file mode 100644 index 00000000000..5102c593558 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/diplexer2_triplexer_1.ckt @@ -0,0 +1,41 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L3 2 3 2.968E-08 +C4 3 0 1.302E-11 +L5 3 4 2.655E-08 +C6 4 0 6.873E-12 +L7 4 5 5.937E-09 +R8 5 0 50 +C9 2 6 8.533E-13 +L10 6 0 1.945E-09 +C11 6 7 9.541E-13 +L12 7 0 3.685E-09 +C13 7 8 4.267E-12 +R14 8 0 50 +C15 2 9 4.12E-13 +L16 9 10 6.148E-08 +L17 10 0 9.393E-10 +C18 10 0 2.697E-11 +C19 10 11 4.607E-13 +L20 11 12 5.499E-08 +L21 12 0 1.779E-09 +C22 12 0 1.424E-11 +C23 12 13 2.06E-12 +L24 13 14 1.23E-08 +R25 14 0 50 +* +* Compensation Elements +* +L1 2 15 1.86E-08 +C1 15 0 3.633E-12 +L2 2 16 6.972E-09 +C2 16 0 1.362E-12 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(5) VDB(8) VDB(14) -160 0 +.PLOT AC VP(5) VP(8) VP(14) -200 200 +.PLOT AC VG(5) VG(8) VG(14) -2E-09 9E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(5) V(8) V(14) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/diplexer2_triplexer_2.ckt b/_unittest/test_45_FilterSolutions/resources/diplexer2_triplexer_2.ckt new file mode 100644 index 00000000000..44f501aaf90 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/diplexer2_triplexer_2.ckt @@ -0,0 +1,41 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L3 2 3 3.979E-08 +C4 3 0 1.745E-11 +L5 3 4 3.559E-08 +C6 4 0 9.213E-12 +L7 4 5 7.958E-09 +R8 5 0 50 +C9 2 6 6.366E-13 +L10 6 0 1.451E-09 +C11 6 7 7.118E-13 +L12 7 0 2.749E-09 +C13 7 8 3.183E-12 +R14 8 0 50 +C15 2 9 8.798E-13 +L16 9 10 2.879E-08 +L17 10 0 2.006E-09 +C18 10 0 1.263E-11 +C19 10 11 9.836E-13 +L20 11 12 2.575E-08 +L21 12 0 3.799E-09 +C22 12 0 6.667E-12 +C23 12 13 4.399E-12 +L24 13 14 5.758E-09 +R25 14 0 50 +* +* Compensation Elements +* +L1 2 15 1.835E-08 +C1 15 0 5.521E-12 +L2 2 16 4.588E-09 +C2 16 0 1.38E-12 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(5) VDB(8) VDB(14) -140 0 +.PLOT AC VP(5) VP(8) VP(14) -200 200 +.PLOT AC VG(5) VG(8) VG(14) -1E-09 5E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(5) V(8) V(14) -0.2 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/equal_capacitors.ckt b/_unittest/test_45_FilterSolutions/resources/equal_capacitors.ckt new file mode 100644 index 00000000000..fbee04c3090 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/equal_capacitors.ckt @@ -0,0 +1,38 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +L1 2 0 4.003E-09 +C2 2 0 6.443E-12 +L3 2 3 7.648E-10 +C3 2 3 4.236E-11 +L4 3 4 4.003E-09 +Rq4 4 0 5E-08 +C5 3 0 4.743E-12 +L6 3 5 7.217E-10 +C6 3 5 2.744E-11 +L7 5 6 4.003E-09 +Rq7 6 0 5E-08 +C8 5 0 6.711E-12 +L9 5 7 3.148E-09 +C9 5 7 1.134E-11 +L10 7 8 5.758E-09 +Rq10 8 0 5E-08 +C11 7 0 5.081E-12 +L12 7 9 1.851E-09 +C12 7 9 9.713E-12 +L13 9 10 2.342E-09 +Rq13 10 0 5E-08 +C14 9 0 1.123E-11 +R15 9 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) -60 0 +.PLOT AC VP(9) -200 200 +.PLOT AC VG(9) 0 2.5E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) -0.04 0.04 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/equal_inductors.ckt b/_unittest/test_45_FilterSolutions/resources/equal_inductors.ckt new file mode 100644 index 00000000000..2cc6bb68d7a --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/equal_inductors.ckt @@ -0,0 +1,32 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +L1 2 0 2.575E-09 +C2 2 3 1.23E-11 +C3 3 0 4.918E-11 +L4 3 4 2.575E-09 +C5 4 0 1.041E-11 +C6 4 5 1.302E-12 +Rq6 4 5 5E+10 +L7 5 0 2.575E-09 +C8 5 0 7.522E-12 +C9 5 6 1.302E-12 +C10 6 0 1.041E-11 +L11 6 7 2.575E-09 +C12 7 0 4.918E-11 +C13 7 8 1.23E-11 +Rq13 7 8 5E+10 +L14 8 0 2.575E-09 +R15 8 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(8) -160 0 +.PLOT AC VP(8) -200 200 +.PLOT AC VG(8) 0 9E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(8) -0.04 0.04 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/equal_legs.ckt b/_unittest/test_45_FilterSolutions/resources/equal_legs.ckt new file mode 100644 index 00000000000..67a3082a9e4 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/equal_legs.ckt @@ -0,0 +1,32 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +L1 2 0 3.08E-09 +C2 2 0 9.836E-12 +L3 2 3 1.263E-08 +L4 3 4 3.08E-09 +Rq4 4 0 5E-08 +C5 3 0 8.641E-12 +C6 3 5 1.589E-12 +L7 5 0 1.269E-09 +C8 5 0 1.728E-11 +C9 5 6 1.589E-12 +L10 6 0 3.08E-09 +C11 6 0 8.641E-12 +L12 6 7 1.263E-08 +L13 7 8 3.08E-09 +Rq13 8 0 5E-08 +C14 7 0 9.836E-12 +R15 7 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(7) -160 0 +.PLOT AC VP(7) -200 200 +.PLOT AC VG(7) 0 9E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(7) -0.04 0.04 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/first_series.ckt b/_unittest/test_45_FilterSolutions/resources/first_series.ckt new file mode 100644 index 00000000000..084c3c8588d --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/first_series.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +L1 2 3 4.918E-09 +C2 3 0 5.15E-12 +L3 3 4 1.592E-08 +C4 4 0 5.15E-12 +L5 4 5 4.918E-09 +R6 5 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(5) -80 0 +.PLOT AC VP(5) -200 200 +.PLOT AC VG(5) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(5) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/first_shunt.ckt b/_unittest/test_45_FilterSolutions/resources/first_shunt.ckt new file mode 100644 index 00000000000..341985a9476 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/first_shunt.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/generator_resistor.ckt b/_unittest/test_45_FilterSolutions/resources/generator_resistor.ckt new file mode 100644 index 00000000000..41bbe13e2dc --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/generator_resistor.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 30 +C1 2 0 1.865E-12 +L2 2 3 4.849E-09 +C3 3 0 8.275E-12 +L4 3 4 8.956E-09 +C5 4 0 8.124E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.8 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/high_low_pass.ckt b/_unittest/test_45_FilterSolutions/resources/high_low_pass.ckt new file mode 100644 index 00000000000..a1e2b114f65 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/high_low_pass.ckt @@ -0,0 +1,22 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 9.836E-12 +L2 2 3 2.476E-09 +C3 3 0 2.627E-10 +L4 3 4 9.78E-11 +C5 4 0 2.04E-08 +L6 4 0 1.242E-12 +C7 4 5 2.59E-10 +L8 5 0 9.641E-11 +C9 5 6 1.023E-11 +L10 6 0 2.575E-09 +R11 6 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) -160 0 +.PLOT AC VP(6) -200 200 +.PLOT AC VG(6) 0 9E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) -0.04 0.04 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/high_low_pass_min_ind.ckt b/_unittest/test_45_FilterSolutions/resources/high_low_pass_min_ind.ckt new file mode 100644 index 00000000000..0331584a42a --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/high_low_pass_min_ind.ckt @@ -0,0 +1,33 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C1 2 0 1.886E-12 +C2 2 3 6.779E-12 +L3 3 4 2.646E-08 +C3 4 0 1.5E-12 +C4 3 5 2.261E-12 +Rq4 3 5 5E+10 +L5 5 6 1.297E-07 +C5 6 0 2.506E-13 +L6 5 7 1.91E-09 +C6 5 7 1.034E-11 +C7 7 0 1.845E-13 +C8 7 8 2.717E-12 +Rq8 7 8 5E+10 +C9 8 0 1.487E-12 +L10 8 9 3.91E-09 +C10 8 9 4.135E-12 +C11 9 0 1.169E-12 +R12 9 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) -70 0 +.PLOT AC VP(9) -200 200 +.PLOT AC VG(9) 0 2E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) -0.04 0.08 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/inductor_cp.ckt b/_unittest/test_45_FilterSolutions/resources/inductor_cp.ckt new file mode 100644 index 00000000000..6ebb131e4f0 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/inductor_cp.ckt @@ -0,0 +1,19 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +CXlp2 2 3 1E-09 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +CXlp4 3 4 1E-09 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -25 -5 +.PLOT AC VP(4) -90 -10 +.PLOT AC VG(4) 0 2.5E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.5 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/inductor_q.ckt b/_unittest/test_45_FilterSolutions/resources/inductor_q.ckt new file mode 100644 index 00000000000..31b6e146174 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/inductor_q.ckt @@ -0,0 +1,19 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +Rq2 3 4 0.809 +C3 4 0 6.366E-12 +L4 4 5 1.288E-08 +Rq4 5 6 0.809 +C5 6 0 1.967E-12 +R6 6 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) -80 0 +.PLOT AC VP(6) -200 200 +.PLOT AC VG(6) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/inductor_rp.ckt b/_unittest/test_45_FilterSolutions/resources/inductor_rp.ckt new file mode 100644 index 00000000000..641e3c9900e --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/inductor_rp.ckt @@ -0,0 +1,19 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +Rp2 2 3 1000 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +Rp4 3 4 1000 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 8E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/inductor_rs.ckt b/_unittest/test_45_FilterSolutions/resources/inductor_rs.ckt new file mode 100644 index 00000000000..95db13c9463 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/inductor_rs.ckt @@ -0,0 +1,19 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +Rs2 3 4 1 +C3 4 0 6.366E-12 +L4 4 5 1.288E-08 +Rs4 5 6 1 +C5 6 0 1.967E-12 +R6 6 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) -80 0 +.PLOT AC VP(6) -200 200 +.PLOT AC VG(6) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/l_lead_inductor.ckt b/_unittest/test_45_FilterSolutions/resources/l_lead_inductor.ckt new file mode 100644 index 00000000000..47b308f3517 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/l_lead_inductor.ckt @@ -0,0 +1,21 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +Ln1L2 2000 2 1E-09 +L2 2000 3001 1.288E-08 +Ln2L2 3001 3 1E-09 +C3 3 0 6.366E-12 +Ln1L4 3000 3 1E-09 +L4 3000 4001 1.288E-08 +Ln2L4 4001 4 1E-09 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) -0.1 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/l_node_capacitor.ckt b/_unittest/test_45_FilterSolutions/resources/l_node_capacitor.ckt new file mode 100644 index 00000000000..4c369e47216 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/l_node_capacitor.ckt @@ -0,0 +1,21 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +Cn1L2 2 0 1E-09 +L2 2 3 1.288E-08 +Cn2L2 3 0 1E-09 +C3 3 0 6.366E-12 +Cn1L4 3 0 1E-09 +L4 3 4 1.288E-08 +Cn2L4 4 0 1E-09 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -200 -50 +.PLOT AC VP(4) -90 0 +.PLOT AC VG(4) 0 3E-11 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) -0.1 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/l_node_compensate.ckt b/_unittest/test_45_FilterSolutions/resources/l_node_compensate.ckt new file mode 100644 index 00000000000..341985a9476 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/l_node_compensate.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/laod_resistor.ckt b/_unittest/test_45_FilterSolutions/resources/laod_resistor.ckt new file mode 100644 index 00000000000..10645a9f9f8 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/laod_resistor.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 8.124E-12 +L2 2 3 8.956E-09 +C3 3 0 8.275E-12 +L4 3 4 4.849E-09 +C5 4 0 1.865E-12 +R6 4 0 30 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.5 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/match_impedance.ckt b/_unittest/test_45_FilterSolutions/resources/match_impedance.ckt new file mode 100644 index 00000000000..9d6028ed29b --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/match_impedance.ckt @@ -0,0 +1,23 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 75 +L1 2 0 3.863E-09 +C2 2 0 6.499E-12 +C3 2 3 3.213E-13 +C4 3 0 7.22E-14 +L5 3 4 6.438E-08 +L6 4 0 7.958E-10 +C7 4 0 3.183E-11 +C8 4 5 3.935E-13 +L9 5 6 6.438E-08 +L10 6 0 2.575E-09 +C11 6 0 9.836E-12 +R12 6 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(6) -160 0 +.PLOT AC VP(6) -200 200 +.PLOT AC VG(6) 0 9E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(6) -0.03 0.03 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/min_cap.ckt b/_unittest/test_45_FilterSolutions/resources/min_cap.ckt new file mode 100644 index 00000000000..20b7b7f3909 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/min_cap.ckt @@ -0,0 +1,34 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +L1 2 3 2.923E-09 +L2 3 0 8.341E-09 +L3 3 4 1.034E-08 +C3 4 0 1.564E-12 +L4 3 5 9.446E-09 +C4 3 5 4.201E-12 +L5 5 6 9.231E-08 +Rq5 6 0 5E-08 +L6 5 7 2.175E-08 +L7 7 8 1.01E-08 +Rq7 8 0 5E-08 +L8 7 9 1.086E-07 +C8 9 0 1.817E-13 +L9 7 10 6.611E-10 +C9 7 10 4.918E-11 +L10 10 11 -1.28E-09 +L11 11 12 -2.966E-08 +Rq11 12 0 5E-08 +R12 11 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(11) -70 0 +.PLOT AC VP(11) -200 200 +.PLOT AC VG(11) 0 2E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(11) -0.04 0.08 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/min_ind.ckt b/_unittest/test_45_FilterSolutions/resources/min_ind.ckt new file mode 100644 index 00000000000..f948a2b0803 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/min_ind.ckt @@ -0,0 +1,33 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C1 2 0 3.83E-13 +C2 2 3 4.123E-12 +C3 3 0 9.716E-13 +L4 3 4 2.561E-09 +C4 3 4 6.314E-12 +L5 4 5 6.878E-09 +C5 5 0 5.769E-12 +C6 4 0 1.075E-11 +C7 4 6 3.713E-12 +Rq7 4 6 5E+10 +L8 6 7 4.542E-10 +C8 6 7 4.345E-11 +L9 7 8 1.229E-07 +C9 8 0 2.644E-13 +C10 7 0 -5.118E-13 +C11 7 9 -1.186E-11 +Rq11 7 9 5E+10 +R12 9 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) -70 0 +.PLOT AC VP(9) -200 200 +.PLOT AC VG(9) 0 2E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) -0.04 0.08 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/multiband.ckt b/_unittest/test_45_FilterSolutions/resources/multiband.ckt new file mode 100644 index 00000000000..5b3c473dacf --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/multiband.ckt @@ -0,0 +1,37 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 6.558E-13 +L2 2 3 1.183E-08 +C2 3 0 7.587E-13 +L3 2 4 1.412E-08 +C3 4 0 1.265E-13 +L4 2 5 4.292E-09 +L5 5 6 4.966E-09 +C5 5 6 1.808E-12 +L6 6 7 8.281E-10 +C6 6 7 2.157E-12 +C7 7 0 2.122E-12 +L8 7 8 3.657E-09 +C8 8 0 2.455E-12 +L9 7 9 4.363E-09 +C9 9 0 4.095E-13 +L10 7 10 4.292E-09 +L11 10 11 4.966E-09 +C11 10 11 1.808E-12 +L12 11 12 8.281E-10 +C12 11 12 2.157E-12 +C13 12 0 6.558E-13 +L14 12 13 1.183E-08 +C14 13 0 7.587E-13 +L15 12 14 1.412E-08 +C15 14 0 1.265E-13 +R16 12 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(12) -200 0 +.PLOT AC VP(12) -200 200 +.PLOT AC VG(12) 0 7E-09 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(12) 0 0.7 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/netlist.ckt b/_unittest/test_45_FilterSolutions/resources/netlist.ckt new file mode 100644 index 00000000000..341985a9476 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/netlist.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/node_cap_ground.ckt b/_unittest/test_45_FilterSolutions/resources/node_cap_ground.ckt new file mode 100644 index 00000000000..3b51b775af0 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/node_cap_ground.ckt @@ -0,0 +1,34 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 98.45 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +L1 2 0 1.413E-08 +C2 2 0 1.455E-12 +L3 2 3 1.389E-09 +C3 2 3 2.333E-11 +C4 3 0 6.44E-13 +L5 3 4 9.637E-10 +C5 3 4 2.055E-11 +L6 4 5 1.841E-09 +Rq6 5 0 5E-08 +C7 4 0 1.313E-11 +L8 4 6 3.952E-09 +C8 4 6 9.032E-12 +C9 6 0 6.44E-13 +L10 6 7 2.251E-09 +C10 6 7 7.985E-12 +L11 7 8 1.912E-09 +Rq11 8 0 5E-08 +C12 7 0 1.296E-11 +R13 7 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(7) -60 0 +.PLOT AC VP(7) -200 200 +.PLOT AC VG(7) 0 2.5E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(7) -0.03 0.03 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/pspice.ckt b/_unittest/test_45_FilterSolutions/resources/pspice.ckt new file mode 100644 index 00000000000..08f462c6a11 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/pspice.ckt @@ -0,0 +1,32 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +L1 2 0 4.285E-09 +C2 2 0 5.911E-12 +L3 2 3 7.553E-10 +C3 2 3 4.289E-11 +L4 3 4 5.905E-10 +C4 3 4 3.354E-11 +L5 4 5 1.237E-09 +Rq5 5 0 5E-08 +C6 4 0 2.048E-11 +L7 4 6 2.913E-09 +C7 4 6 1.225E-11 +L8 6 7 2.067E-09 +C8 6 7 8.696E-12 +L9 7 8 2.068E-09 +Rq9 8 0 5E-08 +C10 7 0 1.225E-11 +R11 7 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(7) -60 0 +.PLOT AC VP(7) -200 200 +.PLOT AC VG(7) 0 2.5E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(7) -0.04 0.04 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/r_lead_inductor.ckt b/_unittest/test_45_FilterSolutions/resources/r_lead_inductor.ckt new file mode 100644 index 00000000000..341985a9476 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/r_lead_inductor.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/r_node_capacitor.ckt b/_unittest/test_45_FilterSolutions/resources/r_node_capacitor.ckt new file mode 100644 index 00000000000..341985a9476 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/r_node_capacitor.ckt @@ -0,0 +1,17 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +C1 2 0 1.967E-12 +L2 2 3 1.288E-08 +C3 3 0 6.366E-12 +L4 3 4 1.288E-08 +C5 4 0 1.967E-12 +R6 4 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(4) -80 0 +.PLOT AC VP(4) -200 200 +.PLOT AC VG(4) 0 9E-10 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(4) 0 0.6 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/resources.py b/_unittest/test_45_FilterSolutions/resources/resources.py new file mode 100644 index 00000000000..2cdcbbb2952 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/resources.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import os + + +def resources_directory(): + dir_path = os.path.dirname(os.path.realpath(__file__)) + test_dir = os.path.dirname(dir_path) + resources_path = os.path.join(test_dir, "resources") + return resources_path + + +def resource_path(resource_file_name): + return os.path.join(resources_directory(), resource_file_name) + + +def read_resource_file(filename): + with open(resource_path(filename)) as f: + return f.read().splitlines() diff --git a/_unittest/test_45_FilterSolutions/resources/set_source_res.ckt b/_unittest/test_45_FilterSolutions/resources/set_source_res.ckt new file mode 100644 index 00000000000..f948a2b0803 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/set_source_res.ckt @@ -0,0 +1,33 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C1 2 0 3.83E-13 +C2 2 3 4.123E-12 +C3 3 0 9.716E-13 +L4 3 4 2.561E-09 +C4 3 4 6.314E-12 +L5 4 5 6.878E-09 +C5 5 0 5.769E-12 +C6 4 0 1.075E-11 +C7 4 6 3.713E-12 +Rq7 4 6 5E+10 +L8 6 7 4.542E-10 +C8 6 7 4.345E-11 +L9 7 8 1.229E-07 +C9 8 0 2.644E-13 +C10 7 0 -5.118E-13 +C11 7 9 -1.186E-11 +Rq11 7 9 5E+10 +R12 9 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) -70 0 +.PLOT AC VP(9) -200 200 +.PLOT AC VG(9) 0 2E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) -0.04 0.08 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/source_res.ckt b/_unittest/test_45_FilterSolutions/resources/source_res.ckt new file mode 100644 index 00000000000..f948a2b0803 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/source_res.ckt @@ -0,0 +1,33 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C1 2 0 3.83E-13 +C2 2 3 4.123E-12 +C3 3 0 9.716E-13 +L4 3 4 2.561E-09 +C4 3 4 6.314E-12 +L5 4 5 6.878E-09 +C5 5 0 5.769E-12 +C6 4 0 1.075E-11 +C7 4 6 3.713E-12 +Rq7 4 6 5E+10 +L8 6 7 4.542E-10 +C8 6 7 4.345E-11 +L9 7 8 1.229E-07 +C9 8 0 2.644E-13 +C10 7 0 -5.118E-13 +C11 7 9 -1.186E-11 +Rq11 7 9 5E+10 +R12 9 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) -70 0 +.PLOT AC VP(9) -200 200 +.PLOT AC VG(9) 0 2E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) -0.04 0.08 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/transferfunction.ckt b/_unittest/test_45_FilterSolutions/resources/transferfunction.ckt new file mode 100644 index 00000000000..2515562c037 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/transferfunction.ckt @@ -0,0 +1,6 @@ + 5 1 + 4 2.033e+10 + 3 2.067e+20 + 2 1.299e+30 + 1 5.044e+39 + 0 9.793e+48 9.793e+48 diff --git a/_unittest/test_45_FilterSolutions/resources/trap_topology.ckt b/_unittest/test_45_FilterSolutions/resources/trap_topology.ckt new file mode 100644 index 00000000000..2293fa0f364 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/trap_topology.ckt @@ -0,0 +1,35 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C1 2 0 2.29E-13 +C2 2 3 4.93E-12 +C3 3 0 1.162E-12 +L5 3 4 3.706E-09 +C6 4 5 2.586E-11 +Rq6 4 5 5E+10 +C4 3 5 5.248E-12 +L7 5 6 8.386E-10 +C7 6 0 1.652E-11 +C8 5 6 3.079E-11 +C10 5 7 2.923E-13 +Rq10 5 7 5E+10 +L11 7 8 7.329E-08 +C9 5 8 3.421E-12 +L12 8 9 1.405E-07 +C12 9 0 -2.474E-13 +C13 8 9 4.788E-13 +C14 8 10 -1.186E-11 +Rq14 8 10 5E+10 +R15 10 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(10) -70 0 +.PLOT AC VP(10) -200 200 +.PLOT AC VG(10) 0 2E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(10) -0.04 0.08 +.END diff --git a/_unittest/test_45_FilterSolutions/resources/zig_zag.ckt b/_unittest/test_45_FilterSolutions/resources/zig_zag.ckt new file mode 100644 index 00000000000..f948a2b0803 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/resources/zig_zag.ckt @@ -0,0 +1,33 @@ +* +V1 1 0 AC 1 PULSE 0 1 0 1.592E-13 0 +R0 1 2 50 +* +* Dummy Resistors Required For Spice +* Have Been Added to Net List. +* +C1 2 0 3.83E-13 +C2 2 3 4.123E-12 +C3 3 0 9.716E-13 +L4 3 4 2.561E-09 +C4 3 4 6.314E-12 +L5 4 5 6.878E-09 +C5 5 0 5.769E-12 +C6 4 0 1.075E-11 +C7 4 6 3.713E-12 +Rq7 4 6 5E+10 +L8 6 7 4.542E-10 +C8 6 7 4.345E-11 +L9 7 8 1.229E-07 +C9 8 0 2.644E-13 +C10 7 0 -5.118E-13 +C11 7 9 -1.186E-11 +Rq11 7 9 5E+10 +R12 9 0 50 +* +.AC DEC 200 2E+08 5E+09 +.PLOT AC VDB(9) -70 0 +.PLOT AC VP(9) -200 200 +.PLOT AC VG(9) 0 2E-08 +.TRAN 5E-11 1E-08 0 +.PLOT TRAN V(9) -0.04 0.08 +.END diff --git a/_unittest/test_45_FilterSolutions/test_filter/__init__.py b/_unittest/test_45_FilterSolutions/test_filter/__init__.py new file mode 100644 index 00000000000..9c4476773da --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_filter/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. diff --git a/_unittest/test_45_FilterSolutions/test_filter/test_attributes.py b/_unittest/test_45_FilterSolutions/test_filter/test_attributes.py new file mode 100644 index 00000000000..ca0088e5d2e --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_filter/test_attributes.py @@ -0,0 +1,508 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import BesselRipplePercentage +from pyaedt.filtersolutions_core.attributes import DiplexerType +from pyaedt.filtersolutions_core.attributes import FilterClass +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.filtersolutions_core.attributes import FilterType +from pyaedt.filtersolutions_core.attributes import GaussianBesselReflection +from pyaedt.filtersolutions_core.attributes import GaussianTransition +from pyaedt.filtersolutions_core.attributes import PassbandDefinition +from pyaedt.filtersolutions_core.attributes import RippleConstrictionBandSelect +from pyaedt.filtersolutions_core.attributes import SinglePointRippleInfZeros +from pyaedt.filtersolutions_core.attributes import StopbandDefinition +from pyaedt.generic.general_methods import is_linux + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + def test_filter_type(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.filter_type == FilterType.BUTTERWORTH + + assert len(FilterType) == 9 + + for fimp in [FilterImplementation.LUMPED]: + design.attributes.filter_implementation = fimp + for ftype in FilterType: + design.attributes.filter_type = ftype + assert design.attributes.filter_type == ftype + + def test_filter_class(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.filter_class == FilterClass.LOW_PASS + + # Only lumped supports all classes + # TODO: Confirm proper exceptions are raised when setting unsupported filter class for each implementation. + + assert len(FilterClass) == 10 + for index, fclass in enumerate(FilterClass): + if index > 5: + design.attributes.filter_multiple_bands_enabled = True + design.attributes.filter_class = fclass + assert design.attributes.filter_class == fclass + + def test_filter_multiple_bands_enabled(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.filter_multiple_bands_enabled is False + design.attributes.filter_multiple_bands_enabled = True + assert design.attributes.filter_multiple_bands_enabled + + def test_filter_multiple_bands_low_pass_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + design.attributes.filter_class = FilterClass.LOW_BAND + assert design.attributes.filter_multiple_bands_low_pass_frequency == "1G" + design.attributes.filter_multiple_bands_low_pass_frequency = "500M" + assert design.attributes.filter_multiple_bands_low_pass_frequency == "500M" + + def test_filter_multiple_bands_high_pass_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + design.attributes.filter_class = FilterClass.BAND_HIGH + assert design.attributes.filter_multiple_bands_high_pass_frequency == "1G" + design.attributes.filter_multiple_bands_high_pass_frequency = "500M" + assert design.attributes.filter_multiple_bands_high_pass_frequency == "500M" + + def test_filter_implementation(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert len(FilterImplementation) == 5 + for fimplementation in FilterImplementation: + design.attributes.filter_implementation = fimplementation + assert design.attributes.filter_implementation == fimplementation + + def test_diplexer_type(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert len(DiplexerType) == 6 + for index, diplexer_type in enumerate(DiplexerType): + if index < 3: + design.attributes.filter_class = FilterClass.DIPLEXER_1 + elif index > 2: + design.attributes.filter_class = FilterClass.DIPLEXER_2 + design.attributes.diplexer_type = diplexer_type + assert design.attributes.diplexer_type == diplexer_type + + def test_order(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.order == 5 + + with pytest.raises(RuntimeError) as info: + design.attributes.order = 0 + assert info.value.args[0] == "The minimum order is 1" + + for i in range(1, 22): + design.attributes.order = i + assert design.attributes.order == i + + with pytest.raises(RuntimeError) as info: + design.attributes.order = 22 + assert info.value.args[0] == "The maximum order is 21" + + def test_minimum_order_stop_band_att(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.minimum_order_stop_band_attenuation_db == "60 dB" + design.attributes.minimum_order_stop_band_attenuation_db = "40 dB" + assert design.attributes.minimum_order_stop_band_attenuation_db == "40 dB" + + def test_minimum_order_stop_band_freq(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.minimum_order_stop_band_frequency == "10 GHz" + design.attributes.minimum_order_stop_band_frequency = "500 MHz" + assert design.attributes.minimum_order_stop_band_frequency == "500 MHz" + + def test_minimum_order_group_delay_error_percent(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.DELAY + assert design.attributes.minimum_order_group_delay_error_percent == "5" + design.attributes.minimum_order_group_delay_error_percent = "7" + assert design.attributes.minimum_order_group_delay_error_percent == "7" + + def test_minimum_order_group_delay_cutoff(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.DELAY + assert design.attributes.minimum_order_group_delay_cutoff == "2 GHz" + design.attributes.minimum_order_group_delay_cutoff = "500 MHz" + assert design.attributes.minimum_order_group_delay_cutoff == "500 MHz" + + def test_minimum_order_stop_band_freq(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.minimum_order_stop_band_frequency == "10 GHz" + design.attributes.minimum_order_stop_band_frequency = "500 MHz" + assert design.attributes.minimum_order_stop_band_frequency == "500 MHz" + + def test_minimum_order(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.order == 5 + design.attributes.ideal_minimum_order + assert design.attributes.order == 3 + + def test_pass_band_definition(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + assert len(PassbandDefinition) == 2 + assert design.attributes.pass_band_definition == PassbandDefinition.CENTER_FREQUENCY + for pbd in PassbandDefinition: + design.attributes.pass_band_definition = pbd + assert design.attributes.pass_band_definition == pbd + + def test_pass_band_center_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.pass_band_center_frequency == "1G" + design.attributes.pass_band_center_frequency = "500M" + assert design.attributes.pass_band_center_frequency == "500M" + + def test_pass_band_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + assert design.attributes.pass_band_width_frequency == "200M" + design.attributes.pass_band_width_frequency = "500M" + assert design.attributes.pass_band_width_frequency == "500M" + + def test_lower_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.pass_band_definition = PassbandDefinition.CORNER_FREQUENCIES + assert design.attributes.lower_frequency == "905 M" + design.attributes.lower_frequency = "800M" + assert design.attributes.lower_frequency == "800M" + + def test_upper_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.pass_band_definition = PassbandDefinition.CORNER_FREQUENCIES + assert design.attributes.upper_frequency == "1.105 G" + design.attributes.upper_frequency = "1.2 G" + assert design.attributes.upper_frequency == "1.2 G" + + def test_stop_band_definition(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + assert len(StopbandDefinition) == 3 + assert design.attributes.stop_band_definition == StopbandDefinition.RATIO + for sbd in StopbandDefinition: + design.attributes.stop_band_definition = sbd + assert design.attributes.stop_band_definition == sbd + + def test_stop_band_ratio(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + assert design.attributes.stop_band_ratio == "1.2" + design.attributes.stop_band_ratio = "1.5" + assert design.attributes.stop_band_ratio == "1.5" + + def test_stop_band_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.stop_band_definition = StopbandDefinition.FREQUENCY + assert design.attributes.stop_band_frequency == "1.2 G" + design.attributes.stop_band_frequency = "1.5 G" + assert design.attributes.stop_band_frequency == "1.5 G" + + def test_stop_band_attenuation(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.stop_band_definition = StopbandDefinition.ATTENUATION_DB + assert design.attributes.stop_band_attenuation_db == "60" + design.attributes.stop_band_attenuation_db = "40 dB" + assert design.attributes.stop_band_attenuation_db == "40" + + def test_standard_pass_band_attenuation(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.standard_pass_band_attenuation + design.attributes.standard_pass_band_attenuation = False + assert design.attributes.standard_pass_band_attenuation is False + + def test_standard_pass_band_attenuation_value_db(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.standard_pass_band_attenuation = False + assert design.attributes.standard_pass_band_attenuation_value_db == "3.01" + design.attributes.standard_pass_band_attenuation_value_db = "4" + assert design.attributes.standard_pass_band_attenuation_value_db == "4" + + def test_equiripple_delay(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.DELAY + assert design.attributes.equiripple_delay + design.attributes.equiripple_delay = False + assert design.attributes.equiripple_delay is False + + def test_group_delay_ripple_period(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.DELAY + assert design.attributes.group_delay_ripple_period == "2" + design.attributes.group_delay_ripple_period = "3" + assert design.attributes.group_delay_ripple_period == "3" + + def test_normalized_group_delay_percentage(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.DELAY + assert len(BesselRipplePercentage) == 6 + for normalized_group_delay_percentage in BesselRipplePercentage: + design.attributes.normalized_group_delay_percentage = normalized_group_delay_percentage + assert design.attributes.normalized_group_delay_percentage == normalized_group_delay_percentage + + def test_bessel_normalized_delay(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.BESSEL + assert design.attributes.bessel_normalized_delay is False + design.attributes.bessel_normalized_delay = True + assert design.attributes.bessel_normalized_delay + + def test_bessel_normalized_delay_period(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.BESSEL + design.attributes.bessel_normalized_delay = True + assert design.attributes.bessel_normalized_delay_period == "2" + design.attributes.bessel_normalized_delay_period = "3" + assert design.attributes.bessel_normalized_delay_period == "3" + + def test_bessel_normalized_delay_percentage(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.BESSEL + design.attributes.bessel_normalized_delay = True + assert len(BesselRipplePercentage) == 6 + for bessel_normalized_delay_percentage in BesselRipplePercentage: + design.attributes.bessel_normalized_delay_percentage = bessel_normalized_delay_percentage + assert design.attributes.bessel_normalized_delay_percentage == bessel_normalized_delay_percentage + + def test_pass_band_ripple(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + assert design.attributes.pass_band_ripple == ".05" + design.attributes.pass_band_ripple = ".03" + assert design.attributes.pass_band_ripple == ".03" + + def test_arith_symmetry(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.filter_class = FilterClass.BAND_PASS + assert design.attributes.arith_symmetry is False + design.attributes.arith_symmetry = True + assert design.attributes.arith_symmetry + + def test_asymmetric(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + assert design.attributes.asymmetric is False + design.attributes.asymmetric = True + assert design.attributes.asymmetric + + def test_asymmetric_low_order(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.asymmetric = True + assert design.attributes.asymmetric_low_order == 5 + + with pytest.raises(RuntimeError) as info: + design.attributes.asymmetric_low_order = 0 + assert info.value.args[0] == "The minimum order is 1" + + for i in range(1, 22): + design.attributes.asymmetric_low_order = i + assert design.attributes.asymmetric_low_order == i + + with pytest.raises(RuntimeError) as info: + design.attributes.asymmetric_low_order = 22 + assert info.value.args[0] == "The maximum order is 21" + + def test_asymmetric_high_order(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.asymmetric = True + assert design.attributes.asymmetric_high_order == 5 + + with pytest.raises(RuntimeError) as info: + design.attributes.asymmetric_high_order = 0 + assert info.value.args[0] == "The minimum order is 1" + + for i in range(1, 22): + design.attributes.asymmetric_high_order = i + assert design.attributes.asymmetric_high_order == i + + with pytest.raises(RuntimeError) as info: + design.attributes.asymmetric_high_order = 22 + assert info.value.args[0] == "The maximum order is 21" + + def test_asymmetric_low_stop_band_ratio(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.asymmetric = True + assert design.attributes.asymmetric_low_stop_band_ratio == "1.2" + design.attributes.asymmetric_low_stop_band_ratio = "1.5" + assert design.attributes.asymmetric_low_stop_band_ratio == "1.5" + + def test_asymmetric_high_stop_band_ratio(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.asymmetric = True + assert design.attributes.asymmetric_high_stop_band_ratio == "1.2" + design.attributes.asymmetric_high_stop_band_ratio = "1.5" + assert design.attributes.asymmetric_high_stop_band_ratio == "1.5" + + def test_asymmetric_low_stop_band_attenuation_db(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.asymmetric = True + assert design.attributes.asymmetric_low_stop_band_attenuation_db == "60" + design.attributes.asymmetric_low_stop_band_attenuation_db = "40" + assert design.attributes.asymmetric_low_stop_band_attenuation_db == "40" + + def test_asymmetric_high_stop_band_attenuation_db(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.asymmetric = True + assert design.attributes.asymmetric_high_stop_band_attenuation_db == "60" + design.attributes.asymmetric_high_stop_band_attenuation_db = "40" + assert design.attributes.asymmetric_high_stop_band_attenuation_db == "40" + + def test_gaussian_transition(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.GAUSSIAN + assert len(GaussianTransition) == 6 + for gaussian_transition in GaussianTransition: + design.attributes.gaussian_transition = gaussian_transition + assert design.attributes.gaussian_transition == gaussian_transition + + def test_gaussian_bessel_reflection(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.BESSEL + assert len(GaussianBesselReflection) == 3 + for gaussian_bessel_reflection in GaussianBesselReflection: + design.attributes.gaussian_bessel_reflection = gaussian_bessel_reflection + assert design.attributes.gaussian_bessel_reflection == gaussian_bessel_reflection + + def test_even_order(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.order = 4 + assert design.attributes.even_order + design.attributes.even_order = False + assert design.attributes.even_order is False + + def test_even_order_refl_zero(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.order = 4 + assert design.attributes.even_order_refl_zero + design.attributes.even_order_refl_zero = False + assert design.attributes.even_order_refl_zero is False + + def test_even_order_trn_zero(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.order = 4 + assert design.attributes.even_order_trn_zero + design.attributes.even_order_trn_zero = False + assert design.attributes.even_order_trn_zero is False + + def test_constrict_ripple(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + assert design.attributes.constrict_ripple is False + design.attributes.constrict_ripple = True + assert design.attributes.constrict_ripple + + def test_single_point_ripple(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + assert design.attributes.single_point_ripple is False + design.attributes.single_point_ripple = True + assert design.attributes.single_point_ripple + + def test_half_band_ripple(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + assert design.attributes.half_band_ripple is False + design.attributes.half_band_ripple = True + assert design.attributes.half_band_ripple + + def test_constrict_ripple_percent(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.constrict_ripple = True + assert design.attributes.constrict_ripple_percent == "50%" + design.attributes.constrict_ripple_percent = "40%" + assert design.attributes.constrict_ripple_percent == "40%" + + def test_ripple_constriction_band(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.constrict_ripple = True + assert len(RippleConstrictionBandSelect) == 3 + for ripple_constriction_band in RippleConstrictionBandSelect: + design.attributes.ripple_constriction_band = ripple_constriction_band + assert design.attributes.ripple_constriction_band == ripple_constriction_band + + def test_single_point_ripple_inf_zeros(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_type = FilterType.ELLIPTIC + design.attributes.single_point_ripple = True + assert len(SinglePointRippleInfZeros) == 2 + for single_point_ripple_inf_zeros in SinglePointRippleInfZeros: + design.attributes.single_point_ripple_inf_zeros = single_point_ripple_inf_zeros + assert design.attributes.single_point_ripple_inf_zeros == single_point_ripple_inf_zeros + + def test_delay_equalizer(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.attributes.delay_equalizer is False + design.attributes.delay_equalizer = True + assert design.attributes.delay_equalizer + + def test_delay_equalizer_order(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.delay_equalizer = True + assert design.attributes.delay_equalizer_order == 2 + + for i in range(0, 21): + design.attributes.delay_equalizer_order = i + assert design.attributes.delay_equalizer_order == i + + with pytest.raises(RuntimeError) as info: + design.attributes.delay_equalizer_order = 21 + assert info.value.args[0] == "The maximum order is 20" + + def test_standard_delay_equ_pass_band_attenuation(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.delay_equalizer = True + assert design.attributes.standard_delay_equ_pass_band_attenuation + design.attributes.standard_delay_equ_pass_band_attenuation = False + assert design.attributes.standard_delay_equ_pass_band_attenuation is False + + def test_standard_delay_equ_pass_band_attenuation_value_db(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.delay_equalizer = True + design.attributes.standard_delay_equ_pass_band_attenuation = False + assert design.attributes.standard_delay_equ_pass_band_attenuation_value_db == "3.01" + design.attributes.standard_delay_equ_pass_band_attenuation_value_db = "4" + assert design.attributes.standard_delay_equ_pass_band_attenuation_value_db == "4" diff --git a/_unittest/test_45_FilterSolutions/test_filter/test_dll_interface.py b/_unittest/test_45_FilterSolutions/test_filter/test_dll_interface.py new file mode 100644 index 00000000000..e7fd0b19753 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_filter/test_dll_interface.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +from _unittest.test_45_FilterSolutions.test_filter import test_transmission_zeros +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.filtersolutions_core.attributes import FilterType +from pyaedt.generic.general_methods import is_linux + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + def test_version(self): + assert pyaedt.filtersolutions_core.api_version() == "FilterSolutions API Version 2025 R1" + + def test_string_to_enum(self): + assert ( + pyaedt.filtersolutions_core._dll_interface().string_to_enum(FilterType, "gaussian") == FilterType.GAUSSIAN + ) + + def test_enum_to_string(self): + assert pyaedt.filtersolutions_core._dll_interface().enum_to_string(FilterType.GAUSSIAN) == "gaussian" + + def test_raise_error(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.row(0) + assert info.value.args[0] == test_transmission_zeros.TestClass.no_transmission_zero_msg diff --git a/_unittest/test_45_FilterSolutions/test_filter/test_graph_setup.py b/_unittest/test_45_FilterSolutions/test_filter/test_graph_setup.py new file mode 100644 index 00000000000..4336f888e61 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_filter/test_graph_setup.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.generic.general_methods import is_linux + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + + def test_minimum_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.graph_setup.minimum_frequency == "200 MHz" + design.graph_setup.minimum_frequency = "500 MHz" + assert design.graph_setup.minimum_frequency == "500 MHz" + + def test_maximum_frequency(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.graph_setup.maximum_frequency == "5 GHz" + design.graph_setup.maximum_frequency = "2 GHz" + assert design.graph_setup.maximum_frequency == "2 GHz" + + def test_minimum_time(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.graph_setup.minimum_time == "0" + design.graph_setup.minimum_time = "5 ns" + assert design.graph_setup.minimum_time == "5 ns" + + def test_maximum_time(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.graph_setup.maximum_time == "10n" + design.graph_setup.maximum_time = "8 ns" + assert design.graph_setup.maximum_time == "8 ns" diff --git a/_unittest/test_45_FilterSolutions/test_filter/test_ideal_response.py b/_unittest/test_45_FilterSolutions/test_filter/test_ideal_response.py new file mode 100644 index 00000000000..4d98e296e3d --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_filter/test_ideal_response.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.filtersolutions_core.ideal_response import FrequencyResponseColumn +from pyaedt.filtersolutions_core.ideal_response import PoleZerosResponseColumn +from pyaedt.filtersolutions_core.ideal_response import SParametersResponseColumn +from pyaedt.filtersolutions_core.ideal_response import TimeResponseColumn +from pyaedt.generic.general_methods import is_linux + +from ..resources import read_resource_file + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + def test_frequency_response_getter(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + + mag_db = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.MAGNITUDE_DB) + assert len(mag_db) == 500 + assert mag_db[100] == pytest.approx(-0.0002779395744451339) + assert mag_db[300] == pytest.approx(-14.14973347970826) + assert mag_db[-1] == pytest.approx(-69.61741290615645) + phs_deg = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.PHASE_DEG) + assert len(phs_deg) == 500 + assert phs_deg[100] == pytest.approx(-72.00174823521779) + assert phs_deg[300] == pytest.approx(57.235563076374426) + assert phs_deg[-1] == pytest.approx(-52.48142049626833) + grp_dly = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.GROUP_DELAY) + assert len(grp_dly) == 500 + assert grp_dly[100] == pytest.approx(5.476886038520659e-10) + assert grp_dly[300] == pytest.approx(3.6873949391963247e-10) + assert grp_dly[-1] == pytest.approx(2.1202561661746704e-11) + phs_rad = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.PHASE_RAD) + assert len(phs_rad) == 500 + assert phs_rad[100] == pytest.approx(-1.256667573896567) + assert phs_rad[300] == pytest.approx(0.9989490249156284) + assert phs_rad[-1] == pytest.approx(-0.9159735837835188) + mag_art = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.MAGNITUDE_ARITH) + assert len(mag_art) == 500 + assert mag_art[100] == pytest.approx(0.9999680015359182) + assert mag_art[300] == pytest.approx(0.1961161351381822) + assert mag_art[-1] == pytest.approx(0.000330467956321812) + mag_r = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.MAGNITUDE_REAL) + assert len(mag_r) == 500 + assert mag_r[100] == pytest.approx(0.3089780880159494) + assert mag_r[300] == pytest.approx(0.10613537973464354) + assert mag_r[-1] == pytest.approx(0.00020126115208825366) + mag_x = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.MAGNITUDE_IMAG) + assert len(mag_x) == 500 + assert mag_x[100] == pytest.approx(-0.9510355120718397) + assert mag_x[300] == pytest.approx(0.1649145828303876) + assert mag_x[-1] == pytest.approx(-0.00026211260712835594) + phs_dev_deg = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.PHASE_DEV_DEG) + assert len(phs_dev_deg) == 500 + assert phs_dev_deg[100] == pytest.approx(116.73031543331324) + assert phs_dev_deg[300] == pytest.approx(-50.566997975196706) + assert phs_dev_deg[-1] == pytest.approx(67.66973459820802) + phs_dev_rad = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.PHASE_DEV_RAD) + assert len(phs_dev_rad) == 500 + assert phs_dev_rad[100] == pytest.approx(2.0373283412028673) + assert phs_dev_rad[300] == pytest.approx(-0.8825606075164885) + assert phs_dev_rad[-1] == pytest.approx(1.181059672689452) + freqs = design.ideal_response._frequency_response_getter(FrequencyResponseColumn.FREQUENCY) + assert len(freqs) == 500 + assert freqs[100] == 2392202091.5388284 + assert freqs[300] == 8669097136.772985 + assert freqs[-1] == 31214328219.225075 + + def test_time_response_getter(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + step_response = design.ideal_response._time_response_getter(TimeResponseColumn.STEP_RESPONSE) + assert len(step_response) == 300 + assert step_response[100] == pytest.approx(1.0006647872833518) + assert step_response[200] == pytest.approx(0.9999988501385255) + assert step_response[-1] == pytest.approx(0.9999999965045667) + ramp_response = design.ideal_response._time_response_getter(TimeResponseColumn.RAMP_RESPONSE) + assert len(ramp_response) == 300 + assert ramp_response[100] == pytest.approx(2.8184497075983895e-09) + assert ramp_response[200] == pytest.approx(6.151630831481296e-09) + assert ramp_response[-1] == pytest.approx(9.45163045223663e-09) + impulse_response = design.ideal_response._time_response_getter(TimeResponseColumn.IMPULSE_RESPONSE) + assert len(impulse_response) == 300 + assert impulse_response[100] == pytest.approx(-8537300.294689251) + assert impulse_response[200] == pytest.approx(-8538.227868086184) + assert impulse_response[-1] == pytest.approx(3.996366349798659) + step_response_db = design.ideal_response._time_response_getter(TimeResponseColumn.STEP_RESPONSE_DB) + assert len(step_response_db) == 300 + assert step_response_db[100] == pytest.approx(-1.0381882969997027) + assert step_response_db[200] == pytest.approx(-1.0439706350712086) + assert step_response_db[-1] == pytest.approx(-1.0439606778565478) + ramp_response_db = design.ideal_response._time_response_getter(TimeResponseColumn.RAMP_RESPONSE_DB) + assert len(ramp_response_db) == 300 + assert ramp_response_db[100] == pytest.approx(-10.540507747401335) + assert ramp_response_db[200] == pytest.approx(-3.7609082425924782) + assert ramp_response_db[-1] == pytest.approx(-0.03057888328183367) + impulse_response_db = design.ideal_response._time_response_getter(TimeResponseColumn.IMPULSE_RESPONSE_DB) + assert len(impulse_response_db) == 300 + assert impulse_response_db[100] == pytest.approx(-48.60282519370875) + assert impulse_response_db[200] == pytest.approx(-100.0) + assert impulse_response_db[-1] == pytest.approx(-100.0) + time = design.ideal_response._time_response_getter(TimeResponseColumn.TIME) + assert len(time) == 300 + assert time[1] == pytest.approx(3.3333333333333335e-11) + assert time[200] == pytest.approx(6.666666666666667e-09) + assert time[-1] == pytest.approx(9.966666666666667e-09) + + def test_sparameters_response_getter(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + s11_response_db = design.ideal_response._sparamaters_response_getter(SParametersResponseColumn.S11_DB) + assert len(s11_response_db) == 500 + assert s11_response_db[100] == pytest.approx(-41.93847819973562) + assert s11_response_db[300] == pytest.approx(-0.1703333929877981) + assert s11_response_db[-1] == pytest.approx(-4.742889883456317e-07) + s21_response_db = design.ideal_response._sparamaters_response_getter(SParametersResponseColumn.S21_DB) + assert len(s21_response_db) == 500 + assert s21_response_db[100] == pytest.approx(-0.0002779395744451339) + assert s21_response_db[300] == pytest.approx(-14.14973347970826) + assert s21_response_db[-1] == pytest.approx(-69.61741290615645) + s11_response = design.ideal_response._sparamaters_response_getter(SParametersResponseColumn.S11_ARITH) + assert len(s11_response) == 500 + assert s11_response[100] == pytest.approx(0.007999744012287301) + assert s11_response[300] == pytest.approx(0.9805806756909208) + assert s11_response[-1] == pytest.approx(0.9999999453954638) + s21_response = design.ideal_response._sparamaters_response_getter(SParametersResponseColumn.S21_ARITH) + assert len(s21_response) == 500 + assert s21_response[100] == pytest.approx(0.9999680015359182) + assert s21_response[300] == pytest.approx(0.1961161351381822) + assert s21_response[-1] == pytest.approx(0.000330467956321812) + freqs = design.ideal_response._sparamaters_response_getter(SParametersResponseColumn.FREQUENCY) + assert len(freqs) == 500 + assert freqs[100] == pytest.approx(2392202091.5388284) + assert freqs[300] == pytest.approx(8669097136.772985) + assert freqs[-1] == pytest.approx(31214328219.225075) + + def test_pole_zeros_response_getter(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + pole_zero_den_x = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.TX_ZERO_DEN_X) + assert len(pole_zero_den_x) == 5 + assert pole_zero_den_x[0] == pytest.approx(-1000000000.0) + assert pole_zero_den_x[1] == pytest.approx(-809016994.3749474) + assert pole_zero_den_x[2] == pytest.approx(-809016994.3749474) + assert pole_zero_den_x[3] == pytest.approx(-309016994.3749475) + assert pole_zero_den_x[4] == pytest.approx(-309016994.3749475) + pole_zero_den_y = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.TX_ZERO_DEN_Y) + assert len(pole_zero_den_y) == 5 + assert pole_zero_den_y[0] == pytest.approx(0.0) + assert pole_zero_den_y[1] == pytest.approx(587785252.2924731) + assert pole_zero_den_y[2] == pytest.approx(-587785252.2924731) + assert pole_zero_den_y[3] == pytest.approx(951056516.2951534) + assert pole_zero_den_y[4] == pytest.approx(-951056516.2951534) + pole_zero_num_x = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.TX_ZERO_NUM_X) + assert len(pole_zero_num_x) == 0 + pole_zero_num_y = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.TX_ZERO_NUM_Y) + assert len(pole_zero_num_y) == 0 + proto_pole_zero_den_x = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_TX_ZERO_DEN_X + ) + assert len(proto_pole_zero_den_x) == 5 + assert proto_pole_zero_den_x[0] == pytest.approx(-0.30901699437494745) + assert proto_pole_zero_den_x[1] == pytest.approx(-0.30901699437494745) + assert proto_pole_zero_den_x[2] == pytest.approx(-0.8090169943749475) + assert proto_pole_zero_den_x[3] == pytest.approx(-0.8090169943749475) + assert proto_pole_zero_den_x[4] == pytest.approx(-1.0) + proto_pole_zero_den_y = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_TX_ZERO_DEN_Y + ) + assert len(proto_pole_zero_den_y) == 5 + assert proto_pole_zero_den_y[0] == pytest.approx(0.9510565162951534) + assert proto_pole_zero_den_y[1] == pytest.approx(-0.9510565162951534) + assert proto_pole_zero_den_y[2] == pytest.approx(-0.5877852522924731) + assert proto_pole_zero_den_y[3] == pytest.approx(0.5877852522924731) + assert proto_pole_zero_den_y[4] == pytest.approx(0.0) + proto_pole_zero_num_x = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_TX_ZERO_NUM_X + ) + assert len(proto_pole_zero_num_x) == 0 + proto_pole_zero_num_y = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_TX_ZERO_NUM_Y + ) + assert len(proto_pole_zero_num_y) == 0 + rx_zero_den_x = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.RX_ZERO_DEN_X) + assert len(rx_zero_den_x) == 5 + assert rx_zero_den_x[0] == pytest.approx(-1000000000.0) + assert rx_zero_den_x[1] == pytest.approx(-809016994.3749474) + assert rx_zero_den_x[2] == pytest.approx(-809016994.3749474) + assert rx_zero_den_x[3] == pytest.approx(-309016994.3749475) + assert rx_zero_den_x[4] == pytest.approx(-309016994.3749475) + rx_zero_den_y = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.RX_ZERO_DEN_Y) + assert len(rx_zero_den_y) == 5 + assert rx_zero_den_y[0] == pytest.approx(0.0) + assert rx_zero_den_y[1] == pytest.approx(587785252.2924731) + assert rx_zero_den_y[2] == pytest.approx(-587785252.2924731) + assert rx_zero_den_y[3] == pytest.approx(951056516.2951534) + assert rx_zero_den_y[4] == pytest.approx(-951056516.2951534) + rx_zero_num_x = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.RX_ZERO_NUM_X) + assert len(rx_zero_num_x) == 5 + assert rx_zero_num_x[0] == pytest.approx(0.0) + assert rx_zero_num_x[1] == pytest.approx(0.0) + assert rx_zero_num_x[2] == pytest.approx(0.0) + assert rx_zero_num_x[3] == pytest.approx(0.0) + assert rx_zero_num_x[4] == pytest.approx(0.0) + rx_zero_num_y = design.ideal_response._pole_zeros_response_getter(PoleZerosResponseColumn.RX_ZERO_NUM_Y) + assert len(rx_zero_num_y) == 5 + assert rx_zero_num_y[0] == pytest.approx(0.0) + assert rx_zero_num_y[1] == pytest.approx(0.0) + assert rx_zero_num_y[2] == pytest.approx(0.0) + assert rx_zero_num_y[3] == pytest.approx(0.0) + assert rx_zero_num_y[4] == pytest.approx(0.0) + proto_rx_zero_den_x = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_RX_ZERO_DEN_X + ) + assert len(proto_rx_zero_den_x) == 5 + assert proto_rx_zero_den_x[0] == pytest.approx(-0.30901699437494745) + assert proto_rx_zero_den_x[1] == pytest.approx(-0.30901699437494745) + assert proto_rx_zero_den_x[2] == pytest.approx(-0.8090169943749475) + assert proto_rx_zero_den_x[3] == pytest.approx(-0.8090169943749475) + assert proto_rx_zero_den_x[4] == pytest.approx(-1.0) + proto_rx_zero_den_y = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_RX_ZERO_DEN_Y + ) + assert len(proto_rx_zero_den_y) == 5 + assert proto_rx_zero_den_y[0] == pytest.approx(0.9510565162951534) + assert proto_rx_zero_den_y[1] == pytest.approx(-0.9510565162951534) + assert proto_rx_zero_den_y[2] == pytest.approx(-0.5877852522924731) + assert proto_rx_zero_den_y[3] == pytest.approx(0.5877852522924731) + assert proto_rx_zero_den_y[4] == pytest.approx(0.0) + proto_rx_zero_num_x = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_RX_ZERO_NUM_X + ) + assert len(proto_rx_zero_num_x) == 5 + assert proto_rx_zero_num_x[0] == pytest.approx(0.0) + assert proto_rx_zero_num_x[1] == pytest.approx(0.0) + assert proto_rx_zero_num_x[2] == pytest.approx(0.0) + assert proto_rx_zero_num_x[3] == pytest.approx(0.0) + assert proto_rx_zero_num_x[4] == pytest.approx(0.0) + proto_rx_zero_num_y = design.ideal_response._pole_zeros_response_getter( + PoleZerosResponseColumn.PROTO_RX_ZERO_NUM_Y + ) + assert len(proto_rx_zero_num_y) == 5 + assert proto_rx_zero_num_y[0] == pytest.approx(0.0) + assert proto_rx_zero_num_y[1] == pytest.approx(0.0) + assert proto_rx_zero_num_y[2] == pytest.approx(0.0) + assert proto_rx_zero_num_y[3] == pytest.approx(0.0) + assert proto_rx_zero_num_y[4] == pytest.approx(0.0) + + def test_filter_vsg_analysis_enabled(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.ideal_response.vsg_analysis_enabled is False + design.ideal_response.vsg_analysis_enabled = True + assert design.ideal_response.vsg_analysis_enabled + + def test_frequency_response(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + freq, mag_db = design.ideal_response.frequency_response( + y_axis_parameter=FrequencyResponseColumn.MAGNITUDE_DB, + minimum_frequency="200 MHz", + maximum_frequency="5 GHz", + vsg_analysis_enabled=False, + ) + assert len(freq) == 500 + assert freq[100] == pytest.approx(380730787.74317527) + assert freq[300] == pytest.approx(1379729661.4612174) + assert freq[-1] == pytest.approx(4967914631.382509) + assert len(mag_db) == 500 + assert mag_db[100] == pytest.approx(-0.0002779395744451339) + assert mag_db[300] == pytest.approx(-14.14973347970826) + assert mag_db[-1] == pytest.approx(-69.61741290615645) + + def test_time_response(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + time, step_response = design.ideal_response.time_response( + y_axis_parameter=TimeResponseColumn.STEP_RESPONSE, + minimum_time="0 ns", + maximum_time="10 ns", + vsg_analysis_enabled=False, + ) + assert len(time) == 300 + assert time[100] == pytest.approx(3.334e-09) + assert time[200] == pytest.approx(6.667e-09) + assert time[-1] == pytest.approx(9.9667e-09) + assert len(step_response) == 300 + assert step_response[100] == pytest.approx(1.0006647872833518) + assert step_response[200] == pytest.approx(0.9999988501385255) + assert step_response[-1] == pytest.approx(0.9999999965045667) + + def test_s_parameters(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + freq, s21_db = design.ideal_response.s_parameters( + y_axis_parameter=SParametersResponseColumn.S21_DB, + minimum_frequency="200 MHz", + maximum_frequency="5 GHz", + ) + assert len(freq) == 500 + assert freq[100] == pytest.approx(380730787.74317527) + assert freq[300] == pytest.approx(1379729661.4612174) + assert freq[-1] == pytest.approx(4967914631.382509) + assert len(s21_db) == 500 + assert s21_db[100] == pytest.approx(-0.0002779395744451339) + assert s21_db[300] == pytest.approx(-14.14973347970826) + assert s21_db[-1] == pytest.approx(-69.61741290615645) + + def test_pole_zero_locations(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + tx_zero_den_x, tx_zero_den_y = design.ideal_response.pole_zero_locations( + x_axis_parameter=PoleZerosResponseColumn.TX_ZERO_DEN_X, + y_axis_parameter=PoleZerosResponseColumn.TX_ZERO_DEN_Y, + ) + assert len(tx_zero_den_x) == 5 + assert tx_zero_den_x[0] == pytest.approx(-1000000000.0) + assert tx_zero_den_x[1] == pytest.approx(-809016994.3749474) + assert tx_zero_den_x[2] == pytest.approx(-809016994.3749474) + assert tx_zero_den_x[3] == pytest.approx(-309016994.3749475) + assert tx_zero_den_x[4] == pytest.approx(-309016994.3749475) + assert len(tx_zero_den_y) == 5 + assert tx_zero_den_y[0] == pytest.approx(0.0) + assert tx_zero_den_y[1] == pytest.approx(587785252.2924731) + assert tx_zero_den_y[2] == pytest.approx(-587785252.2924731) + assert tx_zero_den_y[3] == pytest.approx(951056516.2951534) + assert tx_zero_den_y[4] == pytest.approx(-951056516.2951534) + + def test_transfer_function_response(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.ideal_response.transfer_function_response().splitlines() == read_resource_file( + "transferfunction.ckt" + ) diff --git a/_unittest/test_45_FilterSolutions/test_filter/test_multiple_bands_table.py b/_unittest/test_45_FilterSolutions/test_filter/test_multiple_bands_table.py new file mode 100644 index 00000000000..39d180d9a05 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_filter/test_multiple_bands_table.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.generic.general_methods import is_linux + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + def test_row_count(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + assert design.multiple_bands_table.row_count == 2 + + def test_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + assert design.multiple_bands_table.row(0) == ("2G", "3G") + + def test_update_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + with pytest.raises(RuntimeError) as info: + design.multiple_bands_table.update_row(0) + assert info.value.args[0] == "It is not possible to update table with an empty value" + design.multiple_bands_table.update_row(0, lower_frequency="100M") + assert design.multiple_bands_table.row(0) == ("100M", "3G") + design.multiple_bands_table.update_row(0, upper_frequency="4G") + assert design.multiple_bands_table.row(0) == ("100M", "4G") + design.multiple_bands_table.update_row(0, "200M", "5G") + assert design.multiple_bands_table.row(0) == ("200M", "5G") + + def test_append_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + design.multiple_bands_table.append_row("100M", "500M") + assert design.multiple_bands_table.row_count == 3 + assert design.multiple_bands_table.row(2) == ("100M", "500M") + with pytest.raises(RuntimeError) as info: + design.multiple_bands_table.append_row("", "500M") + assert info.value.args[0] == "It is not possible to append an empty value" + with pytest.raises(RuntimeError) as info: + design.multiple_bands_table.append_row("100M", "") + assert info.value.args[0] == "It is not possible to append an empty value" + + def test_insert_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + design.multiple_bands_table.insert_row(0, "200M", "5G") + assert design.multiple_bands_table.row(0) == ("200M", "5G") + design.multiple_bands_table.insert_row(0, lower_frequency="500M", upper_frequency="2G") + assert design.multiple_bands_table.row(0) == ("500M", "2G") + with pytest.raises(RuntimeError) as info: + design.multiple_bands_table.insert_row(22, lower_frequency="500M", upper_frequency="2G") + assert info.value.args[0] == "The rowIndex must be greater than zero and less than row count" + + def test_remove_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.attributes.filter_multiple_bands_enabled = True + design.multiple_bands_table.remove_row(0) + assert design.multiple_bands_table.row(0) == ("4G", "5G") + with pytest.raises(RuntimeError) as info: + design.multiple_bands_table.row(1) + assert ( + info.value.args[0] + == "Either no value is set for this band or the rowIndex must be greater than zero and less than row count" + ) diff --git a/_unittest/test_45_FilterSolutions/test_filter/test_transmission_zeros.py b/_unittest/test_45_FilterSolutions/test_filter/test_transmission_zeros.py new file mode 100644 index 00000000000..2fcc0edaae8 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_filter/test_transmission_zeros.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.generic.general_methods import is_linux + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + no_transmission_zero_msg = "This filter has no transmission zero at row 0" + no_transmission_zero_update_msg = "This filter has no transmission zero at row 0 to update" + input_value_blank_msg = "The input value is blank" + + def test_row_count(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert design.transmission_zeros_bandwidth.row_count == 0 + assert design.transmission_zeros_ratio.row_count == 0 + + def test_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.row(0) + assert info.value.args[0] == self.no_transmission_zero_msg + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.row(0) + assert info.value.args[0] == self.no_transmission_zero_msg + + def test_update_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.update_row(0, zero="1.3G", position="2") + assert info.value.args[0] == self.no_transmission_zero_update_msg + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.update_row(0, "1.3", "2") + assert info.value.args[0] == self.no_transmission_zero_update_msg + + def test_append_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.append_row(zero="", position="") + assert info.value.args[0] == self.input_value_blank_msg + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.append_row("", "") + assert info.value.args[0] == self.input_value_blank_msg + design.transmission_zeros_bandwidth.append_row("1600M") + assert design.transmission_zeros_bandwidth.row(0) == ("1600M", "") + design.transmission_zeros_bandwidth.clear_row() + design.transmission_zeros_bandwidth.append_row(zero="1600M", position="2") + assert design.transmission_zeros_bandwidth.row(0) == ("1600M", "2") + design.transmission_zeros_bandwidth.clear_row() + design.transmission_zeros_ratio.append_row("1.6") + assert design.transmission_zeros_ratio.row(0) == ("1.6", "") + design.transmission_zeros_ratio.clear_row() + design.transmission_zeros_ratio.append_row(zero="1.6", position="2") + assert design.transmission_zeros_ratio.row(0) == ("1.6", "2") + + def test_insert_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.insert_row(6, zero="1.3G", position="2") + assert info.value.args[0] == "The given index 6 is larger than zeros order" + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.insert_row(6, "1.3", "2") + assert info.value.args[0] == "The given index 6 is larger than zeros order" + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.insert_row(0, zero="", position="2") + assert info.value.args[0] == self.input_value_blank_msg + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.insert_row(0, "", "") + assert info.value.args[0] == self.input_value_blank_msg + design.transmission_zeros_bandwidth.insert_row(0, "1600M") + assert design.transmission_zeros_bandwidth.row(0) == ("1600M", "") + design.transmission_zeros_bandwidth.insert_row(0, zero="1600M", position="2") + assert design.transmission_zeros_bandwidth.row(0) == ("1600M", "2") + design.transmission_zeros_bandwidth.clear_row() + design.transmission_zeros_ratio.insert_row(0, "1.6") + assert design.transmission_zeros_ratio.row(0) == ("1.6", "") + design.transmission_zeros_ratio.insert_row(0, zero="1.6", position="2") + assert design.transmission_zeros_ratio.row(0) == ("1.6", "2") + + def test_remove_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.remove_row(2) + assert info.value.args[0] == "The given index 2 is larger than zeros order" + design.transmission_zeros_bandwidth.append_row(zero="1600M", position="2") + design.transmission_zeros_bandwidth.remove_row(0) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.row(0) + assert info.value.args[0] == self.no_transmission_zero_msg + + def test_clear_row(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.transmission_zeros_bandwidth.insert_row(0, zero="1600M", position="2") + assert design.transmission_zeros_bandwidth.row(0) == ("1600M", "2") + design.transmission_zeros_bandwidth.clear_row() + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_bandwidth.row(0) + assert info.value.args[0] == self.no_transmission_zero_msg + design.transmission_zeros_ratio.insert_row(0, zero="1.6", position="2") + assert design.transmission_zeros_ratio.row(0) == ("1.6", "2") + design.transmission_zeros_ratio.clear_row() + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.row(0) + assert info.value.args[0] == self.no_transmission_zero_msg + + def test_restore_default_positions(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + design.transmission_zeros_bandwidth.insert_row(0, zero="1600M", position="2") + design.transmission_zeros_bandwidth.restore_default_positions() + assert design.transmission_zeros_bandwidth.row(0) == ("1600M", "3") + design.transmission_zeros_ratio.insert_row(0, zero="1.6", position="2") + design.transmission_zeros_ratio.restore_default_positions() + assert design.transmission_zeros_ratio.row(0) == ("1.6", "3") diff --git a/_unittest/test_45_FilterSolutions/test_lumped_filter/__init__.py b/_unittest/test_45_FilterSolutions/test_lumped_filter/__init__.py new file mode 100644 index 00000000000..9c4476773da --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_lumped_filter/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. diff --git a/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_nodes_and_leads.py b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_nodes_and_leads.py new file mode 100644 index 00000000000..0d213741854 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_nodes_and_leads.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config + +# from ..filtersolutions_resources import resource_path +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.generic.general_methods import is_linux + +from ..resources import read_resource_file + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + + def test_lumped_c_node_capacitor(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.c_node_capacitor == "0" + lumpdesign.leads_and_nodes.c_node_capacitor = "1n" + assert lumpdesign.leads_and_nodes.c_node_capacitor == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("c_node_capacitor.ckt") + + def test_lumped_c_lead_inductor(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.c_lead_inductor == "0" + lumpdesign.leads_and_nodes.c_lead_inductor = "1n" + assert lumpdesign.leads_and_nodes.c_lead_inductor == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("c_lead_inductor.ckt") + + def test_lumped_l_node_capacitor(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.l_node_capacitor == "0" + lumpdesign.leads_and_nodes.l_node_capacitor = "1n" + assert lumpdesign.leads_and_nodes.l_node_capacitor == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("l_node_capacitor.ckt") + + def test_lumped_l_lead_inductor(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.l_lead_inductor == "0" + lumpdesign.leads_and_nodes.l_lead_inductor = "1n" + assert lumpdesign.leads_and_nodes.l_lead_inductor == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("l_lead_inductor.ckt") + + def test_lumped_r_node_capacitor(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.r_node_capacitor == "0" + lumpdesign.leads_and_nodes.r_node_capacitor = "1n" + assert lumpdesign.leads_and_nodes.r_node_capacitor == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("r_node_capacitor.ckt") + + def test_lumped_r_lead_inductor(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.r_lead_inductor == "0" + lumpdesign.leads_and_nodes.r_lead_inductor = "1n" + assert lumpdesign.leads_and_nodes.r_lead_inductor == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("r_lead_inductor.ckt") + + def test_lumped_c_node_compensate(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.c_node_compensate is False + lumpdesign.leads_and_nodes.c_node_compensate = True + assert lumpdesign.leads_and_nodes.c_node_compensate + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("c_node_compensate.ckt") + + def test_lumped_l_node_compensate(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.leads_and_nodes.l_node_compensate is False + lumpdesign.leads_and_nodes.l_node_compensate = True + assert lumpdesign.leads_and_nodes.l_node_compensate + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("l_node_compensate.ckt") diff --git a/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_parasitics.py b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_parasitics.py new file mode 100644 index 00000000000..c56af561f4d --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_parasitics.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.generic.general_methods import is_linux + +from ..resources import read_resource_file + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + + def test_lumped_capacitor_q(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.capacitor_q == "Inf" + lumpdesign.parasitics.capacitor_q = "100" + assert lumpdesign.parasitics.capacitor_q == "100" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("capacitor_q.ckt") + + def test_lumped_capacitor_rs(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.capacitor_rs == "0" + lumpdesign.parasitics.capacitor_rs = "1" + assert lumpdesign.parasitics.capacitor_rs == "1" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("capacitor_rs.ckt") + + def test_lumped_capacitor_rp(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.capacitor_rp == "Inf" + lumpdesign.parasitics.capacitor_rp = "1000" + assert lumpdesign.parasitics.capacitor_rp == "1000" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("capacitor_rp.ckt") + + def test_lumped_capacitor_ls(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.capacitor_ls == "0" + lumpdesign.parasitics.capacitor_ls = "1n" + assert lumpdesign.parasitics.capacitor_ls == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("capacitor_ls.ckt") + + def test_lumped_inductor_q(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.inductor_q == "Inf" + lumpdesign.parasitics.inductor_q = "100" + assert lumpdesign.parasitics.inductor_q == "100" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("inductor_q.ckt") + + def test_lumped_inductor_rs(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.inductor_rs == "0" + lumpdesign.parasitics.inductor_rs = "1" + assert lumpdesign.parasitics.inductor_rs == "1" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("inductor_rs.ckt") + + def test_lumped_inductor_rp(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.inductor_rp == "Inf" + lumpdesign.parasitics.inductor_rp = "1000" + assert lumpdesign.parasitics.inductor_rp == "1000" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("inductor_rp.ckt") + + def test_lumped_inductor_cp(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.parasitics.inductor_cp == "0" + lumpdesign.parasitics.inductor_cp = "1n" + assert lumpdesign.parasitics.inductor_cp == "1n" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("inductor_cp.ckt") diff --git a/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_termination_impedance.py b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_termination_impedance.py new file mode 100644 index 00000000000..69b4d1e986d --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_termination_impedance.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.filtersolutions_core.lumped_termination_impedance import ComplexReactanceType +from pyaedt.filtersolutions_core.lumped_termination_impedance import ComplexTerminationDefinition +from pyaedt.generic.general_methods import is_linux + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + def test_row_count(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + assert lumpdesign.source_impedance_table.row_count == 3 + assert lumpdesign.load_impedance_table.row_count == 3 + + def test_row(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + assert lumpdesign.source_impedance_table.row(0) == ("0.100G", "1.000", "0.000") + assert lumpdesign.load_impedance_table.row(0) == ("0.100G", "1.000", "0.000") + + def test_update_row(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + with pytest.raises(RuntimeError) as info: + lumpdesign.source_impedance_table.update_row(0) + assert info.value.args[0] == "There is no input value to update" + with pytest.raises(RuntimeError) as info: + lumpdesign.load_impedance_table.update_row(0) + assert info.value.args[0] == "There is no input value to update" + lumpdesign.source_impedance_table.update_row(0, "2G", "22", "11") + assert lumpdesign.source_impedance_table.row(0) == ("2G", "22", "11") + lumpdesign.load_impedance_table.update_row(0, "2G", "22", "11") + assert lumpdesign.load_impedance_table.row(0) == ("2G", "22", "11") + lumpdesign.source_impedance_table.update_row(0, frequency="4G") + assert lumpdesign.source_impedance_table.row(0) == ("4G", "22", "11") + lumpdesign.load_impedance_table.update_row(0, frequency="4G") + assert lumpdesign.load_impedance_table.row(0) == ("4G", "22", "11") + lumpdesign.source_impedance_table.update_row(0, "2G", "50", "0") + assert lumpdesign.source_impedance_table.row(0) == ("2G", "50", "0") + lumpdesign.load_impedance_table.update_row(0, "2G", "50", "0") + assert lumpdesign.load_impedance_table.row(0) == ("2G", "50", "0") + + def test_append_row(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + lumpdesign.source_impedance_table.append_row("100M", "10", "20") + assert lumpdesign.source_impedance_table.row_count == 4 + assert lumpdesign.source_impedance_table.row(3) == ("100M", "10", "20") + lumpdesign.topology.complex_termination = True + lumpdesign.load_impedance_table.append_row("100M", "10", "20") + assert lumpdesign.load_impedance_table.row_count == 4 + assert lumpdesign.load_impedance_table.row(3) == ("100M", "10", "20") + + def test_insert_row(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + lumpdesign.source_impedance_table.insert_row(0, "2G", "50", "0") + assert lumpdesign.source_impedance_table.row(0) == ("2G", "50", "0") + lumpdesign.load_impedance_table.insert_row(0, "2G", "50", "0") + assert lumpdesign.load_impedance_table.row(0) == ("2G", "50", "0") + + def test_remove_row(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + lumpdesign.source_impedance_table.remove_row(0) + assert lumpdesign.source_impedance_table.row(0) == ("1.000G", "1.000", "0.000") + with pytest.raises(RuntimeError) as info: + lumpdesign.source_impedance_table.row(2) + assert info.value.args[0] == "No value is set for this band" + lumpdesign.load_impedance_table.remove_row(0) + assert lumpdesign.load_impedance_table.row(0) == ("1.000G", "1.000", "0.000") + with pytest.raises(RuntimeError) as info: + lumpdesign.load_impedance_table.row(2) + assert info.value.args[0] == "No value is set for this band" + + def test_complex_definition(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + assert len(ComplexTerminationDefinition) == 4 + assert lumpdesign.source_impedance_table.complex_definition == ComplexTerminationDefinition.CARTESIAN + for cdef in ComplexTerminationDefinition: + lumpdesign.source_impedance_table.complex_definition = cdef + assert lumpdesign.source_impedance_table.complex_definition == cdef + assert lumpdesign.load_impedance_table.complex_definition == ComplexTerminationDefinition.CARTESIAN + for cdef in ComplexTerminationDefinition: + lumpdesign.load_impedance_table.complex_definition = cdef + assert lumpdesign.load_impedance_table.complex_definition == cdef + + def test_reactance_type(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + assert len(ComplexReactanceType) == 3 + assert lumpdesign.source_impedance_table.reactance_type == ComplexReactanceType.REAC + for creac in ComplexReactanceType: + lumpdesign.source_impedance_table.reactance_type = creac + assert lumpdesign.source_impedance_table.reactance_type == creac + assert lumpdesign.load_impedance_table.reactance_type == ComplexReactanceType.REAC + for creac in ComplexReactanceType: + lumpdesign.load_impedance_table.reactance_type = creac + assert lumpdesign.load_impedance_table.reactance_type == creac + + def test_compensation_enabled(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + assert lumpdesign.source_impedance_table.compensation_enabled is False + lumpdesign.source_impedance_table.compensation_enabled = True + assert lumpdesign.source_impedance_table.compensation_enabled + assert lumpdesign.load_impedance_table.compensation_enabled is False + lumpdesign.load_impedance_table.compensation_enabled = True + assert lumpdesign.load_impedance_table.compensation_enabled + + def test_compensation_order(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + lumpdesign.source_impedance_table.compensation_enabled = True + assert lumpdesign.source_impedance_table.compensation_order == 2 + with pytest.raises(RuntimeError) as info: + lumpdesign.source_impedance_table.compensation_order = 0 + assert info.value.args[0] == "The minimum impedance compensation order is 1" + for i in range(1, 22): + lumpdesign.source_impedance_table.compensation_order = i + assert lumpdesign.source_impedance_table.compensation_order == i + with pytest.raises(RuntimeError) as info: + lumpdesign.source_impedance_table.compensation_order = 22 + assert info.value.args[0] == "The maximum impedance compensation order is 21" + lumpdesign.load_impedance_table.compensation_enabled = True + assert lumpdesign.load_impedance_table.compensation_order == 2 + with pytest.raises(RuntimeError) as info: + lumpdesign.load_impedance_table.compensation_order = 0 + assert info.value.args[0] == "The minimum impedance compensation order is 1" + for i in range(1, 22): + lumpdesign.load_impedance_table.compensation_order = i + assert lumpdesign.load_impedance_table.compensation_order == i + with pytest.raises(RuntimeError) as info: + lumpdesign.load_impedance_table.compensation_order = 22 + assert info.value.args[0] == "The maximum impedance compensation order is 21" diff --git a/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_topology.py b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_topology.py new file mode 100644 index 00000000000..9b10710c1fb --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_lumped_filter/test_lumped_topology.py @@ -0,0 +1,313 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import DiplexerType +from pyaedt.filtersolutions_core.attributes import FilterClass +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.filtersolutions_core.attributes import FilterType +from pyaedt.generic.general_methods import is_linux + +from ..resources import read_resource_file + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + def test_lumped_generator_resistor_30(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.topology.generator_resistor == "50" + lumpdesign.topology.generator_resistor = "30" + assert lumpdesign.topology.generator_resistor == "30" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("generator_resistor.ckt") + + def test_lumped_load_resistor_30(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.topology.load_resistor == "50" + lumpdesign.topology.load_resistor = "30" + assert lumpdesign.topology.load_resistor == "30" + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("laod_resistor.ckt") + + def test_lumped_current_source(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.topology.current_source is False + lumpdesign.topology.current_source = True + assert lumpdesign.topology.current_source + + def test_lumped_first_shunt(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.topology.first_shunt + lumpdesign.topology.first_shunt = True + assert lumpdesign.topology.first_shunt + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("first_shunt.ckt") + + def test_lumped_first_series(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.topology.first_shunt + lumpdesign.topology.first_shunt = False + assert lumpdesign.topology.first_shunt is False + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("first_series.ckt") + + def test_lumped_bridge_t(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.bridge_t is False + lumpdesign.topology.bridge_t = True + assert lumpdesign.topology.bridge_t + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("bridge_t.ckt") + + def test_lumped_bridge_t_low(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_1 + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_1 + lumpdesign.attributes.diplexer_type = DiplexerType.HI_LO + assert lumpdesign.attributes.diplexer_type == DiplexerType.HI_LO + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.bridge_t_low is False + lumpdesign.topology.bridge_t_low = True + assert lumpdesign.topology.bridge_t_low + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("bridge_t_low.ckt") + + def test_lumped_bridge_t_high(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_1 + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_1 + lumpdesign.attributes.diplexer_type = DiplexerType.HI_LO + assert lumpdesign.attributes.diplexer_type == DiplexerType.HI_LO + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.bridge_t_high is False + lumpdesign.topology.bridge_t_high = True + assert lumpdesign.topology.bridge_t_high + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("bridge_t_high.ckt") + + def test_lumped_equal_inductors(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.topology.equal_inductors is False + lumpdesign.topology.equal_inductors = True + assert lumpdesign.topology.equal_inductors + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("equal_inductors.ckt") + + def test_lumped_equal_capacitors(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + lumpdesign.topology.zig_zag = True + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.zig_zag + assert lumpdesign.topology.min_cap is False + assert lumpdesign.topology.equal_capacitors is False + lumpdesign.topology.min_cap = True + lumpdesign.topology.equal_capacitors = True + assert lumpdesign.topology.min_cap + assert lumpdesign.topology.equal_capacitors + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("equal_capacitors.ckt") + + def test_lumped_equal_legs(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.topology.equal_legs is False + lumpdesign.topology.equal_legs = True + assert lumpdesign.topology.equal_legs + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("equal_legs.ckt") + + def test_lumped_high_low_pass(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.topology.high_low_pass is False + lumpdesign.topology.high_low_pass = True + assert lumpdesign.topology.high_low_pass + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("high_low_pass.ckt") + + def test_lumped_high_low_pass_min_ind(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.high_low_pass_min_ind is False + lumpdesign.topology.high_low_pass_min_ind = True + assert lumpdesign.topology.high_low_pass_min_ind + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("high_low_pass_min_ind.ckt") + + def test_lumped_zig_zag(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.zig_zag is False + lumpdesign.topology.zig_zag = True + assert lumpdesign.topology.zig_zag + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("zig_zag.ckt") + + def test_lumped_min_ind(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + lumpdesign.topology.zig_zag = True + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.zig_zag + assert lumpdesign.topology.min_ind + lumpdesign.topology.min_ind = True + assert lumpdesign.topology.min_ind + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("min_ind.ckt") + + def test_lumped_min_cap(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + lumpdesign.topology.zig_zag = True + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.zig_zag + assert lumpdesign.topology.min_cap is False + lumpdesign.topology.min_cap = True + assert lumpdesign.topology.min_cap + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("min_cap.ckt") + + def test_lumped_set_source_res(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + lumpdesign.topology.zig_zag = True + lumpdesign.topology.set_source_res = False + assert lumpdesign.topology.set_source_res is False + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.zig_zag + lumpdesign.topology.set_source_res = True + assert lumpdesign.topology.set_source_res + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("set_source_res.ckt") + + def test_lumped_trap_topology(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + lumpdesign.topology.zig_zag = True + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.zig_zag + assert lumpdesign.topology.trap_topology is False + lumpdesign.topology.trap_topology = True + assert lumpdesign.topology.trap_topology + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("trap_topology.ckt") + + def test_lumped_node_cap_ground(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.attributes.filter_type = FilterType.ELLIPTIC + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.attributes.filter_type == FilterType.ELLIPTIC + assert lumpdesign.topology.node_cap_ground is False + lumpdesign.topology.node_cap_ground = True + assert lumpdesign.topology.node_cap_ground + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("node_cap_ground.ckt") + + def test_lumped_match_impedance(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.BAND_PASS + lumpdesign.topology.generator_resistor = "75" + assert lumpdesign.attributes.filter_class == FilterClass.BAND_PASS + assert lumpdesign.topology.generator_resistor == "75" + assert lumpdesign.topology.match_impedance is False + lumpdesign.topology.match_impedance = True + assert lumpdesign.topology.match_impedance + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("match_impedance.ckt") + + def test_lumped_complex_termination(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.topology.complex_termination is False + lumpdesign.topology.complex_termination = True + assert lumpdesign.topology.complex_termination + + def test_complex_element_tune_enabled(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.topology.complex_termination = True + assert lumpdesign.topology.complex_element_tune_enabled + lumpdesign.topology.complex_element_tune_enabled = False + assert lumpdesign.topology.complex_element_tune_enabled is False + + def test_lumped_circuit_export(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("netlist.ckt") + + def test_lumped_diplexer1_hi_lo(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_1 + lumpdesign.attributes.diplexer_type = DiplexerType.HI_LO + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_1 + assert lumpdesign.attributes.diplexer_type == DiplexerType.HI_LO + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("diplexer1_hi_lo.ckt") + + def test_lumped_diplexer1_bp_1(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_1 + lumpdesign.attributes.diplexer_type = DiplexerType.BP_1 + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_1 + assert lumpdesign.attributes.diplexer_type == DiplexerType.BP_1 + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("diplexer1_bp_1.ckt") + + def test_lumped_diplexer1_bp_2(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_1 + lumpdesign.attributes.diplexer_type = DiplexerType.BP_2 + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_1 + assert lumpdesign.attributes.diplexer_type == DiplexerType.BP_2 + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("diplexer1_bp_2.ckt") + + def test_lumped_diplexer2_bp_bs(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_2 + lumpdesign.attributes.diplexer_type = DiplexerType.BP_BS + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_2 + assert lumpdesign.attributes.diplexer_type == DiplexerType.BP_BS + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("diplexer2_bp_bs.ckt") + + def test_lumped_diplexer2_triplexer_1(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_2 + lumpdesign.attributes.diplexer_type = DiplexerType.TRIPLEXER_1 + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_2 + assert lumpdesign.attributes.diplexer_type == DiplexerType.TRIPLEXER_1 + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("diplexer2_triplexer_1.ckt") + + def test_lumped_diplexer2_triplexer_2(self): + lumpdesign = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + lumpdesign.attributes.filter_class = FilterClass.DIPLEXER_2 + lumpdesign.attributes.diplexer_type = DiplexerType.TRIPLEXER_2 + assert lumpdesign.attributes.filter_class == FilterClass.DIPLEXER_2 + assert lumpdesign.attributes.diplexer_type == DiplexerType.TRIPLEXER_2 + assert lumpdesign.topology.circuit_response().splitlines() == read_resource_file("diplexer2_triplexer_2.ckt") diff --git a/_unittest/test_45_FilterSolutions/test_raise_error.py b/_unittest/test_45_FilterSolutions/test_raise_error.py new file mode 100644 index 00000000000..d613dcded93 --- /dev/null +++ b/_unittest/test_45_FilterSolutions/test_raise_error.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from _unittest.conftest import config +from _unittest.test_45_FilterSolutions.test_filter import test_transmission_zeros +import pytest + +import pyaedt +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.generic.general_methods import is_linux + + +@pytest.mark.skipif(is_linux, reason="FilterSolutions API is not supported on Linux.") +@pytest.mark.skipif(config["desktopVersion"] < "2025.1", reason="Skipped on versions earlier than 2025.1") +class TestClass: + def test_raise_error(self): + design = pyaedt.FilterSolutions(implementation_type=FilterImplementation.LUMPED) + with pytest.raises(RuntimeError) as info: + design.transmission_zeros_ratio.row(0) + assert info.value.args[0] == test_transmission_zeros.TestClass.no_transmission_zero_msg diff --git a/codecov.yml b/codecov.yml index d200f680ce7..77a9f15f3a4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -25,4 +25,5 @@ coverage: - "pyaedt/common_rpc.py" # ignore folders and all its contents - "pyaedt/generic/grpc_plugin_dll_class.py" # ignore file to interact with AEDT grpc api - "pyaedt/edb.py" # ignore folders and all its contents - - "pyaedt/workflows/project/kernel_converter.py" # ignore folders and all its contents + - "pyaedt/filtersolutions_core" # ignore filtersolutions feature for the current release pending 2025R1 update + - "pyaedt/filtersolutions.py" # ignore filtersolutions feature for the current release pending 2025R1 update diff --git a/examples/08-FilterSolutions/Lumped_Element_Response.py b/examples/08-FilterSolutions/Lumped_Element_Response.py new file mode 100644 index 00000000000..07941e02385 --- /dev/null +++ b/examples/08-FilterSolutions/Lumped_Element_Response.py @@ -0,0 +1,65 @@ +""" +Design a lumped element filter +------------------------------ +This example shows how to use PyAEDT to use the ``FilterSolutions`` module to design and +visualize the frequency response of a band-pass Butterworth filter. +""" + +############################################################################### +# Perform required imports +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# Perform required imports. + +import pyaedt +import pyaedt.filtersolutions_core.attributes +from pyaedt.filtersolutions_core.attributes import FilterType, FilterClass, FilterImplementation +from pyaedt.filtersolutions_core.ideal_response import FrequencyResponseColumn +import matplotlib.pyplot as plt + +############################################################################### +# Create the lumped design +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# Create a lumped element filter design and assign the class, type, frequency, and order. +design = pyaedt.FilterSolutions(version="2025.1", implementation_type= FilterImplementation.LUMPED) +design.attributes.filter_class = FilterClass.BAND_PASS +design.attributes.filter_type = FilterType.BUTTERWORTH +design.attributes.pass_band_center_frequency = "1G" +design.attributes.pass_band_width_frequency = "500M" +design.attributes.filter_order = 5 + +############################################################################## +# Plot the frequency response of the filter +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Plot the frequency response of the filter without any transmission zeros. + +freq, mag_db = design.ideal_response.frequency_response(FrequencyResponseColumn.MAGNITUDE_DB) +plt.plot(freq, mag_db, linewidth=2.0, label="Without Tx Zero") +def format_plot(): + plt.xlabel("Frequency (Hz)") + plt.ylabel("Magnitude S21 (dB)") + plt.title("Ideal Frequency Response") + plt.xscale("log") + plt.legend() + plt.grid() +format_plot() +plt.show() + +############################################################################## +# Add a transmission zero to the filter design +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Add a transmission zeros that yields nulls separated by 2 times the pass band width (1 GHz). +# Plot the frequency response of the filter with the transmission zero. + +design.transmission_zeros_ratio.append_row("2.0") +freq_with_zero, mag_db_with_zero = design.ideal_response.frequency_response(FrequencyResponseColumn.MAGNITUDE_DB) +plt.plot(freq, mag_db, linewidth=2.0, label="Without Tx Zero") +plt.plot(freq_with_zero, mag_db_with_zero, linewidth=2.0, label="With Tx Zero") +format_plot() +plt.show() + +############################################################################## +# Generate the netlist for the designed filter +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Generate and print the netlist for the designed filter with the added transmission zero to filter. +netlist = design.topology.circuit_response() +print("Netlist: \n", netlist) diff --git a/pyaedt/__init__.py b/pyaedt/__init__.py index 813506620dc..3b919daee40 100644 --- a/pyaedt/__init__.py +++ b/pyaedt/__init__.py @@ -76,6 +76,7 @@ def custom_show_warning(message, category, filename, lineno, file=None, line=Non from pyaedt.generic.design_types import Circuit from pyaedt.generic.design_types import Desktop from pyaedt.generic.design_types import Emit +from pyaedt.generic.design_types import FilterSolutions from pyaedt.generic.design_types import Hfss from pyaedt.generic.design_types import Hfss3dLayout from pyaedt.generic.design_types import Icepak diff --git a/pyaedt/filtersolutions.py b/pyaedt/filtersolutions.py new file mode 100644 index 00000000000..fe83fbc73e7 --- /dev/null +++ b/pyaedt/filtersolutions.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import pyaedt +from pyaedt.filtersolutions_core.attributes import Attributes +from pyaedt.filtersolutions_core.attributes import FilterImplementation +from pyaedt.filtersolutions_core.graph_setup import GraphSetup +from pyaedt.filtersolutions_core.ideal_response import IdealResponse +from pyaedt.filtersolutions_core.lumped_nodes_and_leads import LumpedNodesandLeads +from pyaedt.filtersolutions_core.lumped_parasitics import LumpedParasitics +from pyaedt.filtersolutions_core.lumped_termination_impedance import LumpedTerminationImpedance +from pyaedt.filtersolutions_core.lumped_termination_impedance import TerminationType +from pyaedt.filtersolutions_core.lumped_topology import LumpedTopology +from pyaedt.filtersolutions_core.multiple_bands_table import MultipleBandsTable +from pyaedt.filtersolutions_core.transmission_zeros import TableFormat +from pyaedt.filtersolutions_core.transmission_zeros import TransmissionZeros + + +class FilterSolutions: + """Provides the ``FilterSolutions`` application interface. + + This class has access to ideal filter attributes and calculated output parameters. + + Parameters + ---------- + version: str, optional + Version of AEDT in ``xxxx.x`` format. The default is ``None``. + implementation_type: FilterImplementation, optional + Technology used to implement the filter. The default is ``LUMPED``. + The ``FilterImplementation`` enum provides the list of implementations. + + + Examples + -------- + Create a ``FilterSolutions`` instance with a band-pass elliptic ideal filter. + + >>> import pyaedt + >>> from pyaedt.filtersolutions_core.attributes import FilterImplementation + + >>> design = pyaedt.FilterSolutions(version="2025 R1", projectname= "fs1", + >>> implementation_type= FilterImplementation.LUMPED, + >>> ) + """ + + def __init__(self, version=None, implementation_type=None): + self.version = version + self.implementation_type = implementation_type + pyaedt.filtersolutions_core._dll_interface(version) + + if implementation_type == FilterImplementation.LUMPED or implementation_type is None: + self._init_lumped_design() + else: + raise RuntimeError("The " + str(implementation_type) + " is not supported in this release.") + + def _init_lumped_design(self): + """Initialize the ``FilterSolutions`` object to support a lumped filter design.""" + + self.attributes = Attributes() + self.ideal_response = IdealResponse() + self.graph_setup = GraphSetup() + self.topology = LumpedTopology() + self.parasitics = LumpedParasitics() + self.leads_and_nodes = LumpedNodesandLeads() + self.source_impedance_table = LumpedTerminationImpedance(TerminationType.SOURCE) + self.load_impedance_table = LumpedTerminationImpedance(TerminationType.LOAD) + self.multiple_bands_table = MultipleBandsTable() + self.transmission_zeros_ratio = TransmissionZeros(TableFormat.RATIO) + self.transmission_zeros_bandwidth = TransmissionZeros(TableFormat.BANDWIDTH) diff --git a/pyaedt/filtersolutions_core/__init__.py b/pyaedt/filtersolutions_core/__init__.py new file mode 100644 index 00000000000..c029850e7fd --- /dev/null +++ b/pyaedt/filtersolutions_core/__init__.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import sys + +from pyaedt.filtersolutions_core.dll_interface import DllInterface + +# Store the current module in a variable for easy access and modification within the module itself. +_this = sys.modules[__name__] + +# Initialize the internal DLL interface attribute to ``None``. This is set to an actual +# ``DllInterface`` instance when needed, implementing a lazy initialization pattern. +_this._internal_dll_interface = None + + +def _dll_interface(version=None) -> DllInterface: + if _this._internal_dll_interface is None: + _this._internal_dll_interface = DllInterface(show_gui=False, version=version) + elif version is not None and version != _this._internal_dll_interface.version: + raise Exception( + "The requested version {} does not match with the previously defined version {}.".format( + version, _this._internal_dll_interface.version + ) + ) + + return _this._internal_dll_interface + + +def api_version() -> str: + return _dll_interface().api_version() diff --git a/pyaedt/filtersolutions_core/attributes.py b/pyaedt/filtersolutions_core/attributes.py new file mode 100644 index 00000000000..919a02fad63 --- /dev/null +++ b/pyaedt/filtersolutions_core/attributes.py @@ -0,0 +1,1562 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import POINTER +from ctypes import byref +from ctypes import c_bool +from ctypes import c_char_p +from ctypes import c_int +from enum import Enum + +import pyaedt + + +class FilterType(Enum): + """Provides an enum of filter types with associated mathematical formulations. + + **Attributes:** + + - GAUSSIAN: Represents a Gaussian filter. + - BESSEL: Represents a Bessel filter. + - BUTTERWORTH: Represents a Butterworth filter. + - LEGENDRE: Represents a Legendre filter. + - CHEBYSHEV_I: Represents a Chevyshev type I filter. + - CHEBYSHEV_II: Represents a Chevyshev type II filter. + - HOURGLASS: Represents an hourglass filter. + - ELLIPTIC: Represents an elliptic filter. + + Custom, raised-cos, and matched filter types are not available in this release. + """ + + GAUSSIAN = 0 + BESSEL = 1 + BUTTERWORTH = 2 + LEGENDRE = 3 + CHEBYSHEV_I = 4 + CHEBYSHEV_II = 5 + HOURGLASS = 6 + ELLIPTIC = 7 + DELAY = 8 + + +# CUSTOM = 8 +# RAISED_COS = 9 +# MATCHED = 10 +# DELAY = 11 + + +class FilterClass(Enum): + """Provides an enum of filter types for single-band and multiple-bands filters. + + **Attributes:** + + - LOW_PASS: Represents a low-pass filter. + - HIGH_PASS: Represents a high-pass filter. + - DIPLEXER_1: Represents a first group of diplexer filter. + - BAND_PASS: Represents a band-pass filter. + - BAND_STOP: Represents a band-stop filter. + - DIPLEXER_2: Represents a second group of diplexer filter. + - LOW_BAND: Represents a combined low-pass and multi-band filter. + - BAND_HIGH: Represents a combined high-pass and multi-band filter. + - BAND_BAND: Represents a multi-band pass filter. + - STOP_STOP: Represents a multi-band stop filter. + """ + + LOW_PASS = 0 + HIGH_PASS = 1 + DIPLEXER_1 = 2 + BAND_PASS = 3 + BAND_STOP = 4 + DIPLEXER_2 = 5 + LOW_BAND = 6 + BAND_HIGH = 7 + BAND_BAND = 8 + STOP_STOP = 9 + + +class FilterImplementation(Enum): + """Provides an enum of filter implementation types. + + **Attributes:** + + - LUMPED: Represents a lumped implementation. + - DISTRIB: Represents a distributed implementation. + - ACTIVE: Represents an active implementation. + - SWCAP: Represents a switched capacitor implementation. + - DIGITAL: Represents a digital implementation. + """ + + LUMPED = 0 + DISTRIB = 1 + ACTIVE = 2 + SWCAP = 3 + DIGITAL = 4 + + +class DiplexerType(Enum): + """Provides an enum of diplexer and triplexer types. + + **Attributes:** + + - HI_LO: Represents a high-pass, low-pass diplexer type. + - BP_1: Represents a band-pass, band-pass diplexer type. + - BP_2: Represents a band-pass, band-pass diplexer type. + - BP_BS: Represents a band-pass, band-stop diplexer type. + - TRIPLEXER_1: Represents a low-pass, band-pass, and high-pass triplexer type. + - TRIPLEXER_2: Represents a low-pass, band-pass, and high-pass triplexer type. + """ + + HI_LO = 0 + BP_1 = 1 + BP_2 = 2 + BP_BS = 3 + TRIPLEXER_1 = 4 + TRIPLEXER_2 = 5 + + +class BesselRipplePercentage(Enum): + """Provides an enum of peak-to-peak group delay ripple magnitudes as percents of averages for Bessel filters. + + **Attributes:** + + - ZERO: 0% + - HALF: 0.5% + - ONE: 1% + - TWO: 2% + - FIVE: 5% + - TEN: 10% + """ + + ZERO = 0 + HALF = 1 + ONE = 2 + TWO = 3 + FIVE = 4 + TEN = 5 + + +class GaussianTransition(Enum): + """Provides an enum of transition attenuations in dB for Gaussian filters to improve group delay response. + + **Attributes:** + + - TRANSITION_NONE: 0dB + - TRANSITION_3_DB: 3dB + - TRANSITION_6_DB: 6dB + - TRANSITION_9_DB: 9dB + - TRANSITION_12_DB: 12dB + - TRANSITION_15_DB: 15dB + """ + + TRANSITION_NONE = 0 + TRANSITION_3_DB = 1 + TRANSITION_6_DB = 2 + TRANSITION_9_DB = 3 + TRANSITION_12_DB = 4 + TRANSITION_15_DB = 5 + + +class GaussianBesselReflection(Enum): + """Provides an enum of synthesis methods for Gaussian and Bessel filters. + + **Attributes:** + + - OPTION_1: The first method for filter synthesis. + - OPTION_2: The second method for filter synthesis. + - OPTION_3: The third method for filter synthesis. + """ + + OPTION_1 = 0 + OPTION_2 = 1 + OPTION_3 = 2 + + +class RippleConstrictionBandSelect(Enum): + """Provides an enum of the bands to apply constrict the ripple parameter. + + **Attributes:** + + - STOP: Stop band + - PASS: Pass band + - BOTH: Stop and pass bands + """ + + STOP = 0 + PASS = 1 + BOTH = 2 + + +class SinglePointRippleInfZeros(Enum): + """Provides an enum for either one or three non-infinite zeros at the single frequency point to confine the ripple. + + **Attributes:** + + - RIPPLE_INF_ZEROS_1: One zero + - RIPPLE_INF_ZEROS_3: Three zeros + """ + + RIPPLE_INF_ZEROS_1 = 0 + RIPPLE_INF_ZEROS_3 = 1 + + +class PassbandDefinition(Enum): + """Provides an enum to get either center frequency and bandwidth or corner frequencies. + + **Attributes:** + + - CENTER_FREQUENCY: Define the passband by the center frequency and bandwidth. + - CORNER_FREQUENCIES: Define the passband by the corner frequencies. + """ + + CENTER_FREQUENCY = 0 + CORNER_FREQUENCIES = 1 + + +class StopbandDefinition(Enum): + """Provides an enum for comparing the stop band parameter to the pass band parameter. + + **Attributes:** + + - RATIO: Ratio between the stop band and pass band frequencies. + - FREQUENCY: Explicit frequency. + - ATTENUATION_DB: Attenuation in decibels. + """ + + RATIO = 0 + FREQUENCY = 1 + ATTENUATION_DB = 2 + + +class Attributes: + """Defines attributes and parameters of filters. + + This class allows you to construct all the necessary attributes for the ``FilterDesign`` class. + """ + + def __init__(self): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._dll_interface.restore_defaults() + self._define_attributes_dll_functions() + + def _define_attributes_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.setFilterType.argtype = c_char_p + self._dll.setFilterType.restype = c_int + self._dll.getFilterType.argtypes = [c_char_p, c_int] + self._dll.getFilterType.restype = c_int + + self._dll.setFilterClass.argtype = c_char_p + self._dll.setFilterClass.restype = int + self._dll.getFilterClass.argtypes = [c_char_p, c_int] + self._dll.getFilterClass.restype = int + + self._dll.setFilterImplementation.argtype = c_char_p + self._dll.setFilterImplementation.restype = c_int + self._dll.getFilterImplementation.argtypes = [c_char_p, c_int] + self._dll.getFilterImplementation.restype = c_int + + self._dll.setMultipleBandsEnabled.argtype = c_bool + self._dll.setMultipleBandsEnabled.restype = c_int + self._dll.getMultipleBandsEnabled.argtype = POINTER(c_bool) + self._dll.getMultipleBandsEnabled.restype = c_int + + self._dll.setMultipleBandsLowPassFrequency.argtype = c_char_p + self._dll.setMultipleBandsLowPassFrequency.restype = c_int + self._dll.getMultipleBandsLowPassFrequency.argtypes = [c_char_p, c_int] + self._dll.getMultipleBandsLowPassFrequency.restype = c_int + + self._dll.setMultipleBandsHighPassFrequency.argtype = c_char_p + self._dll.setMultipleBandsHighPassFrequency.restype = c_int + self._dll.getMultipleBandsHighPassFrequency.argtypes = [c_char_p, c_int] + self._dll.getMultipleBandsHighPassFrequency.restype = c_int + + self._dll.setDiplexerType.argtype = c_char_p + self._dll.setDiplexerType.restype = c_int + self._dll.getDiplexerType.argtypes = [c_char_p, c_int] + self._dll.getDiplexerType.restype = c_int + + self._dll.setDiplexerType.argtype = c_char_p + self._dll.setDiplexerType.restype = c_int + self._dll.getDiplexerType.argtypes = [c_char_p, c_int] + self._dll.getDiplexerType.restype = c_int + + self._dll.setOrder.argtype = c_int + self._dll.setOrder.restype = c_int + self._dll.getOrder.argtype = POINTER(c_int) + self._dll.getOrder.restype = c_int + + self._dll.setMinimumOrderStopbandAttenuationdB.argtype = c_char_p + self._dll.setMinimumOrderStopbandAttenuationdB.restype = c_int + self._dll.getMinimumOrderStopbandAttenuationdB.argtypes = [c_char_p, c_int] + self._dll.getMinimumOrderStopbandAttenuationdB.restype = c_int + + self._dll.setMinimumOrderStopbandFrequency.argtype = c_char_p + self._dll.setMinimumOrderStopbandFrequency.restype = c_int + self._dll.getMinimumOrderStopbandFrequency.argtypes = [c_char_p, c_int] + self._dll.getMinimumOrderStopbandFrequency.restype = c_int + + self._dll.setMinimumOrderGroupDelayError.argtype = c_char_p + self._dll.setMinimumOrderGroupDelayError.restype = c_int + self._dll.getMinimumOrderGroupDelayError.argtypes = [c_char_p, c_int] + self._dll.getMinimumOrderGroupDelayError.restype = c_int + + self._dll.setMinimumOrderGroupDelayCutoff.argtype = c_char_p + self._dll.setMinimumOrderGroupDelayCutoff.restype = c_int + self._dll.getMinimumOrderGroupDelayCutoff.argtypes = [c_char_p, c_int] + self._dll.getMinimumOrderGroupDelayCutoff.restype = c_int + + self._dll.setIdealMinimumOrder.argtype = POINTER(c_int) + self._dll.setIdealMinimumOrder.restype = c_int + + self._dll.getErrorMessage.argtypes = [c_char_p, c_int] + self._dll.getErrorMessage.restype = c_int + + self._dll.setPassbandDef.argtype = c_int + self._dll.setPassbandDef.restype = c_int + self._dll.getPassbandDef.argtype = POINTER(c_int) + self._dll.getPassbandDef.restype = c_int + + self._dll.setCenterFrequency.argtype = c_char_p + self._dll.setCenterFrequency.restype = c_int + self._dll.getCenterFrequency.argtypes = [c_char_p, c_int] + self._dll.getCenterFrequency.restype = c_int + + self._dll.setDelayTime.argtype = c_char_p + self._dll.setDelayTime.restype = c_int + self._dll.getDelayTime.argtypes = [c_char_p, c_int] + self._dll.getDelayTime.restype = c_int + + self._dll.setPassbandFrequency.argtype = c_char_p + self._dll.setPassbandFrequency.restype = c_int + self._dll.getPassbandFrequency.argtypes = [c_char_p, c_int] + self._dll.getPassbandFrequency.restype = c_int + + self._dll.setLowerFrequency.argtype = c_char_p + self._dll.setLowerFrequency.restype = c_int + self._dll.getLowerFrequency.argtypes = [c_char_p, c_int] + self._dll.getLowerFrequency.restype = c_int + + self._dll.setUpperFrequency.argtype = c_char_p + self._dll.setUpperFrequency.restype = c_int + self._dll.getUpperFrequency.argtypes = [c_char_p, c_int] + self._dll.getUpperFrequency.restype = c_int + + self._dll.setStopbandDef.argtype = c_int + self._dll.setStopbandDef.restype = c_int + self._dll.getStopbandDef.argtype = POINTER(c_int) + self._dll.getStopbandDef.restype = c_int + + self._dll.setStopbandRatio.argtype = c_char_p + self._dll.setStopbandRatio.restype = c_int + self._dll.getStopbandRatio.argtypes = [c_char_p, c_int] + self._dll.getStopbandRatio.restype = c_int + + self._dll.setStopbandFrequency.argtype = c_char_p + self._dll.setStopbandFrequency.restype = c_int + self._dll.getStopbandFrequency.argtypes = [c_char_p, c_int] + self._dll.getStopbandFrequency.restype = c_int + + self._dll.setStopbandAttenuationdB.argtype = c_char_p + self._dll.setStopbandAttenuationdB.restype = c_int + self._dll.getStopbandAttenuationdB.argtypes = [c_char_p, c_int] + self._dll.getStopbandAttenuationdB.restype = c_int + + self._dll.setStandardCutoffEnabled.argtype = c_bool + self._dll.setStandardCutoffEnabled.restype = c_int + self._dll.getStandardCutoffEnabled.argtype = POINTER(c_bool) + self._dll.getStandardCutoffEnabled.restype = c_int + + self._dll.setEquirippleDelayEnabled.argtype = c_bool + self._dll.setEquirippleDelayEnabled.restype = c_int + self._dll.getEquirippleDelayEnabled.argtype = POINTER(c_bool) + self._dll.getEquirippleDelayEnabled.restype = c_int + + self._dll.setDelayRipplePeriod.argtype = c_char_p + self._dll.setDelayRipplePeriod.restype = c_int + self._dll.getDelayRipplePeriod.argtypes = [c_char_p, c_int] + self._dll.getDelayRipplePeriod.restype = c_int + + self._dll.setGroupDealyRipplePercentage.argtype = c_int + self._dll.setGroupDealyRipplePercentage.restype = c_int + self._dll.setGroupDealyRipplePercentage.argtype = POINTER(c_int) + self._dll.setGroupDealyRipplePercentage.restype = c_int + + self._dll.setCutoffAttenuationdB.argtype = c_char_p + self._dll.setCutoffAttenuationdB.restype = c_int + self._dll.getCutoffAttenuationdB.argtypes = [c_char_p, c_int] + self._dll.getCutoffAttenuationdB.restype = c_int + + self._dll.setBesselNormalizedDelayEnabled.argtype = c_bool + self._dll.setBesselNormalizedDelayEnabled.restype = c_int + self._dll.getBesselNormalizedDelayEnabled.argtype = POINTER(c_bool) + self._dll.getBesselNormalizedDelayEnabled.restype = c_int + + self._dll.setBesselEquiRippleDelayPeriod.argtype = c_char_p + self._dll.setBesselEquiRippleDelayPeriod.restype = c_int + self._dll.getBesselEquiRippleDelayPeriod.argtypes = [c_char_p, c_int] + self._dll.getBesselEquiRippleDelayPeriod.restype = c_int + + self._dll.setBesselRipplePercentage.argtype = c_int + self._dll.setBesselRipplePercentage.restype = c_int + self._dll.getBesselRipplePercentage.argtype = POINTER(c_int) + self._dll.getBesselRipplePercentage.restype = c_int + + self._dll.setPassbandRipple.argtype = c_char_p + self._dll.setPassbandRipple.restype = c_int + self._dll.getPassbandRipple.argtypes = [c_char_p, c_int] + self._dll.getPassbandRipple.restype = c_int + + self._dll.setArithSymmetry.argtype = c_bool + self._dll.setArithSymmetry.restype = c_int + self._dll.getArithSymmetry.argtype = POINTER(c_bool) + self._dll.getArithSymmetry.restype = c_int + + self._dll.setAsymmetric.argtype = c_bool + self._dll.setAsymmetric.restype = c_int + self._dll.getAsymmetric.argtype = POINTER(c_bool) + self._dll.getAsymmetric.restype = c_int + + self._dll.setAsymmetricLowOrder.argtype = c_int + self._dll.setAsymmetricLowOrder.restype = c_int + self._dll.getAsymmetricLowOrder.argtype = POINTER(c_int) + self._dll.getAsymmetricLowOrder.restype = c_int + + self._dll.setAsymmetricHighOrder.argtype = c_int + self._dll.setAsymmetricHighOrder.restype = c_int + self._dll.getAsymmetricHighOrder.argtype = POINTER(c_int) + self._dll.getAsymmetricHighOrder.restype = c_int + + self._dll.setAsymmetricLowStopbandRatio.argtype = c_char_p + self._dll.setAsymmetricLowStopbandRatio.restype = c_int + self._dll.getAsymmetricLowStopbandRatio.argtypes = [c_char_p, c_int] + self._dll.getAsymmetricLowStopbandRatio.restype = c_int + + self._dll.setAsymmetricHighStopbandRatio.argtype = c_char_p + self._dll.setAsymmetricHighStopbandRatio.restype = c_int + self._dll.getAsymmetricHighStopbandRatio.argtypes = [c_char_p, c_int] + self._dll.getAsymmetricHighStopbandRatio.restype = c_int + + self._dll.setAsymmetricLowStopbandAttenuationdB.argtype = c_char_p + self._dll.setAsymmetricLowStopbandAttenuationdB.restype = c_int + self._dll.getAsymmetricLowStopbandAttenuationdB.argtypes = [c_char_p, c_int] + self._dll.getAsymmetricLowStopbandAttenuationdB.restype = c_int + + self._dll.setAsymmetricHighStopbandAttenuationdB.argtype = c_char_p + self._dll.setAsymmetricHighStopbandAttenuationdB.restype = c_int + self._dll.getAsymmetricHighStopbandAttenuationdB.argtypes = [c_char_p, c_int] + self._dll.getAsymmetricHighStopbandAttenuationdB.restype = c_int + + self._dll.setGaussianTransition.argtype = c_char_p + self._dll.setGaussianTransition.restype = c_int + self._dll.getGaussianTransition.argtypes = [c_char_p, c_int] + self._dll.getGaussianTransition.restype = c_int + + self._dll.setGaussianBesselReflection.argtype = c_int + self._dll.setGaussianBesselReflection.restype = c_int + self._dll.getGaussianBesselReflection.argtype = POINTER(c_int) + self._dll.getGaussianBesselReflection.restype = c_int + + self._dll.setEvenOrderMode.argtype = c_bool + self._dll.setEvenOrderMode.restype = c_int + self._dll.getEvenOrderMode.argtype = POINTER(c_bool) + self._dll.getEvenOrderMode.restype = c_int + + self._dll.setEvenReflZeroTo0.argtype = c_bool + self._dll.setEvenReflZeroTo0.restype = c_int + self._dll.getEvenReflZeroTo0.argtype = POINTER(c_bool) + self._dll.getEvenReflZeroTo0.restype = c_int + + self._dll.setEvenTrnZeroToInf.argtype = c_bool + self._dll.setEvenTrnZeroToInf.restype = c_int + self._dll.getEvenTrnZeroToInf.argtype = POINTER(c_bool) + self._dll.getEvenTrnZeroToInf.restype = c_int + + self._dll.setConstrictRipple.argtype = c_bool + self._dll.setConstrictRipple.restype = c_int + self._dll.getConstrictRipple.argtype = POINTER(c_bool) + self._dll.getConstrictRipple.restype = c_int + + self._dll.setSinglePointRipple.argtype = c_bool + self._dll.setSinglePointRipple.restype = c_int + self._dll.getSinglePointRipple.argtype = POINTER(c_bool) + self._dll.getSinglePointRipple.restype = c_int + + self._dll.setHalfBandRipple.argtype = c_bool + self._dll.setHalfBandRipple.restype = c_int + self._dll.getHalfBandRipple.argtype = POINTER(c_bool) + self._dll.getHalfBandRipple.restype = c_int + + self._dll.setRippleConstrictionPercent.argtype = c_char_p + self._dll.setRippleConstrictionPercent.restype = c_int + self._dll.getRippleConstrictionPercent.argtypes = [c_char_p, c_int] + self._dll.getRippleConstrictionPercent.restype = c_int + + self._dll.setRippleConstrictionBandSelect.argtype = c_char_p + self._dll.setRippleConstrictionBandSelect.restype = c_int + self._dll.getRippleConstrictionBandSelect.argtypes = [c_char_p, c_int] + self._dll.getRippleConstrictionBandSelect.restype = c_int + + self._dll.setSinglePointRippleNoninfiniteZeros.argtype = c_char_p + self._dll.setSinglePointRippleNoninfiniteZeros.restype = c_int + self._dll.getSinglePointRippleNoninfiniteZeros.argtypes = [c_char_p, c_int] + self._dll.getSinglePointRippleNoninfiniteZeros.restype = c_int + + self._dll.setDelayEqualizer.argtype = c_bool + self._dll.setDelayEqualizer.restype = c_int + self._dll.getDelayEqualizer.argtype = POINTER(c_bool) + self._dll.getDelayEqualizer.restype = c_int + + self._dll.setDelayEqualizerOrder.argtype = c_int + self._dll.setDelayEqualizerOrder.restype = c_int + self._dll.getDelayEqualizerOrder.argtype = POINTER(c_int) + self._dll.getDelayEqualizerOrder.restype = c_int + + self._dll.setStandardDelayEquCut.argtype = c_bool + self._dll.setStandardDelayEquCut.restype = c_int + self._dll.getStandardDelayEquCut.argtype = POINTER(c_bool) + self._dll.getStandardDelayEquCut.restype = c_int + + self._dll.setDelayEquCutoffAttenuationdB.argtype = c_char_p + self._dll.setDelayEquCutoffAttenuationdB.restype = c_int + self._dll.getDelayEquCutoffAttenuationdB.argtypes = [c_char_p, c_int] + self._dll.getDelayEquCutoffAttenuationdB.restype = c_int + + @property + def filter_type(self) -> FilterType: + """Type (mathematical formulation) of the filter. The default is ``BUTTERWORTH``. + The ``FilterType`` enum provides a list of all types. + + Returns + ------- + :enum:`FilterType` + """ + type_string = self._dll_interface.get_string(self._dll.getFilterType) + return self._dll_interface.string_to_enum(FilterType, type_string) + + @filter_type.setter + def filter_type(self, filter_type: FilterType): + if filter_type: + string_value = self._dll_interface.enum_to_string(filter_type) + self._dll_interface.set_string(self._dll.setFilterType, string_value) + + @property + def filter_class(self) -> FilterClass: + """Class (band definition) of the filter. The default is ``LOW_PASS``. + The ``FilterClass`` enum provides a list of all classes. + + Returns + ------- + :enum:`FilterClass` + """ + type_string = self._dll_interface.get_string(self._dll.getFilterClass) + return self._dll_interface.string_to_enum(FilterClass, type_string) + + @filter_class.setter + def filter_class(self, filter_class: FilterClass): + if filter_class: + string_value = self._dll_interface.enum_to_string(filter_class) + self._dll_interface.set_string(self._dll.setFilterClass, string_value) + + @property + def filter_implementation(self) -> FilterImplementation: + """Technology for implementing the filter. The default is ``LUMPED``. + The ``FilterImplementation`` enum provides a list of all implementations. + + Returns + ------- + :enum:`FilterImplementation` + """ + type_string = self._dll_interface.get_string(self._dll.getFilterImplementation) + return self._dll_interface.string_to_enum(FilterImplementation, type_string) + + @filter_implementation.setter + def filter_implementation(self, filter_implementation: FilterImplementation): + if filter_implementation: + string_value = self._dll_interface.enum_to_string(filter_implementation) + self._dll_interface.set_string(self._dll.setFilterImplementation, string_value) + + @property + def diplexer_type(self) -> DiplexerType: + """Type of diplexer topology. This property is only applicable to lumped filters. + + - The default is ``HI_LO`` for the ``DIPLEXER_1`` filter class. + + - The default is ``BP_BS`` for the ``DIPLEXER_2`` filter class. + + The ``DiplexerType`` enum provides a full list of diplexer types. + + Returns + ------- + :enum:`DiplexerType` + """ + type_string = self._dll_interface.get_string(self._dll.getDiplexerType) + return self._dll_interface.string_to_enum(DiplexerType, type_string) + + @diplexer_type.setter + def diplexer_type(self, diplexer_type: DiplexerType): + string_value = self._dll_interface.enum_to_string(diplexer_type) + self._dll_interface.set_string(self._dll.setDiplexerType, string_value) + + @property + def filter_multiple_bands_enabled(self) -> bool: + """Flag indicating if the multiple bands table is enabled. + + Returns + ------- + bool + """ + filter_multiple_bands_enabled = c_bool() + status = self._dll.getMultipleBandsEnabled(byref(filter_multiple_bands_enabled)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(filter_multiple_bands_enabled.value) + + @filter_multiple_bands_enabled.setter + def filter_multiple_bands_enabled(self, filter_multiple_bands_enabled: bool): + status = self._dll.setMultipleBandsEnabled(filter_multiple_bands_enabled) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def filter_multiple_bands_low_pass_frequency(self) -> str: + """Multiple bands low-pass frequency of combined low-pass and band-pass filters. The default is ``1GHz``. + + Returns + ------- + str + """ + filter_multiple_bands_low_pass_freq_string = self._dll_interface.get_string( + self._dll.getMultipleBandsLowPassFrequency + ) + return filter_multiple_bands_low_pass_freq_string + + @filter_multiple_bands_low_pass_frequency.setter + def filter_multiple_bands_low_pass_frequency(self, filter_multiple_bands_low_pass_freq_string): + self._dll_interface.set_string( + self._dll.setMultipleBandsLowPassFrequency, + filter_multiple_bands_low_pass_freq_string, + ) + + @property + def filter_multiple_bands_high_pass_frequency(self) -> str: + """Multiple bands high-pass frequency of combined high-pass and band-pass filters. The default is ``1GHz``. + + Returns + ------- + str + """ + filter_multiple_bands_high_pass_freq_string = self._dll_interface.get_string( + self._dll.getMultipleBandsHighPassFrequency + ) + return filter_multiple_bands_high_pass_freq_string + + @filter_multiple_bands_high_pass_frequency.setter + def filter_multiple_bands_high_pass_frequency(self, filter_multiple_bands_high_pass_freq_string): + self._dll_interface.set_string( + self._dll.setMultipleBandsHighPassFrequency, + filter_multiple_bands_high_pass_freq_string, + ) + + @property + def order(self) -> int: + """Order of the filter. The default is ``5``. + + Returns + ------- + int + """ + order = c_int() + status = self._dll.getOrder(byref(order)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(order.value) + + @order.setter + def order(self, order: int): + status = self._dll.setOrder(order) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def minimum_order_stop_band_attenuation_db(self) -> str: + """Filter stop band attenuation in dB for calculation of the filter minimum order. + The default is ``50``. + + Returns + ------- + str + """ + minimum_order_stop_band_attenuation_db_string = self._dll_interface.get_string( + self._dll.getMinimumOrderStopbandAttenuationdB + ) + return minimum_order_stop_band_attenuation_db_string + + @minimum_order_stop_band_attenuation_db.setter + def minimum_order_stop_band_attenuation_db(self, minimum_order_stop_band_attenuation_db_string): + self._dll_interface.set_string( + self._dll.setMinimumOrderStopbandAttenuationdB, + minimum_order_stop_band_attenuation_db_string, + ) + + @property + def minimum_order_stop_band_frequency(self) -> str: + """Filter stop band frequency for calculation of the filter minimum order. + The default is ``10 GHz``. + + Returns + ------- + str + """ + minimum_order_stop_band_frequency_string = self._dll_interface.get_string( + self._dll.getMinimumOrderStopbandFrequency + ) + return minimum_order_stop_band_frequency_string + + @minimum_order_stop_band_frequency.setter + def minimum_order_stop_band_frequency(self, minimum_order_stop_band_frequency_string): + self._dll_interface.set_string( + self._dll.setMinimumOrderStopbandFrequency, + minimum_order_stop_band_frequency_string, + ) + + @property + def minimum_order_group_delay_error_percent(self) -> str: + """Filter maximum group delay in % for calculation of the filter minimum order. + The default is ``5``. + + Returns + ------- + str + """ + minimum_order_group_delay_error_percent_string = self._dll_interface.get_string( + self._dll.getMinimumOrderGroupDelayError + ) + return minimum_order_group_delay_error_percent_string + + @minimum_order_group_delay_error_percent.setter + def minimum_order_group_delay_error_percent(self, minimum_order_group_delay_error_percent): + self._dll_interface.set_string( + self._dll.setMinimumOrderGroupDelayError, + minimum_order_group_delay_error_percent, + ) + + @property + def minimum_order_group_delay_cutoff(self) -> str: + """Filter group delay cutoff frequency for calculation of the filter minimum order. + The default is ``10 GHz``. + + Returns + ------- + str + """ + minimum_order_group_delay_cutoff_string = self._dll_interface.get_string( + self._dll.getMinimumOrderGroupDelayCutoff + ) + return minimum_order_group_delay_cutoff_string + + @minimum_order_group_delay_cutoff.setter + def minimum_order_group_delay_cutoff(self, minimum_order_group_delay_cutoff_string): + self._dll_interface.set_string( + self._dll.setMinimumOrderGroupDelayCutoff, + minimum_order_group_delay_cutoff_string, + ) + + @property + def ideal_minimum_order(self) -> int: + """Filter minimum order for the defined stop band frequency and attenuation parameters. + + Returns + ------- + int + """ + minimum_order = c_int() + status = self._dll.setIdealMinimumOrder(byref(minimum_order)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(minimum_order.value) + + @property + def delay_time(self) -> str: + """Filter delay time. + The default is ``1 ns``. + + Returns + ------- + str + """ + delay_time_string = self._dll_interface.get_string(self._dll.getDelayTime) + return delay_time_string + + @delay_time.setter + def delay_time(self, delay_time_string): + self._dll_interface.set_string(self._dll.setDelayTime, delay_time_string) + + @property + def pass_band_definition(self) -> PassbandDefinition: + """Pass band frequency entry options. + The default is ``CENTER_FREQUENCY``. + + Returns + ------- + :enum:`PassbandDefinition` + """ + index = c_int() + pass_band_definition = list(PassbandDefinition) + status = self._dll.getPassbandDef(byref(index)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + pass_band_definition = pass_band_definition[index.value] + return pass_band_definition + + @pass_band_definition.setter + def pass_band_definition(self, column: PassbandDefinition): + status = self._dll.setPassbandDef(column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def pass_band_center_frequency(self) -> str: + """Filter pass band or center frequency. + The default is ``1 GHz``. + + Returns + ------- + str + """ + center_freq_string = self._dll_interface.get_string(self._dll.getCenterFrequency) + return center_freq_string + + @pass_band_center_frequency.setter + def pass_band_center_frequency(self, center_freq_string): + self._dll_interface.set_string(self._dll.setCenterFrequency, center_freq_string) + + @property + def pass_band_width_frequency(self) -> str: + """Pass band width frequency for band pass or band stop filters. + The default is ``200 MHz``. + + Returns + ------- + str + """ + pass_band_freq_string = self._dll_interface.get_string(self._dll.getPassbandFrequency) + return pass_band_freq_string + + @pass_band_width_frequency.setter + def pass_band_width_frequency(self, pass_band_freq_string): + self._dll_interface.set_string(self._dll.setPassbandFrequency, pass_band_freq_string) + + @property + def lower_frequency(self) -> str: + """Filter lower corner frequency. + The default is ``905 MHz``. + + Returns + ------- + str + """ + lower_freq_string = self._dll_interface.get_string(self._dll.getLowerFrequency) + return lower_freq_string + + @lower_frequency.setter + def lower_frequency(self, lower_freq_string): + self._dll_interface.set_string(self._dll.setLowerFrequency, lower_freq_string) + + @property + def upper_frequency(self) -> str: + """Filter upper corner frequency. + The default is ``1.105 MHz``. + + Returns + ------- + str + """ + upper_freq_string = self._dll_interface.get_string(self._dll.getUpperFrequency) + return upper_freq_string + + @upper_frequency.setter + def upper_frequency(self, upper_freq_string): + self._dll_interface.set_string(self._dll.setUpperFrequency, upper_freq_string) + + @property + def stop_band_definition(self) -> StopbandDefinition: + """Stop band parameter entry option. + The default is ``RATIO``. + + Returns + ------- + :enum:`StopbandDefinition` + """ + index = c_int() + stop_band_definition = list(StopbandDefinition) + status = self._dll.getStopbandDef(byref(index)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + stop_band_definition = stop_band_definition[index.value] + return stop_band_definition + + @stop_band_definition.setter + def stop_band_definition(self, column: StopbandDefinition): + status = self._dll.setStopbandDef(column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def stop_band_ratio(self) -> str: + """Filter stop band ratio. + The default is ``1.2``. + + Returns + ------- + str + """ + stop_band_ratio_string = self._dll_interface.get_string(self._dll.getStopbandRatio) + return stop_band_ratio_string + + @stop_band_ratio.setter + def stop_band_ratio(self, stop_band_ratio_string): + self._dll_interface.set_string(self._dll.setStopbandRatio, stop_band_ratio_string) + + @property + def stop_band_frequency(self) -> str: + """Filter stop band frequency. + The default is ``1.2 GHz``. + + Returns + ------- + str + """ + stop_band_frequency_string = self._dll_interface.get_string(self._dll.getStopbandFrequency) + return stop_band_frequency_string + + @stop_band_frequency.setter + def stop_band_frequency(self, stop_band_frequency_string): + self._dll_interface.set_string(self._dll.setStopbandFrequency, stop_band_frequency_string) + + @property + def stop_band_attenuation_db(self) -> str: + """Filter stop band attenuation in dB. + The default is ``60 dB``. + + Returns + ------- + str + """ + stop_band_attenuation_db_string = self._dll_interface.get_string(self._dll.getStopbandAttenuationdB) + return stop_band_attenuation_db_string + + @stop_band_attenuation_db.setter + def stop_band_attenuation_db(self, stop_band_attenuation_db_string): + self._dll_interface.set_string(self._dll.setStopbandAttenuationdB, stop_band_attenuation_db_string) + + @property + def standard_pass_band_attenuation(self) -> bool: + """Flag indicating if the standard cut is enabled. + + Returns + ------- + bool + """ + standard_pass_band_attenuation = c_bool() + status = self._dll.getStandardCutoffEnabled(byref(standard_pass_band_attenuation)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(standard_pass_band_attenuation.value) + + @standard_pass_band_attenuation.setter + def standard_pass_band_attenuation(self, standard_pass_band_attenuation: bool): + status = self._dll.setStandardCutoffEnabled(standard_pass_band_attenuation) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def equiripple_delay(self) -> bool: + """Flag indicating if the equiripple delay is enabled. + + Returns + ------- + bool + """ + equiripple_delay = c_bool() + status = self._dll.getEquirippleDelayEnabled(byref(equiripple_delay)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(equiripple_delay.value) + + @equiripple_delay.setter + def equiripple_delay(self, equiripple_delay: bool): + status = self._dll.setEquirippleDelayEnabled(equiripple_delay) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def group_delay_ripple_period(self) -> str: + """Filter approximate normalized group delay ripple period. + The default is ''2''. + + Returns + ------- + str + """ + group_delay_ripple_period_string = self._dll_interface.get_string(self._dll.getDelayRipplePeriod) + return group_delay_ripple_period_string + + @group_delay_ripple_period.setter + def group_delay_ripple_period(self, group_delay_ripple_period_string): + self._dll_interface.set_string( + self._dll.setDelayRipplePeriod, + group_delay_ripple_period_string, + ) + + @property + def normalized_group_delay_percentage(self) -> int: + """Bessel filter ripple percentage. + The default is ''0''. + + Returns + ------- + int + """ + index = c_int() + normalized_group_delay_percentage = list(BesselRipplePercentage) + status = self._dll.getGroupDealyRipplePercentage(byref(index)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + normalized_group_delay_percentage_string = normalized_group_delay_percentage[index.value] + return normalized_group_delay_percentage_string + + @normalized_group_delay_percentage.setter + def normalized_group_delay_percentage(self, column: BesselRipplePercentage): + status = self._dll.setGroupDealyRipplePercentage(column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def standard_pass_band_attenuation_value_db(self) -> str: + """Filter cut off attenuation in dB. + The default is ''3.01 dB''. + + Returns + ------- + str + """ + standard_pass_band_attenuation_value_db_string = self._dll_interface.get_string( + self._dll.getCutoffAttenuationdB + ) + return standard_pass_band_attenuation_value_db_string + + @standard_pass_band_attenuation_value_db.setter + def standard_pass_band_attenuation_value_db(self, standard_pass_band_attenuation_value_db_string): + self._dll_interface.set_string( + self._dll.setCutoffAttenuationdB, + standard_pass_band_attenuation_value_db_string, + ) + + @property + def bessel_normalized_delay(self) -> bool: + """Flag indicating if the normalized delay is enabled. + + Returns + ------- + bool + """ + bessel_normalized_delay = c_bool() + status = self._dll.getBesselNormalizedDelayEnabled(byref(bessel_normalized_delay)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(bessel_normalized_delay.value) + + @bessel_normalized_delay.setter + def bessel_normalized_delay(self, bessel_normalized_delay: bool): + status = self._dll.setBesselNormalizedDelayEnabled(bessel_normalized_delay) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def bessel_normalized_delay_period(self) -> str: + """Bessel filter normalized delay period. + The default is ''2''. + + Returns + ------- + str + """ + bessel_normalized_delay_period_string = self._dll_interface.get_string(self._dll.getBesselEquiRippleDelayPeriod) + return bessel_normalized_delay_period_string + + @bessel_normalized_delay_period.setter + def bessel_normalized_delay_period(self, bessel_normalized_delay_period_string): + self._dll_interface.set_string( + self._dll.setBesselEquiRippleDelayPeriod, + bessel_normalized_delay_period_string, + ) + + @property + def bessel_normalized_delay_percentage(self) -> int: + """Bessel filter ripple percentage. + The default is ''0''. + + Returns + ------- + int + """ + index = c_int() + bessel_normalized_delay_percentage = list(BesselRipplePercentage) + status = self._dll.getBesselRipplePercentage(byref(index)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + bessel_normalized_delay_percentage_string = bessel_normalized_delay_percentage[index.value] + return bessel_normalized_delay_percentage_string + + @bessel_normalized_delay_percentage.setter + def bessel_normalized_delay_percentage(self, column: BesselRipplePercentage): + status = self._dll.setBesselRipplePercentage(column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def pass_band_ripple(self) -> str: + """Filter pass band ripple in dB. + The default is ''0.05 dB''. + + Returns + ------- + str + """ + pass_band_ripple_string = self._dll_interface.get_string(self._dll.getPassbandRipple) + return pass_band_ripple_string + + @pass_band_ripple.setter + def pass_band_ripple(self, pass_band_ripple_string): + self._dll_interface.set_string(self._dll.setPassbandRipple, pass_band_ripple_string) + + @property + def arith_symmetry(self) -> bool: + """Flag indicating if the arithmetic symmetry is enabled. + + Returns + ------- + bool + """ + arith_symmetry = c_bool() + status = self._dll.getArithSymmetry(byref(arith_symmetry)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(arith_symmetry.value) + + @arith_symmetry.setter + def arith_symmetry(self, arith_symmetry: bool): + status = self._dll.setArithSymmetry(arith_symmetry) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def asymmetric(self) -> bool: + """Flag indicating if the asymmetric is enabled. + + Returns + ------- + bool + """ + asymmetric = c_bool() + status = self._dll.getAsymmetric(byref(asymmetric)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(asymmetric.value) + + @asymmetric.setter + def asymmetric(self, asymmetric: bool): + status = self._dll.setAsymmetric(asymmetric) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def asymmetric_low_order(self) -> int: + """Order for low side of an asymmetric filter. + The default is ''5''. + + Returns + ------- + int + """ + asymmetric_low_order = c_int() + status = self._dll.getAsymmetricLowOrder(byref(asymmetric_low_order)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(asymmetric_low_order.value) + + @asymmetric_low_order.setter + def asymmetric_low_order(self, asymmetric_low_order: int): + status = self._dll.setAsymmetricLowOrder(asymmetric_low_order) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def asymmetric_high_order(self) -> int: + """Order for high side of an asymmetric filter. + The default is ''5''. + + Returns + ------- + int + """ + asymmetric_high_order = c_int() + status = self._dll.getAsymmetricHighOrder(byref(asymmetric_high_order)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(asymmetric_high_order.value) + + @asymmetric_high_order.setter + def asymmetric_high_order(self, asymmetric_high_order: int): + status = self._dll.setAsymmetricHighOrder(asymmetric_high_order) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def asymmetric_low_stop_band_ratio(self) -> str: + """Stop-band ratio for low side of an asymmetric filter. + The default is ''1.2''. + + Returns + ------- + str + """ + asymmetric_low_stop_band_ratio_string = self._dll_interface.get_string(self._dll.getAsymmetricLowStopbandRatio) + return asymmetric_low_stop_band_ratio_string + + @asymmetric_low_stop_band_ratio.setter + def asymmetric_low_stop_band_ratio(self, asymmetric_low_stop_band_ratio_string): + self._dll_interface.set_string( + self._dll.setAsymmetricLowStopbandRatio, + asymmetric_low_stop_band_ratio_string, + ) + + @property + def asymmetric_high_stop_band_ratio(self) -> str: + """Stop-band ratio for high side of an asymmetric filter. + The default is ''1.2''. + + Returns + ------- + str + """ + asymmetric_high_stop_band_ratio_string = self._dll_interface.get_string( + self._dll.getAsymmetricHighStopbandRatio + ) + return asymmetric_high_stop_band_ratio_string + + @asymmetric_high_stop_band_ratio.setter + def asymmetric_high_stop_band_ratio(self, asymmetric_high_stop_band_ratio_string): + self._dll_interface.set_string( + self._dll.setAsymmetricHighStopbandRatio, + asymmetric_high_stop_band_ratio_string, + ) + + @property + def asymmetric_low_stop_band_attenuation_db(self) -> str: + """Stop-band attenuation for low side of an asymmetric filter. + The default is ''60 dB''. + + Returns + ------- + str + """ + asymmetric_low_stop_band_attenuation_db_string = self._dll_interface.get_string( + self._dll.getAsymmetricLowStopbandAttenuationdB + ) + return asymmetric_low_stop_band_attenuation_db_string + + @asymmetric_low_stop_band_attenuation_db.setter + def asymmetric_low_stop_band_attenuation_db(self, asymmetric_low_stop_band_attenuation_db_string): + self._dll_interface.set_string( + self._dll.setAsymmetricLowStopbandAttenuationdB, + asymmetric_low_stop_band_attenuation_db_string, + ) + + @property + def asymmetric_high_stop_band_attenuation_db(self) -> str: + """Stop-band attenuation for high side of an asymmetric filter. + The default is ''60 dB''. + + Returns + ------- + str + """ + asymmetric_high_stop_band_attenuation_db_string = self._dll_interface.get_string( + self._dll.getAsymmetricHighStopbandAttenuationdB + ) + return asymmetric_high_stop_band_attenuation_db_string + + @asymmetric_high_stop_band_attenuation_db.setter + def asymmetric_high_stop_band_attenuation_db(self, asymmetric_high_stop_band_attenuation_db_string): + self._dll_interface.set_string( + self._dll.setAsymmetricHighStopbandAttenuationdB, + asymmetric_high_stop_band_attenuation_db_string, + ) + + @property + def gaussian_transition(self) -> GaussianTransition: + """Gaussian filter transition option. + The default is ''TRANSITION_NONE''. + + Returns + ------- + :enum:`GaussianTransition` + """ + type_string = self._dll_interface.get_string(self._dll.getGaussianTransition) + type_string = "TRANSITION_" + type_string + return self._dll_interface.string_to_enum(GaussianTransition, type_string) + + @gaussian_transition.setter + def gaussian_transition(self, gaussian_transition: GaussianTransition): + string_value = self._dll_interface.enum_to_string(gaussian_transition) + self._dll_interface.set_string(self._dll.setGaussianTransition, string_value) + + @property + def gaussian_bessel_reflection(self) -> GaussianBesselReflection: + """Gaussian or Bessel filter reflection option. + The default is ''OPTION_1''. + + Returns + ------- + :enum:`GaussianBesselReflection` + """ + index = c_int() + gaussian_bessel_reflection = list(GaussianBesselReflection) + status = self._dll.getGaussianBesselReflection(byref(index)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + gaussian_bessel_reflection = gaussian_bessel_reflection[index.value] + return gaussian_bessel_reflection + + @gaussian_bessel_reflection.setter + def gaussian_bessel_reflection(self, column: GaussianBesselReflection): + status = self._dll.setGaussianBesselReflection(column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def even_order(self) -> bool: + """Flag indicating if the even order mode for a filter with even orders is enabled. + + Returns + ------- + bool + """ + even_order = c_bool() + status = self._dll.getEvenOrderMode(byref(even_order)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(even_order.value) + + @even_order.setter + def even_order(self, even_order: bool): + status = self._dll.setEvenOrderMode(even_order) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def even_order_refl_zero(self) -> bool: + """Flag indicating if the even order reflection zeros translation to 0 is enabled. + + Returns + ------- + bool + """ + even_order_refl_zero = c_bool() + status = self._dll.getEvenReflZeroTo0(byref(even_order_refl_zero)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(even_order_refl_zero.value) + + @even_order_refl_zero.setter + def even_order_refl_zero(self, even_order_refl_zero: bool): + status = self._dll.setEvenReflZeroTo0(even_order_refl_zero) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def even_order_trn_zero(self) -> bool: + """Flag indicating if the even order reflection zeros translation to infinite is enabled. + + Returns + ------- + bool + """ + even_order_trn_zero = c_bool() + status = self._dll.getEvenTrnZeroToInf(byref(even_order_trn_zero)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(even_order_trn_zero.value) + + @even_order_trn_zero.setter + def even_order_trn_zero(self, even_order_trn_zero: bool): + status = self._dll.setEvenTrnZeroToInf(even_order_trn_zero) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def constrict_ripple(self) -> bool: + """Flag indicating if the equiripple constriction is enabled. + + Returns + ------- + bool + """ + constrict_ripple = c_bool() + status = self._dll.getConstrictRipple(byref(constrict_ripple)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(constrict_ripple.value) + + @constrict_ripple.setter + def constrict_ripple(self, constrict_ripple: bool): + status = self._dll.setConstrictRipple(constrict_ripple) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def single_point_ripple(self) -> bool: + """Flag indicating if the ripple confinement to a single frequency point is enabled. + + Returns + ------- + bool + """ + single_point_ripple = c_bool() + status = self._dll.getSinglePointRipple(byref(single_point_ripple)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(single_point_ripple.value) + + @single_point_ripple.setter + def single_point_ripple(self, single_point_ripple: bool): + self._dll.setSinglePointRipple(single_point_ripple) + + @property + def half_band_ripple(self) -> bool: + """Flag indicating if the ripple with half of the zeros in the given band is enabled. + + Returns + ------- + bool + """ + half_band_point_ripple = c_bool() + status = self._dll.getHalfBandRipple(byref(half_band_point_ripple)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(half_band_point_ripple.value) + + @half_band_ripple.setter + def half_band_ripple(self, half_band_ripple: bool): + status = self._dll.setHalfBandRipple(half_band_ripple) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def constrict_ripple_percent(self) -> str: + """Filter ripple constriction percentage. + The default is ''50%''. + + Returns + ------- + str + """ + constrict_ripple_percent_string = self._dll_interface.get_string(self._dll.getRippleConstrictionPercent) + return constrict_ripple_percent_string + + @constrict_ripple_percent.setter + def constrict_ripple_percent(self, constrict_ripple_percent_string): + self._dll_interface.set_string(self._dll.setRippleConstrictionPercent, constrict_ripple_percent_string) + + @property + def ripple_constriction_band(self) -> RippleConstrictionBandSelect: + """Filter ripple constriction band option. + The default is ''STOP''. + + Returns + ------- + :enum:`RippleConstrictionBandSelect` + """ + type_string = self._dll_interface.get_string(self._dll.getRippleConstrictionBandSelect) + return self._dll_interface.string_to_enum(RippleConstrictionBandSelect, type_string) + + @ripple_constriction_band.setter + def ripple_constriction_band(self, ripple_constriction_band: RippleConstrictionBandSelect): + string_value = self._dll_interface.enum_to_string(ripple_constriction_band) + self._dll_interface.set_string(self._dll.setRippleConstrictionBandSelect, string_value) + + @property + def single_point_ripple_inf_zeros(self) -> SinglePointRippleInfZeros: + """Filter number of single point ripple infinite zeros. + The default is ''RIPPLE_INF_ZEROS_1''. + + Returns + ------- + :enum:`SinglePointRippleInfZeros` + """ + type_string = self._dll_interface.get_string(self._dll.getSinglePointRippleNoninfiniteZeros) + type_string = "RIPPLE_INF_ZEROS_" + type_string + return self._dll_interface.string_to_enum(SinglePointRippleInfZeros, type_string) + + @single_point_ripple_inf_zeros.setter + def single_point_ripple_inf_zeros(self, single_point_ripple_inf_zeros: SinglePointRippleInfZeros): + string_value = self._dll_interface.enum_to_string(single_point_ripple_inf_zeros) + self._dll_interface.set_string(self._dll.setSinglePointRippleNoninfiniteZeros, string_value) + + @property + def delay_equalizer(self) -> bool: + """Flag indicating if the delay equalizer is enabled. + + Returns + ------- + bool + """ + delay_equalizer = c_bool() + status = self._dll.getDelayEqualizer(byref(delay_equalizer)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(delay_equalizer.value) + + @delay_equalizer.setter + def delay_equalizer(self, delay_equalizer: bool): + self._dll.setDelayEqualizer(delay_equalizer) + + @property + def delay_equalizer_order(self) -> int: + """Filter delay equalizer order. + The default is ''2''. + + Returns + ------- + int + """ + delay_equalizer_order = c_int() + status = self._dll.getDelayEqualizerOrder(byref(delay_equalizer_order)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(delay_equalizer_order.value) + + @delay_equalizer_order.setter + def delay_equalizer_order(self, delay_equalizer_order: int): + status = self._dll.setDelayEqualizerOrder(delay_equalizer_order) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def standard_delay_equ_pass_band_attenuation(self) -> bool: + """Flag indicating if the standard delay equalizer attenuation is enabled. + + Returns + ------- + bool + """ + standard_dealy_equ_cut = c_bool() + status = self._dll.getStandardDelayEquCut(byref(standard_dealy_equ_cut)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(standard_dealy_equ_cut.value) + + @standard_delay_equ_pass_band_attenuation.setter + def standard_delay_equ_pass_band_attenuation(self, standard_delay_equ_pass_band_attenuation: bool): + status = self._dll.setStandardDelayEquCut(standard_delay_equ_pass_band_attenuation) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def standard_delay_equ_pass_band_attenuation_value_db(self) -> str: + """Filter standard delay equalizer cut Off attenuation in dB. + The default is ''3.01 dB''. + + Returns + ------- + str + """ + delay_equcutoff_attenuation_db_string = self._dll_interface.get_string(self._dll.getDelayEquCutoffAttenuationdB) + return delay_equcutoff_attenuation_db_string + + @standard_delay_equ_pass_band_attenuation_value_db.setter + def standard_delay_equ_pass_band_attenuation_value_db( + self, standard_delay_equ_pass_band_attenuation_value_db_string + ): + self._dll_interface.set_string( + self._dll.setDelayEquCutoffAttenuationdB, + standard_delay_equ_pass_band_attenuation_value_db_string, + ) diff --git a/pyaedt/filtersolutions_core/dll_interface.py b/pyaedt/filtersolutions_core/dll_interface.py new file mode 100644 index 00000000000..5d761f7f0df --- /dev/null +++ b/pyaedt/filtersolutions_core/dll_interface.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import ctypes +from enum import Enum +import os +import threading +import time +from typing import Callable + +from pyaedt.misc.misc import current_version +from pyaedt.misc.misc import installed_versions + + +class DllInterface: + """Interfaces with the FilterSolutions C++ API DLL.""" + + def __init__(self, show_gui=False, version=None): + self._init_dll_path(version) + self._init_dll(show_gui) + + def restore_defaults(self): + """Restore the state of the API, including all options and values, to the initial startup state.""" + status = self._dll.startApplication(self.show_gui) + self.raise_error(status) + + def _init_dll_path(self, version): + """Set DLL path and print the status of the DLL access to the screen.""" + latest_version = current_version() + if latest_version == "": + raise Exception("AEDT is not installed on your system. Install AEDT 2025 R1 or later.") + if version is None: + version = latest_version + if float(version[0:6]) < 2025: + raise ValueError( + "FilterSolutions supports AEDT version 2025 R1 and later. Recommended version is 2025 R1 or later." + ) + if not (version in installed_versions()) and not (version + "CL" in installed_versions()): + raise ValueError("Specified version {} is not installed on your system".format(version[0:6])) + self.dll_path = os.path.join(installed_versions()[version], "nuhertz/FilterSolutionsAPI.dll") + print("DLL Path:", self.dll_path) + if not os.path.isfile(self.dll_path): + raise RuntimeError(f"The 'FilterSolutions' API DLL was not found at {self.dll_path}.") # pragma: no cover + self.version = version + + def _init_dll(self, show_gui): + """Load DLL and initialize application parameters to default values.""" + + self._dll = ctypes.cdll.LoadLibrary(self.dll_path) + self._define_dll_functions() + self.show_gui = show_gui + if show_gui: # pragma: no cover + self._app_thread = threading.Thread(target=self._app_thread_task) + self._app_thread.start() + # TODO: Need some way to confirm that the GUI has completed initialization. + # Otherwise some subsequent API calls will fail. For now, sleep a few seconds. + time.sleep(5) + else: + status = self._dll.startApplication(False) + self.raise_error(status) + + print("DLL Loaded:", self.api_version()) + print("API Ready") + print("") + + def _app_thread_task(self): # pragma: no cover + """Print the status of running application thread.""" + print("Starting Application::Run thread") + status = self._dll.startApplication(self.show_gui) + self.raise_error(status) + + def _define_dll_functions(self): + """Define DLL function.""" + self._dll.getVersion.argtypes = [ctypes.c_char_p, ctypes.c_int] + self._dll.getVersion.restype = ctypes.c_int + + def get_string(self, dll_function: Callable, max_size=100) -> str: + """ + Call a DLL function that returns a string. + + Parameters + ---------- + dll_function: Callable + DLL function to call. It must be a function that returns a string. + max_size: int + Maximum number of string characters to return. This value is used for the string buffer size. + + Raises + ------- + If there is an error in the execution of the DLL function, an exception is raised. + + Returns + -------- + str + Return value of the called DLL function. + """ + text_buffer = ctypes.create_string_buffer(max_size) + status = dll_function(text_buffer, max_size) + self.raise_error(status) + text = text_buffer.value.decode("utf-8") + return text + + def set_string(self, dll_function: Callable, string: str): + """ + Call a DLL function that sets a string. + + Parameters + ---------- + dll_function: Callable + DLL function to call. It must be a function that sets a string. + string: str + String to set. + """ + bytes_value = bytes(string, "ascii") + status = dll_function(bytes_value) + self.raise_error(status) + + def string_to_enum(self, enum_type: Enum, string: str) -> Enum: + """ + Convert a string to a string defined by an enum. + + Parameters + ---------- + enum_type: Enum + Type of enum to convert. + string: str + String to convert. + + Returns + ------- + str + Enum value of the converted string. + """ + fixed_string = string.upper().replace(" ", "_") + return enum_type[fixed_string] + + def enum_to_string(self, enum_value: Enum) -> str: + """ + Convert an enum value to a string. + + Parameters + ---------- + enum_value: Enum + Enum value to convert to string. + + Returns + ------- + str + String converted from the enum string. + """ + fixed_string = str(enum_value.name).replace("_", " ").lower() + return fixed_string + + def api_version(self) -> str: + """ + Get the version of the API. + + Returns + ------- + str + API version. + """ + version = self.get_string(self._dll.getVersion) + return version + + def raise_error(self, error_status): + if error_status != 0: + error_message = self.get_string(self._dll.getErrorMessage, 4096) + raise RuntimeError(error_message) diff --git a/pyaedt/filtersolutions_core/graph_setup.py b/pyaedt/filtersolutions_core/graph_setup.py new file mode 100644 index 00000000000..7c0ffda0cc0 --- /dev/null +++ b/pyaedt/filtersolutions_core/graph_setup.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import c_char_p +from ctypes import c_int + +import pyaedt + + +class GraphSetup: + """Defines the frequency and time limits of the exported responses.""" + + def __init__(self): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_graph_dll_functions() + + def _define_graph_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.setPlotMinimumFrequency.argtype = c_char_p + self._dll.setPlotMinimumFrequency.restype = c_int + self._dll.getPlotMinimumFrequency.argtypes = [c_char_p, c_int] + self._dll.getPlotMinimumFrequency.restype = c_int + + self._dll.setPlotMaximumFrequency.argtype = c_char_p + self._dll.setPlotMaximumFrequency.restype = c_int + self._dll.getPlotMaximumFrequency.argtypes = [c_char_p, c_int] + self._dll.getPlotMaximumFrequency.restype = c_int + + self._dll.setPlotMinimumTime.argtype = c_char_p + self._dll.setPlotMinimumTime.restype = c_int + self._dll.getPlotMinimumTime.argtypes = [c_char_p, c_int] + self._dll.getPlotMinimumTime.restype = c_int + + self._dll.setPlotMaximumTime.argtype = c_char_p + self._dll.setPlotMaximumTime.restype = c_int + self._dll.getPlotMaximumTime.argtypes = [c_char_p, c_int] + self._dll.getPlotMaximumTime.restype = c_int + + @property + def minimum_frequency(self) -> str: + """Minimum frequency value for exporting frequency and S parameter responses. The default is ``200 MHz``. + + Returns + ------- + str + """ + min_freq_string = self._dll_interface.get_string(self._dll.getPlotMinimumFrequency) + return min_freq_string + + @minimum_frequency.setter + def minimum_frequency(self, min_freq_string): + self._dll_interface.set_string(self._dll.setPlotMinimumFrequency, min_freq_string) + + @property + def maximum_frequency(self) -> str: + """Maximum frequency value for exporting frequency and S parameters responses. The default is ``5 GHz``. + + Returns + ------- + str + """ + max_freq_string = self._dll_interface.get_string(self._dll.getPlotMaximumFrequency) + return max_freq_string + + @maximum_frequency.setter + def maximum_frequency(self, max_freq_string): + self._dll_interface.set_string(self._dll.setPlotMaximumFrequency, max_freq_string) + + @property + def minimum_time(self) -> str: + """Minimum time value for exporting time response. The default is ``0 s``. + + Returns + ------- + str + """ + min_time_string = self._dll_interface.get_string(self._dll.getPlotMinimumTime) + return min_time_string + + @minimum_time.setter + def minimum_time(self, min_time_string): + self._dll_interface.set_string(self._dll.setPlotMinimumTime, min_time_string) + + @property + def maximum_time(self) -> str: + """Maximum time value for exporting time response. The default is ``10 ns``. + + Returns + ------- + str + """ + max_time_string = self._dll_interface.get_string(self._dll.getPlotMaximumTime) + return max_time_string + + @maximum_time.setter + def maximum_time(self, max_time_string): + self._dll_interface.set_string(self._dll.setPlotMaximumTime, max_time_string) diff --git a/pyaedt/filtersolutions_core/ideal_response.py b/pyaedt/filtersolutions_core/ideal_response.py new file mode 100644 index 00000000000..81c2a9f5272 --- /dev/null +++ b/pyaedt/filtersolutions_core/ideal_response.py @@ -0,0 +1,461 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import POINTER +from ctypes import byref +from ctypes import c_bool +from ctypes import c_char_p +from ctypes import c_double +from ctypes import c_int +from enum import Enum +import math + +import pyaedt +from pyaedt.filtersolutions_core.graph_setup import GraphSetup + + +class FrequencyResponseColumn(Enum): + """Provides an enum of frequency response parameters. + + **Attributes:** + + - MAGNITUDE_DB: Represents the frequency response magnitude in dB. + - PHASE_DEG: Represents the frequency response phase in degree. + - GROUP_DELAY: Represents the frequency response group delay. + - PHASE_RAD: Represents the frequency response phase in radian. + - MAGNITUDE_ARITH: Represents the frequency response magnitude. + - MAGNITUDE_REAL: Represents the real part of frequency response magnitude. + - MAGNITUDE_IMAG: Represents the imaginary part of frequency response magnitude. + - PHASE_DEV_DEG: Represents the frequency response phase deviation in degrees. + - PHASE_DEV_RAD: Represents the frequency response phase deviation in radian. + - FREQUENCY: Represents frequency parameter of the frequency response . + """ + + MAGNITUDE_DB = 0 + PHASE_DEG = 1 + GROUP_DELAY = 2 + PHASE_RAD = 4 + MAGNITUDE_ARITH = 3 + MAGNITUDE_REAL = 5 + MAGNITUDE_IMAG = 6 + PHASE_DEV_DEG = 7 + PHASE_DEV_RAD = 8 + FREQUENCY = 9 + + +class TimeResponseColumn(Enum): + """Provides an enum of time response parameters. + + **Attributes:** + + - STEP_RESPONSE: Represents the step time response. + - RAMP_RESPONSE: Represents the ramp time response. + - IMPULSE_RESPONSE: Represents the impulse time response. + - STEP_RESPONSE_DB: Represents the step time response in dB. + - RAMP_RESPONSE_DB: Represents the ramp time response in dB. + - IMPULSE_RESPONSE_DB: Represents the impulse time response in dB. + - TIME: Represents time parameter of the time response . + """ + + STEP_RESPONSE = 0 + RAMP_RESPONSE = 1 + IMPULSE_RESPONSE = 2 + STEP_RESPONSE_DB = 3 + RAMP_RESPONSE_DB = 4 + IMPULSE_RESPONSE_DB = 5 + TIME = 6 + + +class SParametersResponseColumn(Enum): + """Provides an enum of S parameters. + + **Attributes:** + + - S21_DB: Represents the S21 parameter in dB. + - S11_DB: Represents the S11 parameter in dB. + - S21_ARITH: Represents the S21 parameter. + - S11_ARITH: Represents the S11 parameter. + - FREQUENCY: Represents the S parameters' frequency parameter. + """ + + S21_DB = 0 + S11_DB = 1 + S21_ARITH = 3 + S11_ARITH = 4 + FREQUENCY = 5 + + +class PoleZerosResponseColumn(Enum): + """Provides an enum of pole zero x and y coordinates of transmission (TX) or reflection (RX) zeros. + + **Attributes:** + + - TX_ZERO_DEN_X: Represents the x coordinate of the filter transmission zero denominator. + - TX_ZERO_DEN_Y: Represents the y coordinate of the filter transmission zero denominator. + - PROTO_TX_ZERO_DEN_X: Represents the x coordinate of the prototype filter transmission zero denominator. + - PROTO_TX_ZERO_DEN_Y: Represents the y coordinate of the prototype filter transmission zero denominator. + - TX_ZERO_NUM_X: Represents the x coordinate of the filter transmission zero numerator. + - TX_ZERO_NUM_Y: Represents the y coordinate of the filter transmission zero numerator. + - PROTO_TX_ZERO_NUM_X: Represents the x coordinate of the prototype filter transmission zero numerator. + - PROTO_TX_ZERO_NUM_Y: Represents the y coordinate of the prototype filter transmission zero numerator. + - RX_ZERO_DEN_X: Represents the x coordinate of the filter reflection zero denominator. + - RX_ZERO_DEN_Y: Represents the y coordinate of the filter reflection zero denominator. + - PROTO_RX_ZERO_DEN_X: Represents the x coordinate of the prototype filter reflection zero denominator. + - PROTO_RX_ZERO_DEN_Y: Represents the y coordinate of the prototype filter reflection zero denominator. + - RX_ZERO_NUM_X: Represents the x coordinate of the filter reflection zero numerator. + - RX_ZERO_NUM_Y: Represents the y coordinate of the filter reflection zero numerator. + - PROTO_RX_ZERO_NUM_X: Represents the x coordinate of the prototype filter reflection zero numerator. + - PROTO_RX_ZERO_NUM_Y: Represents the y coordinate of the prototype filter reflection zero numerator. + """ + + TX_ZERO_DEN_X = 0 + TX_ZERO_DEN_Y = 1 + PROTO_TX_ZERO_DEN_X = 2 + PROTO_TX_ZERO_DEN_Y = 3 + TX_ZERO_NUM_X = 4 + TX_ZERO_NUM_Y = 5 + PROTO_TX_ZERO_NUM_X = 6 + PROTO_TX_ZERO_NUM_Y = 7 + RX_ZERO_DEN_X = 8 + RX_ZERO_DEN_Y = 9 + PROTO_RX_ZERO_DEN_X = 10 + PROTO_RX_ZERO_DEN_Y = 11 + RX_ZERO_NUM_X = 12 + RX_ZERO_NUM_Y = 13 + PROTO_RX_ZERO_NUM_X = 14 + PROTO_RX_ZERO_NUM_Y = 15 + + +class IdealResponse: + """Returns the data for available ideal filter responses. + + Types of responses Include ``frequency``, ``time``, ``S parameters``, ``transfer function``, + and ``pole zero location``. + + This class allows you to construct all the necessary ideal response attributes for the ``FilterDesign`` class. + """ + + def __init__(self): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_response_dll_functions() + self.graph_setup = GraphSetup() + + def _define_response_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.getIdealFrequencyResponseSize.argtype = POINTER(c_int) + self._dll.getIdealFrequencyResponseSize.restype = c_int + self._dll.getIdealFrequencyResponse.argtypes = [POINTER(c_double), c_int, c_int] + self._dll.getIdealFrequencyResponse.restype = c_int + + self._dll.getIdealTimeResponseSize.argtype = POINTER(c_int) + self._dll.getIdealTimeResponseSize.restype = c_int + self._dll.getIdealTimeResponse.argtypes = [POINTER(c_double), c_int, c_int] + self._dll.getIdealTimeResponse.restype = c_int + + self._dll.getIdealSParamatersResponseSize.argtype = POINTER(c_int) + self._dll.getIdealSParamatersResponseSize.restype = c_int + self._dll.getIdealSParamatersResponse.argtypes = [ + POINTER(c_double), + c_int, + c_int, + ] + self._dll.getIdealSParamatersResponse.restype = c_int + + self._dll.getIdealPoleZerosResponseSize.argtypes = [POINTER(c_int), c_int] + self._dll.getIdealPoleZerosResponseSize.restype = c_int + self._dll.getIdealPoleZerosResponse.argtypes = [POINTER(c_double), c_int, c_int] + self._dll.getIdealPoleZerosResponse.restype = c_int + + self._dll.getIdealTransferFunctionResponseSize.argtype = POINTER(c_int) + self._dll.getIdealTransferFunctionResponseSize.restype = c_int + self._dll.getIdealTransferFunctionResponse.argtypes = [c_char_p, c_int] + self._dll.getIdealTransferFunctionResponse.restype = c_int + + self._dll.setVSGAnalsyis.argtype = c_bool + self._dll.setVSGAnalsyis.restype = c_int + self._dll.getVSGAnalsyis.argtype = POINTER(c_bool) + self._dll.getVSGAnalsyis.restype = c_int + + def _frequency_response_getter(self, column: FrequencyResponseColumn): + """Get the ideal filter frequency response. + + Parameters + ---------- + column: `FrequencyResponseColumn` + Frequency response column to get. + + Returns + ------- + list + List of values for the requested frequency response column (such as magnitude or phase). + """ + size = c_int() + status = self._dll.getIdealFrequencyResponseSize(byref(size)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + array = (c_double * size.value)() + status = self._dll.getIdealFrequencyResponse(array, size.value, column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + values = [float(val) for val in array] + return values + + def _time_response_getter(self, column: TimeResponseColumn): + """Get the ideal filter time response. + + Parameters + ---------- + column: `TimeResponseColumn` + Time response column to get. + + Returns + ------- + list + List of values for the requested time response column (such as step or pulse). + """ + size = c_int() + status = self._dll.getIdealTimeResponseSize(byref(size)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + array = (c_double * size.value)() + status = self._dll.getIdealTimeResponse(array, size.value, column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + values = [float(val) for val in array] + return values + + def _sparamaters_response_getter(self, column: SParametersResponseColumn): + """Get the ideal filter S parameter's response. + + Parameters + ---------- + column: `SParametersResponseColumn` + S parameter's response column to get. + + Returns + ------- + list + List of values for the requested S parameter's response column (such as S11 or S21). + """ + size = c_int() + status = self._dll.getIdealSParamatersResponseSize(byref(size)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + array = (c_double * size.value)() + status = self._dll.getIdealSParamatersResponse(array, size.value, column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + values = [float(val) for val in array] + return values + + def _pole_zeros_response_getter(self, column: PoleZerosResponseColumn): + """Get the ideal pole zero's location parameters (such as the x coordinate of + the denominator of transmission zeros). + + Parameters + ---------- + column: `PoleZerosResponseColumn` + Pole zero's response column to get. + + Returns + ------- + list + List of values for the requested pole zero's response column. + """ + size = c_int() + status = self._dll.getIdealPoleZerosResponseSize(byref(size), column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + array = (c_double * size.value)() + status = self._dll.getIdealPoleZerosResponse(array, size.value, column.value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + values = [float(val) for val in array] + return values + + def transfer_function_response(self): + """Get the ideal filter transfer function's parameters. + + Returns + ------- + str + Requested parameter array. + + str + Multi-line string where each line contains a coefficient from + the numerator and/or the denominator of the transfer function. + The coefficient for the highest-order term is first, and the terms are in decreasing order. + """ + size = c_int() + status = self._dll.getIdealTransferFunctionResponseSize(byref(size)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + transfer_function_response_string = self._dll_interface.get_string( + self._dll.getIdealTransferFunctionResponse, max_size=size.value + ) + return transfer_function_response_string + + @property + def vsg_analysis_enabled(self) -> bool: + """Flag indicating if the offset due to source resistor in frequency and time responses is enabled. + + Returns + ------- + bool + """ + + vsg_analysis_enabled = c_bool() + status = self._dll.getVSGAnalsyis(byref(vsg_analysis_enabled)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(vsg_analysis_enabled.value) + + @vsg_analysis_enabled.setter + def vsg_analysis_enabled(self, filter_vsg_analysis_enabled: bool): + status = self._dll.setVSGAnalsyis(filter_vsg_analysis_enabled) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def frequency_response( + self, + y_axis_parameter=FrequencyResponseColumn.MAGNITUDE_DB, + minimum_frequency="200 MHz", + maximum_frequency="5 GHz", + vsg_analysis_enabled=False, + ): + """Get the ideal filter frequency response for the given parameters. + + Parameters + ---------- + y_axis_parameter: `FrequencyResponseColumn`, optional + Frequency response column to return. The default is the frequency response magnitude in dB. + minimum_frequency: str, optional + The default is ``200 MHz``. + maximum_frequency: str, optional + The default is ``5 GHz``. + vsg_analysis_enabled: bool, optional + The default is ``False``. + + Returns + ------- + tuple + The tuple contains two lists of strings. The first is a list + of the defined frequency ranges, and the second is a + list of the requested parameters. + """ + self.graph_setup.minimum_frequency = minimum_frequency + self.graph_setup.maximum_frequency = maximum_frequency + self.vsg_analysis_enabled = vsg_analysis_enabled + frequency = self._frequency_response_getter(FrequencyResponseColumn.FREQUENCY) + frequency_hz = [] + for freq in frequency: + frequency_hz.append(freq / (2 * math.pi)) + parameter = self._frequency_response_getter(y_axis_parameter) + return frequency_hz, parameter + + def time_response( + self, + y_axis_parameter=TimeResponseColumn.STEP_RESPONSE, + minimum_time="0 s", + maximum_time="10 ns", + vsg_analysis_enabled=False, + ): + """Get the ideal filter time response for the given parameters. + + Parameters + ---------- + y_axis_parameter: `TimeResponseColumn`, optional + Time response column to get. The default is the step time response. + minimum_time: str, optional + The default is ``0 s``. + maximum_time: str, optional + The default is ``10 ns``. + vsg_analysis_enabled: bool, optional + The default is ``False``. + + Returns + ------- + tuple + The tuple contains two lists of strings. The first is a list + of the defined time ranges, and the second is a + list of the requested parameters. + """ + self.graph_setup.minimum_time = minimum_time + self.graph_setup.maximum_time = maximum_time + self.vsg_analysis_enabled = vsg_analysis_enabled + time = self._time_response_getter(TimeResponseColumn.TIME) + parameter = self._time_response_getter(y_axis_parameter) + return time, parameter + + def s_parameters( + self, + y_axis_parameter=SParametersResponseColumn.S21_DB, + minimum_frequency="200 MHz", + maximum_frequency="5 GHz", + ): + """Get the ideal filter S parameters response for the given parameters. + + Parameters + ---------- + y_axis_parameter: `SParametersResponseColumn`, optional + S parameter's response column to get. The default is the S21 parameter response in dB. + minimum_frequency: str, optional + The default is ``200 MHz``. + maximum_frequency: str, optional + The default is ``5 GHz``. + vsg_analysis_enabled: bool, optional + The default is ``False``. + + Returns + ------- + tuple + The tuple contains two lists of strings. The first is a list + of the defined frequency ranges, and the second is a + list of the requested parameters. + """ + self.graph_setup.minimum_frequency = minimum_frequency + self.graph_setup.maximum_frequency = maximum_frequency + frequency = self._sparamaters_response_getter(SParametersResponseColumn.FREQUENCY) + frequency_hz = [] + for freq in frequency: + frequency_hz.append(freq / (2 * math.pi)) + parameter = self._sparamaters_response_getter(y_axis_parameter) + return frequency_hz, parameter + + def pole_zero_locations( + self, + x_axis_parameter=PoleZerosResponseColumn.TX_ZERO_DEN_X, + y_axis_parameter=PoleZerosResponseColumn.TX_ZERO_DEN_Y, + ): + """Get the ideal pole zero location for the given parameters. + + Parameters + ---------- + x_axis_parameter: `PoleZerosResponseColumn`, optional + X axis parameter of the pole zeros response column to get. The default is the x coordinate + of the filter transmission zero denominator. + y_axis_parameter: `PoleZerosResponseColumn`, optional + Y axis parameter of the pole zeros response column to get. The default is the y coordinate + of the filter transmission zero denominator. + + Returns + ------- + tuple + The tuple contains two lists of strings. The first is a list + of the x coordinates of the requested parameter, and the second is a + list of the y coordinates of the requested parameter. + """ + x_parameter = self._pole_zeros_response_getter(x_axis_parameter) + y_parameter = self._pole_zeros_response_getter(y_axis_parameter) + return x_parameter, y_parameter diff --git a/pyaedt/filtersolutions_core/lumped_nodes_and_leads.py b/pyaedt/filtersolutions_core/lumped_nodes_and_leads.py new file mode 100644 index 00000000000..9a04d2ba764 --- /dev/null +++ b/pyaedt/filtersolutions_core/lumped_nodes_and_leads.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import POINTER +from ctypes import byref +from ctypes import c_bool +from ctypes import c_char_p +from ctypes import c_int + +import pyaedt + + +class LumpedNodesandLeads: + """Defines attributes of the lumped element node capacitors and lead inductors. + + This class lets you to construct all the necessary node capacitors and + lead inductors attributes of the lumped elements for the ``LumpedDesign`` class. + """ + + def __init__(self): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_nodes_and_leads_dll_functions() + + def _define_nodes_and_leads_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.setLumpedCNodeCapacitor.argtype = c_char_p + self._dll.setLumpedCNodeCapacitor.restype = c_int + self._dll.getLumpedCNodeCapacitor.argtypes = [c_char_p, c_int] + self._dll.getLumpedCNodeCapacitor.restype = c_int + + self._dll.setLumpedCLeadInductor.argtype = c_char_p + self._dll.setLumpedCLeadInductor.restype = c_int + self._dll.getLumpedCLeadInductor.argtypes = [c_char_p, c_int] + self._dll.getLumpedCLeadInductor.restype = c_int + + self._dll.setLumpedLNodeCapacitor.argtype = c_char_p + self._dll.setLumpedLNodeCapacitor.restype = c_int + self._dll.getLumpedLNodeCapacitor.argtypes = [c_char_p, c_int] + self._dll.getLumpedLNodeCapacitor.restype = c_int + + self._dll.setLumpedLLeadInductor.argtype = c_char_p + self._dll.setLumpedLLeadInductor.restype = c_int + self._dll.getLumpedLLeadInductor.argtypes = [c_char_p, c_int] + self._dll.getLumpedLLeadInductor.restype = c_int + + self._dll.setLumpedRNodeCapacitor.argtype = c_char_p + self._dll.setLumpedRNodeCapacitor.restype = c_int + self._dll.getLumpedRNodeCapacitor.argtypes = [c_char_p, c_int] + self._dll.getLumpedRNodeCapacitor.restype = c_int + + self._dll.setLumpedRLeadInductor.argtype = c_char_p + self._dll.setLumpedRLeadInductor.restype = c_int + self._dll.getLumpedRLeadInductor.argtypes = [c_char_p, c_int] + self._dll.getLumpedRLeadInductor.restype = c_int + + self._dll.setLumpedCNodeLedComensate.argtype = c_bool + self._dll.setLumpedCNodeLedComensate.restype = c_int + self._dll.getLumpedCNodeLedComensate.argtype = POINTER(c_bool) + self._dll.getLumpedCNodeLedComensate.restype = c_int + + self._dll.setLumpedLNodeLedComensate.argtype = c_bool + self._dll.setLumpedLNodeLedComensate.restype = c_int + self._dll.getLumpedLNodeLedComensate.argtype = POINTER(c_bool) + self._dll.getLumpedLNodeLedComensate.restype = c_int + + @property + def c_node_capacitor(self) -> str: + """Shunt capacitance assigned to each capacitor node. + The default is ``0``. + + Returns + ------- + str + """ + c_node_capacitor = self._dll_interface.get_string(self._dll.getLumpedCNodeCapacitor) + return c_node_capacitor + + @c_node_capacitor.setter + def c_node_capacitor(self, c_node_capacitor): + self._dll_interface.set_string(self._dll.setLumpedCNodeCapacitor, c_node_capacitor) + + @property + def c_lead_inductor(self) -> str: + """Series inductance assigned to each capacitor lead. + The default is ``0``. + + Returns + ------- + str + """ + c_lead_inductor = self._dll_interface.get_string(self._dll.getLumpedCLeadInductor) + return c_lead_inductor + + @c_lead_inductor.setter + def c_lead_inductor(self, c_lead_inductor): + self._dll_interface.set_string(self._dll.setLumpedCLeadInductor, c_lead_inductor) + + @property + def l_node_capacitor(self) -> str: + """Shunt capacitance assigned to each inductor node. + The default is` ``0``. + + Returns + ------- + str + """ + l_node_capacitor = self._dll_interface.get_string(self._dll.getLumpedLNodeCapacitor) + return l_node_capacitor + + @l_node_capacitor.setter + def l_node_capacitor(self, l_node_capacitor): + self._dll_interface.set_string(self._dll.setLumpedLNodeCapacitor, l_node_capacitor) + + @property + def l_lead_inductor(self) -> str: + """Series inductance assigned to each inductor lead. + The default is ``0``. + + Returns + ------- + str + """ + l_lead_inductor = self._dll_interface.get_string(self._dll.getLumpedLLeadInductor) + return l_lead_inductor + + @l_lead_inductor.setter + def l_lead_inductor(self, l_lead_inductor): + self._dll_interface.set_string(self._dll.setLumpedLLeadInductor, l_lead_inductor) + + @property + def r_node_capacitor(self) -> str: + """Shunt capacitance assigned to each resistor node. + The default is ``0``. + + Returns + ------- + str + """ + r_node_capacitor = self._dll_interface.get_string(self._dll.getLumpedRNodeCapacitor) + return r_node_capacitor + + @r_node_capacitor.setter + def r_node_capacitor(self, r_node_capacitor): + self._dll_interface.set_string(self._dll.setLumpedRNodeCapacitor, r_node_capacitor) + + @property + def r_lead_inductor(self) -> str: + """Series inductance assigned to each resistor lead. + The default is ``0``. + + Returns + ------- + str + """ + r_lead_inductor = self._dll_interface.get_string(self._dll.getLumpedRLeadInductor) + return r_lead_inductor + + @r_lead_inductor.setter + def r_lead_inductor(self, r_lead_inductor): + self._dll_interface.set_string(self._dll.setLumpedRLeadInductor, r_lead_inductor) + + @property + def c_node_compensate(self) -> bool: + """Flag indicating if the adjusting capacitor values to compensate for node capacitance is enabled. + + Returns + ------- + bool + """ + c_node_compensate = c_bool() + status = self._dll.getLumpedCNodeLedComensate(byref(c_node_compensate)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(c_node_compensate.value) + + @c_node_compensate.setter + def c_node_compensate(self, c_node_compensate: bool): + status = self._dll.setLumpedCNodeLedComensate(c_node_compensate) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def l_node_compensate(self) -> bool: + """Flag indicating if the adjusting inductor values to compensate for lead inductance is enabled. + + Returns + ------- + bool + """ + l_node_compensate = c_bool() + status = self._dll.getLumpedLNodeLedComensate(byref(l_node_compensate)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(l_node_compensate.value) + + @l_node_compensate.setter + def l_node_compensate(self, l_node_compensate: bool): + status = self._dll.setLumpedLNodeLedComensate(l_node_compensate) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) diff --git a/pyaedt/filtersolutions_core/lumped_parasitics.py b/pyaedt/filtersolutions_core/lumped_parasitics.py new file mode 100644 index 00000000000..b876ea59b15 --- /dev/null +++ b/pyaedt/filtersolutions_core/lumped_parasitics.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import c_char_p +from ctypes import c_int + +import pyaedt + + +class LumpedParasitics: + """Defines attributes of the lumped element parasitics. + + This class lets you to construct all the necessary parasitic + attributes of lumped elements for the ``LumpedDesign`` class. + """ + + def __init__(self): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_parasitics_dll_functions() + + def _define_parasitics_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.setLumpedCapacitorQ.argtype = c_char_p + self._dll.setLumpedCapacitorQ.restype = c_int + self._dll.getLumpedCapacitorQ.argtypes = [c_char_p, c_int] + self._dll.getLumpedCapacitorQ.restype = c_int + + self._dll.setLumpedCapacitorRs.argtype = c_char_p + self._dll.setLumpedCapacitorRs.restype = c_int + self._dll.getLumpedCapacitorRs.argtypes = [c_char_p, c_int] + self._dll.getLumpedCapacitorRs.restype = c_int + + self._dll.setLumpedCapacitorRp.argtype = c_char_p + self._dll.setLumpedCapacitorRp.restype = c_int + self._dll.getLumpedCapacitorRp.argtypes = [c_char_p, c_int] + self._dll.getLumpedCapacitorRp.restype = c_int + + self._dll.setLumpedCapacitorLs.argtype = c_char_p + self._dll.setLumpedCapacitorLs.restype = c_int + self._dll.getLumpedCapacitorLs.argtypes = [c_char_p, c_int] + self._dll.getLumpedCapacitorLs.restype = c_int + + self._dll.setLumpedInductorQ.argtype = c_char_p + self._dll.setLumpedInductorQ.restype = c_int + self._dll.getLumpedInductorQ.argtypes = [c_char_p, c_int] + self._dll.getLumpedInductorQ.restype = c_int + + self._dll.setLumpedInductorRs.argtype = c_char_p + self._dll.setLumpedInductorRs.restype = c_int + self._dll.getLumpedInductorRs.argtypes = [c_char_p, c_int] + self._dll.getLumpedInductorRs.restype = c_int + + self._dll.setLumpedInductorRp.argtype = c_char_p + self._dll.setLumpedInductorRp.restype = c_int + self._dll.getLumpedInductorRp.argtypes = [c_char_p, c_int] + self._dll.getLumpedInductorRp.restype = c_int + + self._dll.setLumpedInductorCp.argtype = c_char_p + self._dll.setLumpedInductorCp.restype = c_int + self._dll.getLumpedInductorCp.argtypes = [c_char_p, c_int] + self._dll.getLumpedInductorCp.restype = c_int + + @property + def capacitor_q(self) -> str: + """Q factor value of non-ideal capacitors in the synthesized circuit. + The default is ``infinite``. + + Returns + ------- + str + """ + capacitor_q_string = self._dll_interface.get_string(self._dll.getLumpedCapacitorQ) + return capacitor_q_string + + @capacitor_q.setter + def capacitor_q(self, capacitor_q_string): + self._dll_interface.set_string(self._dll.setLumpedCapacitorQ, capacitor_q_string) + + @property + def capacitor_rs(self) -> str: + """Series resistor value of non-ideal capacitors in the synthesized circuit. + The default is ``0``. + + Returns + ------- + str + """ + capacitor_rs_string = self._dll_interface.get_string(self._dll.getLumpedCapacitorRs) + return capacitor_rs_string + + @capacitor_rs.setter + def capacitor_rs(self, capacitor_rs_string): + self._dll_interface.set_string(self._dll.setLumpedCapacitorRs, capacitor_rs_string) + + @property + def capacitor_rp(self) -> str: + """Shunt resistor value of non-ideal capacitors in the synthesized circuit. + The default is ``infinite``. + + Returns + ------- + str + """ + capacitor_rp_string = self._dll_interface.get_string(self._dll.getLumpedCapacitorRp) + return capacitor_rp_string + + @capacitor_rp.setter + def capacitor_rp(self, capacitor_rp_string): + self._dll_interface.set_string(self._dll.setLumpedCapacitorRp, capacitor_rp_string) + + @property + def capacitor_ls(self) -> str: + """Series inductance value of non-ideal capacitors in the synthesized circuit. + The default is ``0``. + + Returns + ------- + str + """ + capacitor_ls_string = self._dll_interface.get_string(self._dll.getLumpedCapacitorLs) + return capacitor_ls_string + + @capacitor_ls.setter + def capacitor_ls(self, capacitor_ls_string): + self._dll_interface.set_string(self._dll.setLumpedCapacitorLs, capacitor_ls_string) + + @property + def inductor_q(self) -> str: + """Q factor value of non-ideal inductors in the synthesized circuit. + The default is ``infinite``. + + Returns + ------- + str + """ + inductor_q_string = self._dll_interface.get_string(self._dll.getLumpedInductorQ) + return inductor_q_string + + @inductor_q.setter + def inductor_q(self, inductor_q_string): + self._dll_interface.set_string(self._dll.setLumpedInductorQ, inductor_q_string) + + @property + def inductor_rs(self) -> str: + """Series resistor value of non-ideal inductors in the synthesized circuit. + The default is` ``0``. + + Returns + ------- + str + """ + inductor_rs_string = self._dll_interface.get_string(self._dll.getLumpedInductorRs) + return inductor_rs_string + + @inductor_rs.setter + def inductor_rs(self, inductor_rs_string): + self._dll_interface.set_string(self._dll.setLumpedInductorRs, inductor_rs_string) + + @property + def inductor_rp(self) -> str: + """Shunt resistor value of non-ideal inductors in the synthesized circuit. + The default is ``infinite``. + + Returns + ------- + str + """ + inductor_rp_string = self._dll_interface.get_string(self._dll.getLumpedInductorRp) + return inductor_rp_string + + @inductor_rp.setter + def inductor_rp(self, inductor_rp_string): + self._dll_interface.set_string(self._dll.setLumpedInductorRp, inductor_rp_string) + + @property + def inductor_cp(self) -> str: + """Shunt capacitor value of non-ideal inductors in the synthesized circuit. + The default is ``0``. + + Returns + ------- + str + """ + inductor_cp_string = self._dll_interface.get_string(self._dll.getLumpedInductorCp) + return inductor_cp_string + + @inductor_cp.setter + def inductor_cp(self, inductor_cp_string): + self._dll_interface.set_string(self._dll.setLumpedInductorCp, inductor_cp_string) diff --git a/pyaedt/filtersolutions_core/lumped_termination_impedance.py b/pyaedt/filtersolutions_core/lumped_termination_impedance.py new file mode 100644 index 00000000000..083c214511c --- /dev/null +++ b/pyaedt/filtersolutions_core/lumped_termination_impedance.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import POINTER +from ctypes import byref +from ctypes import c_bool +from ctypes import c_char_p +from ctypes import c_int +from ctypes import create_string_buffer +from enum import Enum + +import pyaedt + + +class ComplexTerminationDefinition(Enum): + """Selects type of complex presentation. + + **Attributes:** + + - POLAR: Represents polar definition. + - CARTESIAN: Represents Cartesian definition. + - PARALLEL: Represents parallel definition with real entry parallel to imaginary entry. + - REAL: Represents only real impedance definition. + """ + + POLAR = 0 + CARTESIAN = 1 + PARALLEL = 2 + REAL = 3 + + +class ComplexReactanceType(Enum): + """Selects type of complex impedance as reactance, equivalent inductance, or equivalent capacitance. + + **Attributes:** + + - REAC: Represents pure reactance of complex impedance. + - IND: Represents equivalent inductance in henries. + - CAP: Represents equivalent capacitance in farads. + """ + + REAC = 0 + IND = 1 + CAP = 2 + + +class TerminationType(Enum): + """Selects either source or load complex impedance table. + + **Attributes:** + + - SOURCE: Represents source impedenace table. + - LOAD: Represents load impedenace table. + """ + + SOURCE = 0 + LOAD = 1 + + +class LumpedTerminationImpedance: + """Manipulates access to the entries of source and load complex impedance table. + + This class allows you to enter, edit, or remove the entries of source and load complex impedance table. + """ + + def __init__(self, table_type): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_termination_impedance_dll_functions() + self.table_type = table_type + + def _define_termination_impedance_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.getComplexTableRowCount.argtypes = [POINTER(c_int), c_bool] + self._dll.getComplexTableRowCount.restype = c_int + + self._dll.getComplexTableRow.argtypes = [ + c_int, + c_char_p, + c_char_p, + c_char_p, + c_bool, + c_int, + ] + self._dll.getComplexTableRow.restype = c_int + + self._dll.updateComplexTableRow.argtypes = [ + c_int, + c_char_p, + c_char_p, + c_char_p, + c_bool, + ] + self._dll.updateComplexTableRow.restype = c_int + + self._dll.appendComplexTableRow.argtypes = [ + c_char_p, + c_char_p, + c_char_p, + c_bool, + ] + self._dll.appendComplexTableRow.restype = c_int + + self._dll.insertComplexTableRow.argtypes = [ + c_int, + c_char_p, + c_char_p, + c_char_p, + c_bool, + ] + self._dll.insertComplexTableRow.restype = c_int + + self._dll.removeComplexTableRow.argtypes = [c_int, c_bool] + self._dll.removeComplexTableRow.restype = c_int + + self._dll.setLumpedComplexDefinition.argtypes = [c_char_p, c_bool] + self._dll.setLumpedComplexDefinition.restype = c_int + self._dll.getLumpedComplexDefinition.argtypes = [c_char_p, c_bool, c_int] + self._dll.getLumpedComplexDefinition.restype = c_int + + self._dll.setLumpedComplexReactanceType.argtypes = [c_char_p, c_bool] + self._dll.setLumpedComplexReactanceType.restype = c_int + self._dll.getLumpedComplexReactanceType.argtypes = [c_char_p, c_bool, c_int] + self._dll.getLumpedComplexReactanceType.restype = c_int + + self._dll.setLumpedComplexImpCompensateEnabled.argtypes = [c_bool, c_bool] + self._dll.setLumpedComplexImpCompensateEnabled.restype = c_int + self._dll.getLumpedComplexImpCompensateEnabled.argtypes = [ + POINTER(c_bool), + c_bool, + ] + self._dll.getLumpedComplexImpCompensateEnabled.restype = c_int + + self._dll.setLumpedComplexCompOrder.argtypes = [c_int, c_bool] + self._dll.setLumpedComplexCompOrder.restype = c_int + self._dll.getLumpedComplexCompOrder.argtypes = [POINTER(c_int), c_bool] + self._dll.getLumpedComplexCompOrder.restype = c_int + + def table_type_to_bool(self): + """Set a flag to recognize source and load complex table. + + Returns + ------- + bool + """ + if self.table_type.value == TerminationType.SOURCE.value: + return False + else: + return True + + @property + def row_count(self) -> int: + """Count of the accumulated complex impedances in the complex impedances's table. + The default is ``3``. + + Returns + ------- + int + """ + table_row_count = c_int() + status = self._dll.getComplexTableRowCount(byref(table_row_count), self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(table_row_count.value) + + def row(self, row_index): + """Get frequency and complex impedance values from a row in the complex impedance table. + + Parameters + ---------- + row_index: int + Row index on complex impedance table, starting at ``0`` and with a maximum value of ``99``. + + Returns + ------- + tuple + The tuple contains three strings. The first is the frequency value, + the second is the real part of the complex impedance, + and the third is the imaginary part of the complex impedance. + """ + frequency_value_buffer = create_string_buffer(100) + real_value_buffer = create_string_buffer(100) + imag_value_buffer = create_string_buffer(100) + status = self._dll.getComplexTableRow( + row_index, + frequency_value_buffer, + real_value_buffer, + imag_value_buffer, + self.table_type_to_bool(), + 100, + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + frequency_value_string = frequency_value_buffer.value.decode("utf-8") + real_value_string = real_value_buffer.value.decode("utf-8") + imag_value_string = imag_value_buffer.value.decode("utf-8") + return frequency_value_string, real_value_string, imag_value_string + + def update_row(self, row_index, frequency="", real="", imag=""): + """Update frequency and complex impedance at a specified index in the complex impedance table. + + Parameters + ---------- + row_index: int + Row index on complex impedance table, starting at ``0`` and with a maximum value of ``99``. + frequency: str, optional + The frequency value to update. If not specified, it remains unchanged. + real: str, optional + The real part of the complex impedance to update. If not specified, it remains unchanged. + imag: str, optional + The imaginary part of the complex impedance to update. If not specified, it remains unchanged. + """ + frequency_bytes_value = bytes(frequency, "ascii") + real_bytes_value = bytes(real, "ascii") + imag_bytes_value = bytes(imag, "ascii") + status = self._dll.updateComplexTableRow( + row_index, + frequency_bytes_value, + real_bytes_value, + imag_bytes_value, + self.table_type_to_bool(), + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def append_row(self, frequency, real, imag): + """Append frequency and complex impedance values to the last row of + both the source and load complex impedance table. + + + Parameters + ---------- + frequency: str + The frequency value to append. + real: str + The real part of the complex impedance to append. + imag: str + The imaginary part of the complex impedance to append. + """ + frequency_bytes_value = bytes(frequency, "ascii") + real_bytes_value = bytes(real, "ascii") + imag_bytes_value = bytes(imag, "ascii") + status = self._dll.appendComplexTableRow( + frequency_bytes_value, + real_bytes_value, + imag_bytes_value, + self.table_type_to_bool(), + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def insert_row(self, row_index, frequency, real, imag): + """Insert frequency and complex impedance values at a specified index in the complex impedance table. + + Parameters + ---------- + row_index : int + Row index in the complex impedance table, starting at ``0`` and with a maximum value of ``99``. + frequency : str + The frequency value to insert. + real : str + The real part of the complex impedance to insert. + imag : str + The imaginary part of the complex impedance to insert. + """ + frequency_bytes_value = bytes(frequency, "ascii") + real_bytes_value = bytes(real, "ascii") + imag_bytes_value = bytes(imag, "ascii") + status = self._dll.insertComplexTableRow( + row_index, + frequency_bytes_value, + real_bytes_value, + imag_bytes_value, + self.table_type_to_bool(), + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def remove_row(self, row_index): + """Remove frequency and complex impedance at a specified index from the complex impedance table. + + Parameters + ---------- + row_index : int + Row index in the complex impedance table, starting at ``0`` and with a maximum value of ``99``. + """ + status = self._dll.removeComplexTableRow(row_index, self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def complex_definition(self) -> ComplexTerminationDefinition: + """Definition type of complex impedance in the complex impedance table. + The default is ``Cartesian``. + + Returns + ------- + :enum:`ComplexTerminationDefinition` + """ + type_string_buffer = create_string_buffer(100) + status = self._dll.getLumpedComplexDefinition( + type_string_buffer, + self.table_type_to_bool(), + 100, + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + type_string = type_string_buffer.value.decode("utf-8") + return self._dll_interface.string_to_enum(ComplexTerminationDefinition, type_string) + + @complex_definition.setter + def complex_definition(self, complex_definition: ComplexTerminationDefinition): + string_value = self._dll_interface.enum_to_string(complex_definition) + string_bytes_value = bytes(string_value, "ascii") + status = self._dll.setLumpedComplexDefinition(string_bytes_value, self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def reactance_type(self) -> ComplexReactanceType: + """Reactance type of complex impedance in the complex impedance table. + The default is ``reactance``. + + Returns + ------- + :enum:`ComplexReactanceType` + """ + + type_string_buffer = create_string_buffer(100) + status = self._dll.getLumpedComplexReactanceType( + type_string_buffer, + self.table_type_to_bool(), + 100, + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + type_string = type_string_buffer.value.decode("utf-8") + return self._dll_interface.string_to_enum(ComplexReactanceType, type_string) + + @reactance_type.setter + def reactance_type(self, reactance_type: ComplexReactanceType): + string_value = self._dll_interface.enum_to_string(reactance_type) + string_bytes_value = bytes(string_value, "ascii") + status = self._dll.setLumpedComplexReactanceType(string_bytes_value, self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def compensation_enabled(self) -> bool: + """Flag indicating if the impedance compensation is enabled. + + Returns + ------- + bool + """ + compensation_enabled = c_bool() + status = self._dll.getLumpedComplexImpCompensateEnabled(byref(compensation_enabled), self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(compensation_enabled.value) + + @compensation_enabled.setter + def compensation_enabled(self, compensation_enabled: bool): + status = self._dll.setLumpedComplexImpCompensateEnabled(compensation_enabled, self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def compensation_order(self) -> int: + """Order of impedance compensation. + The default is` ``2``. + + Returns + ------- + int + """ + compensation_order = c_int() + status = self._dll.getLumpedComplexCompOrder(byref(compensation_order), self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(compensation_order.value) + + @compensation_order.setter + def compensation_order(self, compensation_order: int): + status = self._dll.setLumpedComplexCompOrder(compensation_order, self.table_type_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) diff --git a/pyaedt/filtersolutions_core/lumped_topology.py b/pyaedt/filtersolutions_core/lumped_topology.py new file mode 100644 index 00000000000..cc5c3956e8f --- /dev/null +++ b/pyaedt/filtersolutions_core/lumped_topology.py @@ -0,0 +1,539 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import POINTER +from ctypes import byref +from ctypes import c_bool +from ctypes import c_char_p +from ctypes import c_int + +import pyaedt + + +class LumpedTopology: + """Defines attributes and parameters of lumped filters. + + This class lets you construct all the necessary attributes for the ``LumpedDesign`` class. + """ + + def __init__(self): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_topology_dll_functions() + + def _define_topology_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.setLumpedGeneratorResistor.argtype = c_char_p + self._dll.setLumpedGeneratorResistor.restype = c_int + self._dll.getLumpedGeneratorResistor.argtypes = [c_char_p, c_int] + self._dll.getLumpedGeneratorResistor.restype = c_int + + self._dll.setLumpedLoadResistor.argtype = c_char_p + self._dll.setLumpedLoadResistor.restype = c_int + self._dll.getLumpedLoadResistor.argtypes = [c_char_p, c_int] + self._dll.getLumpedLoadResistor.restype = c_int + + self._dll.setLumpedCurrentSource.argtype = c_bool + self._dll.setLumpedCurrentSource.restype = c_int + self._dll.getLumpedCurrentSource.argtype = POINTER(c_bool) + self._dll.getLumpedCurrentSource.restype = c_int + + self._dll.setLumpedFirstElementShunt.argtype = c_bool + self._dll.setLumpedFirstElementShunt.restype = c_int + self._dll.getLumpedFirstElementShunt.argtype = POINTER(c_bool) + self._dll.getLumpedFirstElementShunt.restype = c_int + + self._dll.setLumpedBridgeT.argtype = c_bool + self._dll.setLumpedBridgeT.restype = c_int + self._dll.getLumpedBridgeT.argtype = POINTER(c_bool) + self._dll.getLumpedBridgeT.restype = c_int + + self._dll.setLumpedBridgeTLow.argtype = c_bool + self._dll.setLumpedBridgeTLow.restype = c_int + self._dll.getLumpedBridgeTLow.argtype = POINTER(c_bool) + self._dll.getLumpedBridgeTLow.restype = c_int + + self._dll.setLumpedBridgeTHigh.argtype = c_bool + self._dll.setLumpedBridgeTHigh.restype = c_int + self._dll.getLumpedBridgeTHigh.argtype = POINTER(c_bool) + self._dll.getLumpedBridgeTHigh.restype = c_int + + self._dll.setLumpedEqualInductors.argtype = c_bool + self._dll.setLumpedEqualInductors.restype = c_int + self._dll.getLumpedEqualInductors.argtype = POINTER(c_bool) + self._dll.getLumpedEqualInductors.restype = c_int + + self._dll.setLumpedEqualCapacitors.argtype = c_bool + self._dll.setLumpedEqualCapacitors.restype = c_int + self._dll.getLumpedEqualCapacitors.argtype = POINTER(c_bool) + self._dll.getLumpedEqualCapacitors.restype = c_int + + self._dll.setLumpedEqualLegs.argtype = c_bool + self._dll.setLumpedEqualLegs.restype = c_int + self._dll.getLumpedEqualLegs.argtype = POINTER(c_bool) + self._dll.getLumpedEqualLegs.restype = c_int + + self._dll.setLumpedHighLowPass.argtype = c_bool + self._dll.setLumpedHighLowPass.restype = c_int + self._dll.getLumpedHighLowPass.argtype = POINTER(c_bool) + self._dll.getLumpedHighLowPass.restype = c_int + + self._dll.setLumpedHighLowPassMinInd.argtype = c_bool + self._dll.setLumpedHighLowPassMinInd.restype = c_int + self._dll.getLumpedHighLowPassMinInd.argtype = POINTER(c_bool) + self._dll.getLumpedHighLowPassMinInd.restype = c_int + + self._dll.setLumpedZigZag.argtype = c_bool + self._dll.setLumpedZigZag.restype = c_int + self._dll.getLumpedZigZag.argtype = POINTER(c_bool) + self._dll.getLumpedZigZag.restype = c_int + + self._dll.setLumpedMinInd.argtype = c_bool + self._dll.setLumpedMinInd.restype = c_int + self._dll.getLumpedMinInd.argtype = POINTER(c_bool) + self._dll.getLumpedMinInd.restype = c_int + + self._dll.setLumpedMinCap.argtype = c_bool + self._dll.setLumpedMinCap.restype = c_int + self._dll.getLumpedMinCap.argtype = POINTER(c_bool) + self._dll.getLumpedMinCap.restype = c_int + + self._dll.setLumpedSourceRes.argtype = c_bool + self._dll.setLumpedSourceRes.restype = c_int + self._dll.getLumpedSourceRes.argtype = POINTER(c_bool) + self._dll.getLumpedSourceRes.restype = c_int + + self._dll.setLumpedTrapTopology.argtype = c_bool + self._dll.setLumpedTrapTopology.restype = c_int + self._dll.getLumpedTrapTopology.argtype = POINTER(c_bool) + self._dll.getLumpedTrapTopology.restype = c_int + + self._dll.setLumpedNodeCapGround.argtype = c_bool + self._dll.setLumpedNodeCapGround.restype = c_int + self._dll.getLumpedNodeCapGround.argtype = POINTER(c_bool) + self._dll.getLumpedNodeCapGround.restype = c_int + + self._dll.setLumpedMatchImpedance.argtype = c_bool + self._dll.setLumpedMatchImpedance.restype = c_int + self._dll.getLumpedMatchImpedance.argtype = POINTER(c_bool) + self._dll.getLumpedMatchImpedance.restype = c_int + + self._dll.setLumpedComplexTermination.argtype = c_bool + self._dll.setLumpedComplexTermination.restype = c_int + self._dll.getLumpedComplexTermination.argtype = POINTER(c_bool) + self._dll.getLumpedComplexTermination.restype = c_int + + self._dll.setLumpedComplexElementTuneEnabled.argtype = c_bool + self._dll.setLumpedComplexElementTuneEnabled.restype = c_int + self._dll.getLumpedComplexElementTuneEnabled.argtype = POINTER(c_bool) + self._dll.getLumpedComplexElementTuneEnabled.restype = c_int + + self._dll.getLumpedCircuitResponseSize.argtype = POINTER(c_int) + self._dll.getLumpedCircuitResponseSize.restype = c_int + self._dll.getLumpedCircuitResponse.argtypes = [c_char_p, c_int] + self._dll.getLumpedCircuitResponse.restype = c_int + + @property + def generator_resistor(self) -> str: + """Generator resistor. The default is ``50``. + + Returns + ------- + str + """ + generator_resistor_string = self._dll_interface.get_string(self._dll.getLumpedGeneratorResistor) + return generator_resistor_string + + @generator_resistor.setter + def generator_resistor(self, generator_resistor_string): + self._dll_interface.set_string(self._dll.setLumpedGeneratorResistor, generator_resistor_string) + + @property + def load_resistor(self) -> str: + """Load resistor. The default is ``50``. + + Returns + ------- + str + """ + load_resistor_string = self._dll_interface.get_string(self._dll.getLumpedLoadResistor) + return load_resistor_string + + @load_resistor.setter + def load_resistor(self, load_resistor_string): + self._dll_interface.set_string(self._dll.setLumpedLoadResistor, load_resistor_string) + + @property + def current_source(self) -> bool: + """Flag indicating if the current source in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + current_source = c_bool() + status = self._dll.getLumpedCurrentSource(byref(current_source)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(current_source.value) + + @current_source.setter + def current_source(self, current_source: bool): + status = self._dll.setLumpedCurrentSource(current_source) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def first_shunt(self) -> bool: + """Flag indicating if shunt elements are first in the synthesized circuit. + If ``False``, series elements are first. + + Returns + ------- + bool + """ + first_shunt = c_bool() + status = self._dll.getLumpedFirstElementShunt(byref(first_shunt)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(first_shunt.value) + + @first_shunt.setter + def first_shunt(self, first_shunt: bool): + status = self._dll.setLumpedFirstElementShunt(first_shunt) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def bridge_t(self) -> bool: + """Flag indicating if the bridgeT topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + bridge_t = c_bool() + status = self._dll.getLumpedBridgeT(byref(bridge_t)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(bridge_t.value) + + @bridge_t.setter + def bridge_t(self, bridge_t: bool): + status = self._dll.setLumpedBridgeT(bridge_t) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def bridge_t_low(self) -> bool: + """Flag indicating if the bridgeT topology for the lower frequency band in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + bridge_t_low = c_bool() + status = self._dll.getLumpedBridgeTLow(byref(bridge_t_low)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(bridge_t_low.value) + + @bridge_t_low.setter + def bridge_t_low(self, bridge_t_low: bool): + status = self._dll.setLumpedBridgeTLow(bridge_t_low) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def bridge_t_high(self) -> bool: + """Flag indicating if the bridgeT topology for the higher frequency band in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + bridge_t_high = c_bool() + status = self._dll.getLumpedBridgeTHigh(byref(bridge_t_high)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(bridge_t_high.value) + + @bridge_t_high.setter + def bridge_t_high(self, bridge_t_high: bool): + status = self._dll.setLumpedBridgeTHigh(bridge_t_high) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def equal_inductors(self) -> bool: + """Flag indicating if the equal inductors topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + equal_inductors = c_bool() + status = self._dll.getLumpedEqualInductors(byref(equal_inductors)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(equal_inductors.value) + + @equal_inductors.setter + def equal_inductors(self, equal_inductors: bool): + status = self._dll.setLumpedEqualInductors(equal_inductors) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def equal_capacitors(self) -> bool: + """Flag indicating if the equal capacitors topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + equal_capacitors = c_bool() + status = self._dll.getLumpedEqualCapacitors(byref(equal_capacitors)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(equal_capacitors.value) + + @equal_capacitors.setter + def equal_capacitors(self, equal_capacitors: bool): + status = self._dll.setLumpedEqualCapacitors(equal_capacitors) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def equal_legs(self) -> bool: + """Flag indicating if the equal pairs shunt or series legs topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + equal_legs = c_bool() + status = self._dll.getLumpedEqualLegs(byref(equal_legs)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(equal_legs.value) + + @equal_legs.setter + def equal_legs(self, equal_legs: bool): + status = self._dll.setLumpedEqualLegs(equal_legs) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def high_low_pass(self) -> bool: + """Flag indicating if the high and low pass topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + high_low_pass = c_bool() + status = self._dll.getLumpedHighLowPass(byref(high_low_pass)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(high_low_pass.value) + + @high_low_pass.setter + def high_low_pass(self, high_low_pass: bool): + status = self._dll.setLumpedHighLowPass(high_low_pass) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def high_low_pass_min_ind(self) -> bool: + """Flag indicating if the high and low pass topology with minimum inductors + in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + high_low_pass_min_ind = c_bool() + status = self._dll.getLumpedHighLowPassMinInd(byref(high_low_pass_min_ind)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(high_low_pass_min_ind.value) + + @high_low_pass_min_ind.setter + def high_low_pass_min_ind(self, high_low_pass_min_ind: bool): + status = self._dll.setLumpedHighLowPassMinInd(high_low_pass_min_ind) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def zig_zag(self) -> bool: + """Flag indicating if the zig-zag topology with minimum inductors in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + zig_zag = c_bool() + status = self._dll.getLumpedZigZag(byref(zig_zag)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(zig_zag.value) + + @zig_zag.setter + def zig_zag(self, zig_zag: bool): + status = self._dll.setLumpedZigZag(zig_zag) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def min_ind(self) -> bool: + """Flag indicating if the minimum inductors topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + min_ind = c_bool() + status = self._dll.getLumpedMinInd(byref(min_ind)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(min_ind.value) + + @min_ind.setter + def min_ind(self, min_ind: bool): + status = self._dll.setLumpedMinInd(min_ind) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def min_cap(self) -> bool: + """Flag indicating if the minimum capacitors topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + min_cap = c_bool() + status = self._dll.getLumpedMinCap(byref(min_cap)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(min_cap.value) + + @min_cap.setter + def min_cap(self, min_cap: bool): + status = self._dll.setLumpedMinCap(min_cap) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def set_source_res(self) -> bool: + """Flag indicating if the matched source resistor for zig-zag topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + set_source_res = c_bool() + status = self._dll.getLumpedSourceRes(byref(set_source_res)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(set_source_res.value) + + @set_source_res.setter + def set_source_res(self, set_source_res: bool): + status = self._dll.setLumpedSourceRes(set_source_res) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def trap_topology(self) -> bool: + """Flag indicating if the trap topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + trap_topology = c_bool() + status = self._dll.getLumpedTrapTopology(byref(trap_topology)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(trap_topology.value) + + @trap_topology.setter + def trap_topology(self, trap_topology: bool): + status = self._dll.setLumpedTrapTopology(trap_topology) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def node_cap_ground(self) -> bool: + """Flag indicating if the parasitic capacitors to ground topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + node_cap_ground = c_bool() + status = self._dll.getLumpedNodeCapGround(byref(node_cap_ground)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(node_cap_ground.value) + + @node_cap_ground.setter + def node_cap_ground(self, node_cap_ground: bool): + status = self._dll.setLumpedNodeCapGround(node_cap_ground) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def match_impedance(self) -> bool: + """Flag indicating if the automatic matched impedance topology in the synthesized circuit is enabled. + + Returns + ------- + bool + """ + match_impedance = c_bool() + status = self._dll.getLumpedMatchImpedance(byref(match_impedance)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(match_impedance.value) + + @match_impedance.setter + def match_impedance(self, match_impedance: bool): + status = self._dll.setLumpedMatchImpedance(match_impedance) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def complex_termination(self) -> bool: + """Flag indicating if the lumped filter complex termination is enabled. + + Returns + ------- + bool + """ + complex_termination = c_bool() + status = self._dll.getLumpedComplexTermination(byref(complex_termination)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(complex_termination.value) + + @complex_termination.setter + def complex_termination(self, complex_termination: bool): + status = self._dll.setLumpedComplexTermination(complex_termination) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + @property + def complex_element_tune_enabled(self) -> bool: + """Flag indicating if the element tune option is enabled. + + Returns + ------- + bool + """ + complex_element_tune_enabled = c_bool() + status = self._dll.getLumpedComplexElementTuneEnabled(byref(complex_element_tune_enabled)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return bool(complex_element_tune_enabled.value) + + @complex_element_tune_enabled.setter + def complex_element_tune_enabled(self, complex_element_tune_enabled: bool): + status = self._dll.setLumpedComplexElementTuneEnabled(complex_element_tune_enabled) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def circuit_response(self): + """Execute real filter synthesis""" + size = c_int() + status = self._dll.getLumpedCircuitResponseSize(byref(size)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + circuit_response_string = self._dll_interface.get_string( + self._dll.getLumpedCircuitResponse, max_size=size.value + ) + return circuit_response_string diff --git a/pyaedt/filtersolutions_core/multiple_bands_table.py b/pyaedt/filtersolutions_core/multiple_bands_table.py new file mode 100644 index 00000000000..1db8ac0d5a1 --- /dev/null +++ b/pyaedt/filtersolutions_core/multiple_bands_table.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import POINTER +from ctypes import byref +from ctypes import c_char_p +from ctypes import c_int +from ctypes import create_string_buffer + +import pyaedt + + +class MultipleBandsTable: + """Manipulates access to the entries of multiple bands table. + + This class allows you to enter, edit, or remove entries in the multiple bands table. + The table includes the lower and upper frequencies of the bands. + To access the multiple bands table, use the ``multiple_bands_table`` attribute of the ``FilterSolutions`` class. + A valid multiple bands table must include both lower and upper frequencies for each band. + """ + + def __init__(self): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_multiple_bands_dll_functions() + + def _define_multiple_bands_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.getMultipleBandsTableRowCount.argtype = POINTER(c_int) + self._dll.getMultipleBandsTableRowCount.restype = c_int + + self._dll.getMultipleBandsTableRow.argtypes = [c_int, c_char_p, c_char_p, c_int] + self._dll.getMultipleBandsTableRow.restype = c_int + + self._dll.updateMultipleBandsTableRow.argtypes = [c_int, c_char_p, c_char_p] + self._dll.updateMultipleBandsTableRow.restype = c_int + + self._dll.appendMultipleBandsTableRow.argtypes = [c_char_p, c_char_p] + self._dll.appendMultipleBandsTableRow.restype = c_int + + self._dll.insertMultipleBandsTableRow.argtypes = [c_int, c_char_p, c_char_p] + self._dll.insertMultipleBandsTableRow.restype = c_int + + self._dll.removeMultipleBandsTableRow.argtype = c_int + self._dll.removeMultipleBandsTableRow.restype = c_int + + @property + def row_count(self) -> int: + """Total number of rows present in the multiple bands table. + The default is ``2``. + + Returns + ------- + int + Current number of rows in the multiple bands table. + """ + table_row_count = c_int() + status = self._dll.getMultipleBandsTableRowCount(byref(table_row_count)) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(table_row_count.value) + + def row(self, row_index): + """Get the lower and upper frequency values for a row in the multiple bands table. + + Parameters + ---------- + row_index: int + Index of the row from to get the frequency values from. + Valid values range from ``0`` to ``6``, inclusive. + + Returns + ------- + tuple + The tuple contains three strings.The first is the lower frequency value, + and the second is the upper frequency value. + + """ + lower_value_buffer = create_string_buffer(100) + upper_value_buffer = create_string_buffer(100) + status = self._dll.getMultipleBandsTableRow(row_index, lower_value_buffer, upper_value_buffer, 100) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + lower_value_string = lower_value_buffer.value.decode("utf-8") + upper_value_string = upper_value_buffer.value.decode("utf-8") + return lower_value_string, upper_value_string + + def update_row(self, row_index, lower_frequency="", upper_frequency=""): + """Update lower and upper frequency values for a row in the multiple bands table. + + Parameters + ---------- + row_index: int + Index of the row to update. Valid values range from ``0`` to ``6``, inclusive. + lower_frequency: str, optional + New lower frequency value to set for the row. + If this value is not provided, the row's lower frequency remains unchanged. + upper_frequency: str, optional + New upper frequency value to set for the row. + If this value is not provided, the row's upper frequency remains unchanged. + """ + lower_bytes_value = bytes(lower_frequency, "ascii") + upper_bytes_value = bytes(upper_frequency, "ascii") + status = self._dll.updateMultipleBandsTableRow(row_index, lower_bytes_value, upper_bytes_value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def append_row(self, lower_frequency, upper_frequency): + """Append a new row with specified lower and upper frequency values to the end of the multiple bands table. + + Parameters + ---------- + lower_frequency: str + Lower frequency value for the new row. + upper_frequency: str + Upper frequency value for the new row. + """ + lower_bytes_value = bytes(lower_frequency, "ascii") + upper_bytes_value = bytes(upper_frequency, "ascii") + status = self._dll.appendMultipleBandsTableRow(lower_bytes_value, upper_bytes_value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def insert_row(self, row_index, lower_frequency, upper_frequency): + """Insert lower and upper frequencies in a given row. + + Parameters + ---------- + row_index: int + Index of the row. Valid values range from ``0`` to ``6``, inclusive. + lower_frequency: str + Lower frequency value to insert. + upper_frequency: str + Upper frequency value to insert. + """ + lower_bytes_value = bytes(lower_frequency, "ascii") + upper_bytes_value = bytes(upper_frequency, "ascii") + status = self._dll.insertMultipleBandsTableRow(row_index, lower_bytes_value, upper_bytes_value) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def remove_row(self, row_index): + """Remove a row specified by its index from the multiple bands table. + + Parameters + ---------- + row_index: int + The index of the row to be removed from the multiple bands table. + Valid values range from ``0``to ``6``, inclusive. + """ + status = self._dll.removeMultipleBandsTableRow(row_index) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) diff --git a/pyaedt/filtersolutions_core/transmission_zeros.py b/pyaedt/filtersolutions_core/transmission_zeros.py new file mode 100644 index 00000000000..2153e61db69 --- /dev/null +++ b/pyaedt/filtersolutions_core/transmission_zeros.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ctypes import POINTER +from ctypes import byref +from ctypes import c_bool +from ctypes import c_char_p +from ctypes import c_int +from ctypes import create_string_buffer +from enum import Enum + +import pyaedt + + +class TableFormat(Enum): + """Enumeration of transmission zeros table. + + **Attributes:** + + - RATIO: Represents transmission zeros ratio table. + - BANDWIDTH: Represents transmission zeros bandwidth table. + """ + + RATIO = 0 + BANDWIDTH = 1 + + +class TransmissionZeros: + """Manipulates access to ratio and bandwidth entries in the tranmsission zeros table. + + This class lets you to enter, edit, or remove ratio and bandwidth entries + in the tranmsission zeros table. + The table includes the ratio or bandwidth and the position of the element creating + the transmission zero in the associated circuit. + The position of the transmission zero is the position of the element in + the associated circuit that creates the transmission zero. + The position is defined automatically if no value is provided. + """ + + def __init__(self, table_format): + self._dll = pyaedt.filtersolutions_core._dll_interface()._dll + self._dll_interface = pyaedt.filtersolutions_core._dll_interface() + self._define_transmission_zeros_dll_functions() + self.table_format = table_format + + def _define_transmission_zeros_dll_functions(self): + """Define C++ API DLL functions.""" + self._dll.getTransmissionZerosTableRowCount.argtypes = [POINTER(c_int), c_bool] + self._dll.getTransmissionZerosTableRowCount.restype = c_int + + self._dll.getTransmissionZerosTableRow.argtypes = [ + c_int, + c_char_p, + c_char_p, + c_bool, + c_int, + ] + self._dll.getTransmissionZerosTableRow.restype = c_int + + self._dll.updateTransmissionZerosTableRow.argtypes = [ + c_int, + c_char_p, + c_char_p, + c_bool, + ] + self._dll.updateTransmissionZerosTableRow.restype = c_int + + self._dll.appendTransmissionZerosTableRow.argtypes = [ + c_char_p, + c_char_p, + c_bool, + ] + self._dll.appendTransmissionZerosTableRow.restype = c_int + + self._dll.insertTransmissionZerosTableRow.argtypes = [ + c_int, + c_char_p, + c_char_p, + c_bool, + ] + self._dll.insertTransmissionZerosTableRow.restype = c_int + + self._dll.removeTransmissionZerosTableRow.argtypes = [c_int, c_bool] + self._dll.removeTransmissionZerosTableRow.restype = c_int + + self._dll.clearTransmissionZerosTableRow.argtype = c_bool + self._dll.clearTransmissionZerosTableRow.restype = c_int + + self._dll.defaultPositionEnabled.argtype = c_bool + self._dll.defaultPositionEnabled.restype = c_int + + def table_format_to_bool(self): + """Check if the entry format of the transmission zeros tables is ratio. + If ``False``, the entry format is bandwidth. + + Returns + ------- + bool + """ + if self.table_format.value == TableFormat.BANDWIDTH.value: + return False + else: + return True + + @property + def row_count(self) -> int: + """Number of transmission zeros in the transmission zeros table. + The default is ``2``. + + Returns + ------- + int + """ + table_row_count = c_int() + status = self._dll.getTransmissionZerosTableRowCount(byref(table_row_count), self.table_format_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + return int(table_row_count.value) + + def row(self, row_index): + """Get the transmission zero ratio or bandwidth and the position of the element + causing the transmission zero from a row in the transmission zeros table. + + Parameters + ---------- + row_index: int + Index of the row. Valid values range from ``0`` to ``9``, inclusive. + + Returns + ------- + tuple + The tuple contains two strings.The first is the transmission zero ratio or bandwidth value, + and the second is the position of the element causing the transmission zero. + """ + zero_value_buffer = create_string_buffer(100) + position_value_buffer = create_string_buffer(100) + status = self._dll.getTransmissionZerosTableRow( + row_index, + zero_value_buffer, + position_value_buffer, + self.table_format_to_bool(), + 100, + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + zero_value_string = zero_value_buffer.value.decode("utf-8") + position_value_string = position_value_buffer.value.decode("utf-8") + return zero_value_string, position_value_string + + def update_row(self, row_index, zero="", position=""): + """Update the transmission zero ratio or bandwidth and its position + for a row in the transmission zeros table. + + Parameters + ---------- + row_index: int + Index of the row. Valid values range from ``0`` to ``9``, inclusive. + zero: str, optional + New transmission zero ratio or bandwidth value to set. + If no value is specified, the value remains unchanged. + position: str, optional + New position of the element causing the transmission zero in the circuit. + If no value is specified, the value remains unchanged. + """ + zero_bytes_value = bytes(zero, "ascii") + position_bytes_value = bytes(position, "ascii") + status = self._dll.updateTransmissionZerosTableRow( + row_index, + zero_bytes_value, + position_bytes_value, + self.table_format_to_bool(), + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def append_row(self, zero, position=""): + """Append a new row that includes the ratio or bandwidth and position. + + Parameters + ---------- + zero: str + Transmission zero ratio or bandwidth value. + position: str + Position of the element creating transmission zero in the associated circuit. + """ + zero_bytes_value = bytes(zero, "ascii") + position_bytes_value = bytes(position, "ascii") + status = self._dll.appendTransmissionZerosTableRow( + zero_bytes_value, position_bytes_value, self.table_format_to_bool() + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def insert_row(self, row_index, zero, position=""): + """Insert a new row that includes the ratio or bandwidth and the position. + + Parameters + ---------- + row_index: int + Index for the new row in the transmission zeros table. Valid values range from ``0`` to ``9``, inclusive. + zero: str + Transmission zero ratio or bandwidth value. + position: str + Position of the element creating transmission zero in the associated circuit. + """ + zero_bytes_value = bytes(zero, "ascii") + position_bytes_value = bytes(position, "ascii") + status = self._dll.insertTransmissionZerosTableRow( + row_index, + zero_bytes_value, + position_bytes_value, + self.table_format_to_bool(), + ) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def remove_row(self, row_index): + """Remove a row, including the ratio or bandwidth and the position. + + Parameters + ---------- + row_index: int + Row index in the transmission zeros table. Valid values range from ``0`` to ``9``, inclusive. + """ + status = self._dll.removeTransmissionZerosTableRow(row_index, self.table_format_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def clear_row(self): + """Clear all entries in the transmission zeros table.""" + status = self._dll.clearTransmissionZerosTableRow(self.table_format_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) + + def restore_default_positions(self): + """Restore default positions of transmissison zeros.""" + status = self._dll.defaultPositionEnabled(self.table_format_to_bool()) + pyaedt.filtersolutions_core._dll_interface().raise_error(status) diff --git a/pyaedt/generic/design_types.py b/pyaedt/generic/design_types.py index e54feb076b8..10c408acfc7 100644 --- a/pyaedt/generic/design_types.py +++ b/pyaedt/generic/design_types.py @@ -49,6 +49,43 @@ Simplorer = TwinBuilder +def FilterSolutions( + version=None, + implementation_type=None, +): + """Initialize a ``FilterSolutions` instance. + + Parameters + ---------- + version : str optional + Version of AEDT to use in ``xxxx.x`` format to use. The default is ``None``, in which case the + active setup or latest installed version is used. + implementation_type : enum, optional + Type of filter implementation available from the ``FilterImplementation`` enum. + The default is ``None``, in which case the lumped implementation is used. + + Returns + ------- + :class:`pyaedt.filtersolutions.FilterSolutions` + + Examples + -------- + Define a band-pass Butterworth filter with a center frequency of 1 GHz and a pass band width of 500 MHz. + + design = pyaedt.FilterSolutions(version="2025.1", implementation_type= FilterImplementation.LUMPED) + design.attributes.filter_class = FilterClass.BAND_PASS + design.attributes.filter_type = FilterType.BUTTERWORTH + design.attributes.pass_band_center_frequency = "1G" + design.attributes.pass_band_width_frequency = "500M" + """ + from pyaedt.filtersolutions import FilterSolutions as app + + return app( + version=version, + implementation_type=implementation_type, + ) + + def launch_desktop( version=None, non_graphical=False,