├── .gitignore
├── .gitmodules
├── LICENSE.txt
├── Makefile
├── PyNEC
├── .gitignore
├── CHANGES.md
├── INSTALL.md
├── LICENCE.txt
├── MANIFEST.in
├── Makefile
├── PyNEC.i
├── README.md
├── build.sh
├── example
│ ├── .gitignore
│ ├── README.md
│ ├── antenna_util.py
│ ├── context_clean.py
│ ├── dipole.py
│ ├── impedance_plot.py
│ ├── logperiodic_opt.py
│ ├── monopole.py
│ ├── monopole_realistic_ground_plane.py
│ ├── optimized.py
│ ├── radiation_pattern.py
│ ├── test_ai.py
│ ├── test_charge_densities.py
│ ├── test_ne_nh.py
│ ├── test_nrp.py
│ ├── test_rp.py
│ ├── test_rp2.py
│ ├── test_se.py
│ ├── test_structure_currents.py
│ └── test_surface_patch_currents.py
├── interface_files
│ ├── c_geometry.i
│ ├── math_util.i
│ ├── nec_antenna_input.i
│ ├── nec_context.i
│ ├── nec_ground.i
│ ├── nec_near_field_pattern.i
│ ├── nec_norm_rx_pattern.i
│ ├── nec_radiation_pattern.i
│ ├── nec_structure_currents.i
│ ├── nec_structure_excitation.i
│ └── safe_array.i
├── pyproject.toml
├── setup.py
└── tests
│ ├── .gitignore
│ ├── Makefile
│ ├── __init__.py
│ ├── test_examples.py
│ ├── test_get_gain.py
│ └── test_multiple_sc_cards.py
├── README.md
├── build_wheels.sh
├── dev-requirements.txt
└── necpp
├── .gitignore
├── INSTALL.md
├── LICENCE.txt
├── MANIFEST.in
├── Makefile
├── README.md
├── build.sh
├── example
├── .gitignore
├── Makefile
├── README.md
├── antenna_util.py
├── different_material.py
├── impedance_plot.py
├── monopole.py
└── optimized.py
├── necpp.i
├── setup.cfg
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | MANIFEST
6 | README.txt
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | env/
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *,cover
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 |
56 | # Sphinx documentation
57 | docs/_build/
58 |
59 | # PyBuilder
60 | target/
61 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "necpp_src"]
2 | path = necpp_src
3 | url = https://github.com/tmolteno/necpp.git
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PLAT=manylinux1_x86_64
2 | DOCKER_IMAGE=quay.io/pypa/manylinux2010_x86_64
3 | PRE_CMD=
4 | docker-wheels:
5 | docker run --rm -e PLAT=${PLAT} -v `pwd`:/io ${DOCKER_IMAGE} ${PRE_CMD} /io/build_wheels.sh
6 | auditwheel repair /output/mylibrary*whl -w /output
7 |
8 | install:
9 | docker pull ${DOCKER_IMAGE}
10 |
--------------------------------------------------------------------------------
/PyNEC/.gitignore:
--------------------------------------------------------------------------------
1 | build/**
2 | *.pyc
3 | PyNEC.py
4 | *.cxx
5 | *.png
6 | necpp_src
7 |
--------------------------------------------------------------------------------
/PyNEC/CHANGES.md:
--------------------------------------------------------------------------------
1 | ### Version 1.7.3.6:
2 |
3 | * Update with a requested fix by user slawkory in context_clean.py
4 | * Also fix an intger division bug introduced with the shift to python3 in logperiodic_opt.py
5 |
--------------------------------------------------------------------------------
/PyNEC/INSTALL.md:
--------------------------------------------------------------------------------
1 | ## Building from Source
2 |
3 | aptitude install swig3.0
4 | git submodule init
5 | git submodule update --remote
6 | cd PyNEC
7 | ./build.sh
8 | sudo python setup.py install
9 |
10 |
11 | ## Uploading the package to pypi
12 |
13 | Source & Binary Distribution
14 | python3 setup.py sdist
15 | python3 setup.py bdist_wheel
16 |
17 | python3 setup.py upload
18 |
--------------------------------------------------------------------------------
/PyNEC/LICENCE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/PyNEC/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENCE.txt
2 |
3 | include necpp_src/src/*.h
4 |
5 | include necpp_src/config.h
6 |
7 | include example/*.py
8 |
9 |
--------------------------------------------------------------------------------
/PyNEC/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | python3 setup.py sdist
3 | #python3 setup.py bdist_wheel
4 |
5 | clean:
6 | rm -rf build
7 | rm -rf dist
8 |
9 | test-upload:
10 | python3 -m pip install --user --upgrade twine
11 |
12 | python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
13 |
14 | upload:
15 | python3 -m twine upload dist/*
16 |
17 | test-install:
18 | python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps PyNEC --upgrade
19 |
20 |
--------------------------------------------------------------------------------
/PyNEC/PyNEC.i:
--------------------------------------------------------------------------------
1 | %module PyNEC
2 |
3 | %include
4 | %include
5 |
6 | %{
7 | #include "Python.h"
8 | #include "numpy/arrayobject.h"
9 | #include "numpy/ndarraytypes.h"
10 | #include "src/math_util.h"
11 | #include "src/nec_context.h"
12 | #include "src/c_geometry.h"
13 | #include "src/nec_radiation_pattern.h"
14 | #include "src/nec_structure_currents.h"
15 | #include "src/nec_results.h"
16 | #include "src/nec_ground.h"
17 | #include "src/safe_array.h"
18 | #include "src/nec_exception.h"
19 | #include
20 | %}
21 |
22 | /*! Exception handling stuff */
23 |
24 | %include exception.i
25 | %exception
26 | {
27 | try {
28 | $action
29 | }
30 | catch (nec_exception* nex) {
31 | SWIG_exception(SWIG_RuntimeError,nex->get_message().c_str());
32 | }
33 | catch (const char* message) {
34 | SWIG_exception(SWIG_RuntimeError,message);
35 | }
36 | catch (...) {
37 | SWIG_exception(SWIG_RuntimeError,"Unknown exception");
38 | }
39 | }
40 |
41 | /*! The following typemaps allow the automatic conversion of vectors and safe_arrays into numpy arrays */
42 |
43 | %typemap (out) real_matrix {
44 | int nd = 2;
45 | npy_intp rows = $1.rows();
46 | npy_intp cols = $1.cols();
47 | npy_intp size[2] = {rows, cols};
48 | PyArrayObject* ret =(PyArrayObject *)(PyArray_SimpleNew(nd, &size[0], NPY_FLOAT64));
49 | for (int32_t i=0; i {
86 | int nd = 1;
87 | npy_intp size = $1.size();
88 | PyArrayObject* ret =(PyArrayObject *)(PyArray_SimpleNew(nd, &size, NPY_FLOAT64));
89 | for (int64_t i=0; i {
95 | int nd = 1;
96 | npy_intp size = $1.size();
97 | PyArrayObject* ret =(PyArrayObject *)(PyArray_SimpleNew(nd, &size, NPY_INT32));
98 | for (int64_t i=0; i {
104 | int nd = 1;
105 | npy_intp size = $1.size();
106 | PyArrayObject* ret =(PyArrayObject *)(PyArray_SimpleNew(nd, &size, NPY_COMPLEX128));
107 | for (int64_t i=0; i= 0.15.0 due to the usage of scipy.optimize.differential_evolution.
96 | * monopole_realistic_ground_plane.py plots the vertical gain pattern of a monopole antenna. Its dimensions are optimized with a local search, and the path through the search space is visualized with a heat map.
97 | * dipole.py does a very simple optimization of a dipole, and plots the VSWR over a given frequency range for different system impedances to file.
98 |
99 |
100 |
--------------------------------------------------------------------------------
/PyNEC/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Build script for the PyNEC module.
4 | #
5 | # Author. Tim Molteno.
6 | #
7 | # FIrst have to do git submodule init
8 | git submodule update --remote
9 | ln -s ../necpp_src .
10 | DIR=`pwd`
11 | cd ../necpp_src
12 | make -f Makefile.git
13 | ./configure --without-lapack
14 | cd ${DIR}
15 |
16 | # Build PyNEC
17 | swig -Wall -v -c++ -python PyNEC.i
18 | python3 setup.py build
19 |
--------------------------------------------------------------------------------
/PyNEC/example/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/PyNEC/example/README.md:
--------------------------------------------------------------------------------
1 | # PyNEC examples
2 |
3 | This folder contains some examples showing the use of PyNEC antenna simulation module
4 |
5 | ## Optimizing a Monopole
6 |
7 | The file monopole.py simulates a monopole antenna, printing out the impedance. To optimize this
8 |
9 | python3 monopole.py
10 |
11 | To optimize the monopole design to achieve a particular impedance, try
12 |
13 | python3 optimized.py --basinhopping --target-impedance=110
14 |
15 | This code uses standard scipy optimizers to find the best solution.
16 |
--------------------------------------------------------------------------------
/PyNEC/example/antenna_util.py:
--------------------------------------------------------------------------------
1 | #
2 | # Some antenna utility functions
3 | #
4 | import numpy as np
5 |
6 | def reflection_coefficient(z, z0):
7 | return np.abs((z - z0) / (z + z0))
8 |
9 | def vswr(z, z0):
10 | Gamma = reflection_coefficient(z, z0)
11 | return float((1 + Gamma) / (1 - Gamma))
12 |
13 | def mismatch(z, z0):
14 | Gamma = reflection_coefficient(z, z0)
15 | return 1 - Gamma**2
16 |
17 | # Source: https://gist.github.com/temporaer/6755266
18 | # 'matplotlib log-polar plots seem to be quite buggy at the time of writing.' Yes, indeed, sadly...
19 | def plot_logpolar(ax, theta, r_, bullseye=None, **kwargs):
20 | min10 = np.log10(np.min(r_))
21 | max10 = np.log10(np.max(r_))
22 | if bullseye is None:
23 | bullseye = min10 - np.log10(0.5 * np.min(r_))
24 | r = np.log10(r_) - min10 + bullseye
25 | ax.plot(theta, r, **kwargs)
26 | l = np.arange(np.floor(min10), max10)
27 | ax.set_rticks(l - min10 + bullseye)
28 | # ax.set_yticklabels(["1e%d" % x for x in l])
29 | ax.set_yticklabels(["%d" % x for x in l])
30 | ax.set_rlim(0, max10 - min10 + bullseye)
31 | return ax
32 |
--------------------------------------------------------------------------------
/PyNEC/example/context_clean.py:
--------------------------------------------------------------------------------
1 | # Note: explicit zeroes are blanks. All other values should be specified symbolically.
2 |
3 | # Currently these contain only the subset of cards that I needed
4 |
5 | class Range(object):
6 | def __init__(self, start, stop, count=None, delta=None):
7 | self.start = start
8 | self.stop = stop
9 | if count is not None:
10 | self.count = count
11 | self.delta = (stop - start) / count
12 | else:
13 | self.count = (stop_ - start) / delta
14 | self.delta = delta
15 |
16 | # Setting do_debug to True will dump all the cards generated with context_clean, so you can verify the output more easily in a text editor (and debug that file manually)
17 | do_debug = False
18 |
19 | def debug(card, *args):
20 | if do_debug:
21 | stringified = " , ".join([str(a) for a in args])
22 | print("%s %s" % (card, stringified))
23 |
24 | class context_clean(object):
25 | def __init__(self, context):
26 | self.context = context
27 |
28 | def remove_all_loads(self):
29 | ld_short_all_loads = -1
30 | self.context.ld_card(ld_short_all_loads, 0, 0, 0, 0, 0, 0)
31 |
32 | def set_wire_conductivity(self, conductivity, wire_tag=None):
33 | """ The conductivity is specified in mhos/meter. Currently all segments of a wire are set. If wire_tag is None, all wire_tags are set (i.e., a tag of 0 is used). """
34 | if wire_tag is None:
35 | wire_tag = 0
36 |
37 | debug("LD", 5, wire_tag, 0, 0, conductivity, 0, 0)
38 | self.context.ld_card(5, wire_tag, 0, 0, conductivity, 0, 0)
39 |
40 | def set_all_wires_conductivity(self, conductivity):
41 | self.set_wire_conductivity(conductivity)
42 |
43 | # TODO: multiplicative
44 | def set_frequencies_linear(self, start_frequency, stop_frequency, count=None, step_size=None):
45 | """ If start_frequency does not equal stop_frequency, either count or step should be specified. The other parameter will be automatically deduced """
46 |
47 | if start_frequency == stop_frequency:
48 | step_size = 0
49 | count = 1
50 | else:
51 | # TODO: add some asserts
52 | if count is not None:
53 | step_size = (stop_frequency - start_frequency) / count
54 | else:
55 | count = (stop_frequency - start_frequency) / step_size
56 |
57 | # TODO, what if we don't have nice divisibility here
58 | count = int(count)
59 |
60 | ifrq_linear_step = 0
61 | debug("FR", ifrq_linear_step, count, start_frequency, step_size, 0, 0, 0)
62 | self.context.fr_card(ifrq_linear_step, count, start_frequency, step_size)
63 |
64 | def set_frequency(self, frequency):
65 | self.set_frequencies_linear(frequency, frequency)
66 |
67 | def clear_ground(self):
68 | gn_nullify_ground = -1
69 | self.context.gn_card(gn_nullify_ground, 0, 0, 0, 0, 0, 0, 0)
70 |
71 | # TODO: I could probably make a ground class, would probably be cleaner to group some of the options and different functions there (like combining ground screen etc)
72 |
73 | # TODO: gn card is iffy, check!
74 | def set_finite_ground(self, ground_dielectric, ground_conductivity):
75 | gn_finite_ground = 0
76 | no_ground_screen = 0
77 |
78 | self.context.gn_card(gn_finite_ground, no_ground_screen, ground_dielectric, ground_conductivity, 0, 0, 0, 0)
79 |
80 | def set_perfect_ground(self):
81 | gn_perfectly_conducting = 1
82 | no_ground_screen = 0
83 |
84 | debug("GN", gn_perfectly_conducting, no_ground_screen, 0, 0, 0, 0, 0, 0)
85 | self.context.gn_card(gn_perfectly_conducting, no_ground_screen, 0, 0, 0, 0, 0, 0)
86 |
87 |
88 | # TODO: i1 = 5 is also a voltage excitation
89 | def voltage_excitation(self, wire_tag, segment_nr, voltage):
90 | ex_voltage_excitation = 0
91 | no_action = 0 # TODO configurable
92 | option_i3i4 = 10*no_action + no_action
93 |
94 | debug("EX", ex_voltage_excitation, wire_tag, segment_nr, option_i3i4, voltage.real, voltage.imag, 0, 0, 0, 0)
95 | self.context.ex_card(ex_voltage_excitation, wire_tag, segment_nr, option_i3i4, voltage.real, voltage.imag, 0, 0, 0, 0)
96 |
97 | def get_geometry(self):
98 | #return geometry_clean(self.context.get_geometry()) # TODO
99 | return self.context.get_geometry()
100 |
101 | def set_extended_thin_wire_kernel(self, enable):
102 | if enable:
103 | debug ("EK", 0)
104 | self.context.set_extended_thin_wire_kernel(True)
105 | else:
106 | debug ("EK", -1)
107 | self.context.set_extended_thin_wire_kernel(False)
108 |
109 | def geometry_complete(self, ground_plane, current_expansion=True):
110 | no_ground_plane = 0
111 | ground_plane_current_expansion = 1
112 | ground_plane_no_current_expansion = -1
113 | if not ground_plane:
114 | debug("GE", no_ground_plane)
115 | self.context.geometry_complete(no_ground_plane)
116 | else:
117 | if current_expansion:
118 | debug("GE", ground_plane_current_expansion)
119 | self.context.geometry_complete(ground_plane_current_expansion)
120 | else:
121 | debug("GE", ground_plane_no_current_expansion)
122 | self.context.geometry_complete(ground_plane_no_current_expansion)
123 |
124 | output_major_minor = 0
125 | output_vertical_horizontal = 1
126 |
127 | normalization_none = 0
128 | normalization_major = 1
129 | normalization_minor = 2
130 | normalization_vertical = 3
131 | normalization_horizontal = 4
132 | normalization_totalgain = 5
133 |
134 | power_gain = 0
135 | directive_gain = 1
136 |
137 | average_none = 0
138 | average_gain = 1
139 | average_todo = 2
140 |
141 | # TODO: this should be different for surface_wave_mode (1), because then thetas = z
142 | def radiation_pattern(self, thetas, phis, output_mode=output_vertical_horizontal, normalization=normalization_none, gain=power_gain, average=average_todo):
143 | """ thetas and phis should be Range(-like) objects """
144 | normal_mode = 0 # TODO other modes
145 |
146 | # the rp_card already has XNDA as separate arguments
147 | radial_distance = 0 # TODO
148 | gnornamize_maximum = 0 # TODO
149 |
150 | xnda = average + 10*gain+100*normalization+1000*output_mode
151 |
152 | debug("RP", normal_mode, thetas.count, phis.count, xnda, thetas.start, phis.start, thetas.delta, phis.delta, radial_distance, gnornamize_maximum)
153 | self.context.rp_card(normal_mode, thetas.count, phis.count, output_mode, normalization, gain, average, thetas.start, phis.start, thetas.delta, phis.delta, radial_distance, gnornamize_maximum)
154 |
155 | # TODO: shunt admittances, length of transmission line if not straight-line distance
156 | def transmission_line(self, src, dst, impedance, crossed_line=False, length=None, shunt_admittance_src=0, shunt_admittance_dst=0):
157 | """ src and dst are (tag_nr, segment_nr) pairs """
158 | if crossed_line:
159 | impedance *= -1
160 | if length is None:
161 | length = 0
162 | shunt_admittance_src = complex(shunt_admittance_src)
163 | shunt_admittance_dst = complex(shunt_admittance_dst)
164 |
165 | debug("TL", src[0], src[1], dst[0], dst[1], impedance, length, shunt_admittance_src.real, shunt_admittance_src.imag, shunt_admittance_dst.real, shunt_admittance_dst.imag)
166 | self.context.tl_card(src[0], src[1], dst[0], dst[1], impedance, length, shunt_admittance_src.real, shunt_admittance_src.imag, shunt_admittance_dst.real, shunt_admittance_dst.imag)
167 |
168 | # Some simple wrappers for context...
169 | # TODO: this should be simpler, can't this be auto-generated, or implicitly defined? The best solution is of course to do this in the C++ code,
170 | # and then the wrappers are immediately correct and nice
171 | def xq_card(self, *args):
172 | return self.context.xq_card(*args)
173 | def get_input_parameters(self, *args):
174 | return self.context.get_input_parameters(*args)
175 |
176 | class geometry_clean(object):
177 | def __init__(self, geometry):
178 | self.geometry = geometry
179 |
180 | def wire(self, tag_id, nr_segments, src, dst, radius, length_ratio=1.0, radius_ratio=1.0):
181 | """ radius is in meter. length_ratio can be set to have non-uniform segment lengths, radius_ratio can be used for tapered wires """
182 | debug("GW", tag_id, nr_segments, src[0], src[1], src[2], dst[0], dst[1], dst[2], radius) # TODO
183 |
184 | self.geometry.wire(tag_id, nr_segments, src[0], src[1], src[2], dst[0], dst[1], dst[2], radius, length_ratio, radius_ratio)
185 |
--------------------------------------------------------------------------------
/PyNEC/example/dipole.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy.optimize
3 | import pylab as plt
4 |
5 |
6 | from PyNEC import *
7 | from antenna_util import *
8 |
9 | from context_clean import *
10 |
11 | import math
12 |
13 | def geometry(freq_mhz, length, nr_segments):
14 | wire_radius = 0.01e-3 # 0.01 mm
15 |
16 | nec = context_clean(nec_context())
17 | nec.set_extended_thin_wire_kernel(False)
18 |
19 | geo = geometry_clean(nec.get_geometry())
20 |
21 | center = np.array([0,0,0])
22 | half = np.array([length/2, 0, 0])
23 |
24 | pt1 = center - half
25 | pt2 = center + half
26 |
27 | wire_tag = 1
28 | geo.wire(tag_id=wire_tag, nr_segments=nr_segments, src=pt1, dst=pt2, radius=wire_radius)
29 |
30 | nec.geometry_complete(ground_plane=False)
31 |
32 | nec.set_frequency(freq_mhz)
33 |
34 | # Voltage excitation in the center of the antenna
35 | nec.voltage_excitation(wire_tag=wire_tag, segment_nr=int(nr_segments/2), voltage=1.0)
36 |
37 | return nec
38 |
39 | def impedance(freq_mhz, length, nr_segments):
40 | nec = geometry(freq_mhz, length, nr_segments)
41 | nec.xq_card(0)
42 |
43 | index = 0
44 | return nec.get_input_parameters(index).get_impedance()
45 |
46 | def create_optimization_target(freq_mhz, nr_segments):
47 | def target(length):
48 | return abs(impedance(freq_mhz, length[0], nr_segments).imag)
49 | return target
50 |
51 | # It's probably possible that the antenna matches in multiple regions, pick the one around the center frequency...
52 | def matched_range_around(nec, count, center_freq, system_impedance):
53 | # TODO: this is not ideal
54 | min_dist = float('inf')
55 | min_idx = None
56 | target_hz = center_freq * 1000000
57 |
58 | for idx in range(0, count):
59 | dist = abs(nec.get_input_parameters(idx).get_frequency() - target_hz)
60 | if dist < min_dist:
61 | min_dist = dist
62 | min_idx = idx
63 |
64 | idx = min_idx
65 | matched_min_freq = None
66 | while idx >= 0:
67 | ipt = nec.get_input_parameters(idx)
68 | z = ipt.get_impedance()
69 | if vswr(z, system_impedance) > 2:
70 | break
71 | matched_min_freq = ipt.get_frequency() / 1000000
72 | idx -= 1
73 |
74 | idx = min_idx
75 | matched_max_freq = None
76 | while idx < count:
77 | ipt = nec.get_input_parameters(idx)
78 | z = ipt.get_impedance()
79 | if vswr(z, system_impedance) > 2:
80 | break
81 | matched_max_freq = ipt.get_frequency() / 1000000
82 | idx += 1
83 |
84 | return (matched_min_freq, matched_max_freq)
85 |
86 | if (__name__ == '__main__'):
87 | design_freq_mhz = 2450
88 | wavelength = 299792e3/(design_freq_mhz*1000000)
89 |
90 | initial_length = wavelength / 2 # TODO
91 |
92 | print("Wavelength is %0.4fm, initial length is %0.4fm" % (wavelength, initial_length))
93 |
94 | nr_segments = 101 # int(math.ceil(50*initial_length/wavelength))
95 |
96 | z = impedance(design_freq_mhz, initial_length, nr_segments)
97 |
98 | print("Initial impedance: (%6.1f,%+6.1fI) Ohms" % (z.real, z.imag))
99 |
100 | target = create_optimization_target(design_freq_mhz, nr_segments)
101 | optimized_result = scipy.optimize.minimize(target, np.array([initial_length]))
102 | optimized_length = optimized_result.x[0]
103 |
104 | z = impedance(design_freq_mhz, optimized_length, nr_segments)
105 |
106 | print("Optimized length %6.6f m, which gives an impedance of: (%6.4f,%+6.4fI) Ohms" % (optimized_length, z.real, z.imag))
107 | print("VSWR @ 75 Ohm is %6.6f" % vswr(z, 75))
108 |
109 | for system_impedance in [75, 50, 300]:
110 | nec = geometry(design_freq_mhz, optimized_length, nr_segments)
111 |
112 | count = 300
113 | nec.set_frequencies_linear(2300, 2600, count=count)
114 | nec.xq_card(0) # Execute simulation
115 |
116 | # TODO: add get_n_items to nec_antenna_input and co, so we can automatically deduce count etc. Much cleaner
117 |
118 | rng = matched_range_around(nec, count, design_freq_mhz, system_impedance)
119 | if rng[0] is None or rng[1] is None:
120 | print("VSWR is nowhere <= 2 @ %i Ohm!" % system_impedance)
121 | else:
122 | bandwidth = 100.0 * (rng[1] - rng[0]) / design_freq_mhz
123 | print("The fractional bandwidth @ %i Ohm is %2.2f%% - %i MHz (%i Mhz to %i MHz)" % (system_impedance, bandwidth, (rng[1] - rng[0]), rng[0], rng[1]))
124 |
125 | freqs = []
126 | vswrs = []
127 | for idx in range(0, count):
128 | ipt = nec.get_input_parameters(idx)
129 | z = ipt.get_impedance()
130 |
131 | freqs.append(ipt.get_frequency() / 1000000)
132 | vswrs.append(vswr(z, system_impedance))
133 |
134 | # print "%i MHz, Z = %6.6f @ %i" % (ipt.get_frequency(), vswr(z, system_impedance), system_impedance)
135 |
136 | plt.figure()
137 | plt.plot(freqs, vswrs)
138 | plt.title("VSWR of a %.2f mm long dipole for a %i Ohm system" % (optimized_length * 1000.0, system_impedance))
139 | plt.xlabel("Frequency (MHz)")
140 | plt.ylabel("VSWR")
141 | plt.grid(True)
142 | filename = "vswr_%i_MHz.pdf" % system_impedance
143 | print("Saving plot to file: %s" % filename)
144 | plt.savefig(filename)
145 |
--------------------------------------------------------------------------------
/PyNEC/example/impedance_plot.py:
--------------------------------------------------------------------------------
1 | #
2 | # Plot of reflection coefficient vs antenna length for a fixed base height.
3 | #
4 | import monopole
5 | import numpy as np
6 | import pylab as plt
7 | from antenna_util import reflection_coefficient
8 |
9 | lengths = np.linspace(0.2, 5.0, 270)
10 | reflections = []
11 | z0 = 50
12 |
13 | for l in lengths:
14 | freq = 134.5
15 | z = monopole.impedance(freq, base=0.5, length=l)
16 | reflections.append(reflection_coefficient(z, z0))
17 |
18 | plt.plot(lengths, reflections)
19 | plt.xlabel("Antenna length (m)")
20 | plt.ylabel("Reflection coefficient")
21 | plt.title("Reflection coefficient vs length (base_height=0.5m)")
22 | plt.grid(True)
23 | plt.show()
24 | plt.savefig("reflection_coefficient.png")
25 |
26 |
--------------------------------------------------------------------------------
/PyNEC/example/logperiodic_opt.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy.optimize
3 | import matplotlib.pyplot as plt
4 | import matplotlib as mpl
5 |
6 | from PyNEC import *
7 | from antenna_util import *
8 |
9 | from context_clean import *
10 |
11 | import math
12 |
13 | """ Optimize and plot the gains/VSWR of a logperiodic antenna (6 brass elements, 75 Ohm transmission lines) for both the 2.4GHz as the 5.8GHz ISM bands.
14 | Inspired by an excercise for a course, hence the weird constraints. """
15 |
16 | brass_conductivity = 15600000 # mhos
17 |
18 | tl_impedance = 75.0
19 |
20 | def geometry_logperiodic(l_1, x_1, tau):
21 | """
22 | x_1 is the distance from the origin to the largest (farthest away) dipole, which has a length of l_1.
23 | The spacing is as follows: l_{i+1}/l_i = tau = x_{i+1}/x_i
24 | """
25 | wire_radius = 0.00025 # 0.25 mm
26 |
27 | # alpha = np.arctan( (l_1/2.0) / x_1 )
28 |
29 | nec = context_clean(nec_context())
30 | nec.set_extended_thin_wire_kernel(True)
31 |
32 | geo = geometry_clean(nec.get_geometry())
33 |
34 | # Dipoles should be oriented in the Z direction; they should be placed on the (positive) X axis
35 |
36 | x_i = x_1
37 | l_i = x_1
38 |
39 | # As ususal, note that nec tags start at 1, and we typically index from 0!
40 | dipole_center_segs = {} # Maps from NEC wire id!
41 |
42 | dipoles_count = 5
43 |
44 | for dipole_tag in range(1, dipoles_count + 1):
45 | nr_segments = int(math.ceil(50*l_i/wavelength)) # TODO this might vary when sweeping even!
46 | #print nr_segments
47 |
48 | dipole_center_segs[dipole_tag] = nr_segments // 2 + 1
49 |
50 | center = np.array([x_i, 0, 0])
51 | half_height = np.array([0 , 0, l_i/2.0])
52 | top = center + half_height
53 | bottom = center - half_height
54 |
55 | geo.wire(tag_id=dipole_tag, nr_segments=nr_segments, src=bottom, dst=top, radius=wire_radius)
56 |
57 | x_i = tau * x_i
58 | l_i = tau * l_i
59 |
60 | # Everything is in brass
61 | nec.set_wire_conductivity(brass_conductivity)
62 |
63 | nec.geometry_complete(ground_plane=False)
64 |
65 | # The 6th tag is the smallest tag is the source element
66 | for dipole in range(0, dipoles_count - 1):
67 | src_tag = int(1 + dipole) # NEC indexing
68 | src_seg = dipole_center_segs[src_tag]
69 |
70 | dst_tag = src_tag + 1
71 | dst_seg = dipole_center_segs[dst_tag]
72 |
73 | nec.transmission_line((src_tag, src_seg), (dst_tag, dst_seg), tl_impedance, crossed_line=True)
74 |
75 | smallest_dipole_tag = dipoles_count # Again, start at 1
76 |
77 | nec.voltage_excitation(wire_tag=smallest_dipole_tag, segment_nr=dipole_center_segs[smallest_dipole_tag], voltage=1.0)
78 |
79 | return nec
80 |
81 | start = 2300
82 | stop = 5900
83 | count = stop - start
84 |
85 |
86 | def get_gain_swr_range(l_1, x_1, tau, start=start, stop=stop, step=10):
87 | gains_db = []
88 | frequencies = []
89 | vswrs = []
90 | for freq in range(start, stop + 1, step):
91 | nec = geometry_logperiodic(l_1, x_1, tau)
92 | nec.set_frequency(freq) # TODO: ensure that we don't need to re-generate this!
93 | nec.radiation_pattern(thetas=Range(90, 90, count=1), phis=Range(180,180,count=1))
94 |
95 | rp = nec.context.get_radiation_pattern(0)
96 | ipt = nec.get_input_parameters(0)
97 | z = ipt.get_impedance()
98 |
99 | # Gains are in decibels
100 | gains_db.append(rp.get_gain()[0])
101 | vswrs.append(vswr(z, system_impedance))
102 | frequencies.append(ipt.get_frequency())
103 |
104 | return frequencies, gains_db, vswrs
105 |
106 | def create_optimization_target():
107 | def target(args):
108 | l_1, x_1, tau = args
109 | if l_1 <= 0 or x_1 <= 0 or tau <= 0:
110 | return float('inf')
111 |
112 | try:
113 | result = 0
114 |
115 | vswr_score = 0
116 | gains_score = 0
117 |
118 | for range_low, range_high in [ (2400, 2500), (5725, 5875) ]:
119 | freqs, gains, vswrs = get_gain_swr_range(l_1, x_1, tau, start=range_low, stop=range_high)
120 |
121 | for gain in gains:
122 | gains_score += gain
123 | for vswr in vswrs:
124 | if vswr >= 1.8:
125 | vswr = np.exp(vswr) # a penalty :)
126 | vswr_score += vswr
127 |
128 | # VSWR should minimal in both bands, gains maximal:
129 | result = vswr_score - gains_score
130 |
131 | except:
132 | print("Caught exception")
133 | return float('inf')
134 |
135 | print(result)
136 |
137 | return result
138 | return target
139 |
140 |
141 | def simulate_and_get_impedance(nec):
142 | nec.set_frequency(design_freq_mhz)
143 |
144 | nec.xq_card(0)
145 |
146 | index = 0
147 | return nec.get_input_parameters(index).get_impedance()
148 |
149 | system_impedance = 50 # This makes it a bit harder to optimize, given the 75 Ohm TLs, which is good for this excercise of course...
150 |
151 | # (2.4 GHz to 2.5 GHz) and the 5.8 GHz ISM band (5.725 GHz to 5.875 GHz)
152 |
153 | design_freq_mhz = 2450 # The center of the first range
154 | wavelength = 299792e3/(design_freq_mhz*1000000)
155 |
156 | majorLocator = mpl.ticker.MultipleLocator(10)
157 | majorFormatter = mpl.ticker.FormatStrFormatter('%d')
158 | minorLocator = mpl.ticker.MultipleLocator(1)
159 | minorFormatter = mpl.ticker.FormatStrFormatter('%d')
160 |
161 | def draw_frequencie_ranges(ax):
162 | ax.axvline(x=2400, color='red', linewidth=1)
163 | ax.axvline(x=2500, color='red', linewidth=1)
164 | ax.axvline(x=5725, color='red', linewidth=1)
165 | ax.axvline(x=5875, color='red', linewidth=1)
166 |
167 | def show_report(l1, x1, tau):
168 | nec = geometry_logperiodic(l1, x1, tau)
169 |
170 | z = simulate_and_get_impedance(nec)
171 |
172 | print("Initial impedance: (%6.1f,%+6.1fI) Ohms" % (z.real, z.imag))
173 | print("VSWR @ 50 Ohm is %6.6f" % vswr(z, 50))
174 |
175 | nec = geometry_logperiodic(l1, x1, tau)
176 |
177 | freqs, gains, vswrs = get_gain_swr_range(l1, x1, tau, step=5)
178 |
179 | freqs = np.array(freqs) / 1000000 # In MHz
180 |
181 | ax = plt.subplot(111)
182 | ax.plot(freqs, gains)
183 | draw_frequencie_ranges(ax)
184 |
185 | ax.set_title("Gains of a 5-element log-periodic antenna")
186 | ax.set_xlabel("Frequency (MHz)")
187 | ax.set_ylabel("Gain")
188 |
189 | ax.yaxis.set_major_locator(majorLocator)
190 | ax.yaxis.set_major_formatter(majorFormatter)
191 |
192 | ax.yaxis.set_minor_locator(minorLocator)
193 | ax.yaxis.set_minor_formatter(minorFormatter)
194 |
195 | ax.yaxis.grid(b=True, which='minor', color='0.75', linestyle='-')
196 |
197 | plt.show()
198 |
199 | ax = plt.subplot(111)
200 | ax.plot(freqs, vswrs)
201 | draw_frequencie_ranges(ax)
202 |
203 | ax.set_yscale("log")
204 | ax.set_title("VSWR of a 6-element log-periodic antenna @ 50 Ohm impedance")
205 | ax.set_xlabel("Frequency (MHz)")
206 | ax.set_ylabel("VSWR")
207 |
208 | ax.yaxis.set_major_locator(majorLocator)
209 | ax.yaxis.set_major_formatter(majorFormatter)
210 | ax.yaxis.set_minor_locator(minorLocator)
211 | ax.yaxis.set_minor_formatter(minorFormatter)
212 |
213 | ax.yaxis.grid(b=True, which='minor', color='0.75', linestyle='-')
214 | plt.show()
215 |
216 |
217 | if (__name__ == '__main__'):
218 | initial_l1 = wavelength / 2
219 | initial_x1 = wavelength / 2
220 | initial_tau = 0.8
221 |
222 | print("Wavelength is %0.4fm, initial length is %0.4fm" % (wavelength, initial_l1))
223 |
224 | print("Unoptimized antenna...")
225 | show_report(initial_l1, initial_x1, initial_tau)
226 |
227 | print("Optimizing antenna...")
228 | target = create_optimization_target()
229 |
230 | # Optimize local minimum only with gradient desce
231 | #optimized_result = scipy.optimize.minimize(target, np.array([initial_l1, initial_x1, initial_tau]), method='Nelder-Mead')
232 |
233 | # Use differential evolution:
234 | minimizer_kwargs = dict(method='Nelder-Mead')
235 | bounds = [ (0.01, 0.2), (0.01, 0.2), (0.7, 0.9) ]
236 | optimized_result = scipy.optimize.differential_evolution(target, bounds, seed=42, disp=True, popsize=20)
237 |
238 | # Basin hopping isn't so good, but could also have been an option:
239 | #optimized_result = scipy.optimize.basinhopping(target, np.array([initial_l1, initial_x1, initial_tau]), minimizer_kwargs=minimizer_kwargs, niter=5, stepsize=0.015, T=2.0, disp=True)
240 |
241 | print("Optimized antenna...")
242 | optimized_l1, optimized_x1, optimized_tau = optimized_result.x[0], optimized_result.x[1], optimized_result.x[2]
243 | show_report(optimized_l1, optimized_x1, optimized_tau)
244 |
245 |
--------------------------------------------------------------------------------
/PyNEC/example/monopole.py:
--------------------------------------------------------------------------------
1 | #
2 | # Simple vertical monopole antenna simulation using python-necpp
3 | # pip install PyNEC
4 | #
5 | from PyNEC import *
6 |
7 | from context_clean import *
8 |
9 | import math
10 |
11 | def geometry(freq, base, length):
12 | conductivity = 1.45e6 # Stainless steel
13 | ground_conductivity = 0.002
14 | ground_dielectric = 10
15 |
16 | wavelength = 3e8/(1e6*freq)
17 | n_seg = int(math.ceil(50*length/wavelength))
18 |
19 | nec = context_clean(nec_context())
20 |
21 | geo = nec.get_geometry()
22 | geo.wire(1, n_seg, 0, 0, base, 0, 0, base+length, 0.002, 1.0, 1.0)
23 | nec.geometry_complete(1)
24 |
25 | nec.set_all_wires_conductivity(conductivity)
26 |
27 | nec.set_finite_ground(ground_dielectric, ground_conductivity)
28 | nec.set_frequency(freq)
29 |
30 | # Voltage excitation one third of the way along the wire
31 | nec.voltage_excitation(wire_tag=1, segment_nr=int(n_seg/3), voltage=1.0)
32 |
33 | return nec
34 |
35 | def impedance(freq, base, length):
36 | nec = geometry(freq, base, length)
37 | nec.xq_card(0) # Execute simulation
38 |
39 | index = 0
40 |
41 | ipt = nec.get_input_parameters(index)
42 | z = ipt.get_impedance()
43 |
44 | return z
45 |
46 | if (__name__ == '__main__'):
47 | z = impedance(freq = 134.5, base = 0.5, length = 4.0)
48 | print("Impedance at base=%0.2f, length=%0.2f : (%6.1f,%+6.1fI) Ohms" % (0.5, 4.0, z.real, z.imag))
49 |
--------------------------------------------------------------------------------
/PyNEC/example/monopole_realistic_ground_plane.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy.optimize
3 | import matplotlib.pyplot as plt
4 | import matplotlib as mpl
5 |
6 |
7 | from PyNEC import *
8 | from antenna_util import *
9 |
10 | from context_clean import *
11 |
12 | import math
13 |
14 | brass_conductivity = 15600000 # mhos
15 |
16 | """ Optimize and plot the gains/VSWR of a simple monopole antenna, that has some brass wires added that act as ground. Visualises the path in the search space explored
17 | by the minimization algorithm.
18 | Inspired by an excercise for a course on antennas. The constraints are thus not really ideal I think... """
19 |
20 | # TODO: this probably could also be done by the GN/GD cards, but am I allowed?
21 | def add_ground_screen(geo, start_tag, length):
22 | # The ground is modeled 6 radial wires (brass, radius 1 mm), equally spaced by 2pi/6, and their initial length is d = 2 cm
23 |
24 | src = np.array([0, 0, 0])
25 | radius = 0.001
26 |
27 | for i in range(0, 6):
28 | angle = i * (2*np.pi/6.0)
29 | dst = np.array([length * np.cos(angle), length * np.sin(angle), 0])
30 | # TODO: nr_segments (actually, more TODO: if nr_segments>=9, the geometry is invalid with this wire radius!)
31 | geo.wire(tag_id=start_tag+i, nr_segments=5, src=src, dst=dst, radius=radius) # TODO: nr_segments
32 |
33 | def geometry_monopole_ground(freq_mhz, monopole_length, ground_wire_length, nr_segments):
34 | wire_radius = 0.0005 # 0.5 mm
35 |
36 | nec = context_clean(nec_context())
37 | nec.set_extended_thin_wire_kernel(True)
38 |
39 | geo = geometry_clean(nec.get_geometry())
40 |
41 | bottom = np.array([0, 0, 0])
42 | top = np.array([0, 0, monopole_length])
43 |
44 | wire_tag = 1
45 | geo.wire(tag_id=wire_tag, nr_segments=nr_segments, src=bottom, dst=top, radius=wire_radius)
46 |
47 | add_ground_screen(geo, start_tag=2, length=ground_wire_length)
48 |
49 | # Everything is in brass
50 | nec.set_wire_conductivity(brass_conductivity)
51 |
52 | nec.geometry_complete(ground_plane=False) # We added our own 'ground plane'
53 |
54 | nec.voltage_excitation(wire_tag=wire_tag, segment_nr=1, voltage=1.0)
55 |
56 | return nec
57 |
58 | def simulate_and_get_impedance(nec):
59 | nec.set_frequency(design_freq_mhz)
60 |
61 | nec.xq_card(0)
62 |
63 | index = 0
64 | return nec.get_input_parameters(index).get_impedance()
65 |
66 | # TODO: perhaps the length <= 0 can be added through additional constraints to minimize?
67 |
68 | design_freq_mhz = 2595
69 | lte_high_band = [2500, 2690]
70 | system_impedance = 50
71 |
72 | sampled_monopole_lenths = []
73 | sampled_ground_wire_lenths = []
74 | sampled_results = []
75 |
76 | def create_optimization_target(freq_mhz, nr_segments):
77 | def target(args):
78 | monopole_length, ground_wire_length = args[0], args[1]
79 | if monopole_length <= 0 or ground_wire_length <= 0:
80 | return float('inf')
81 |
82 | result = 0
83 | # VSWR should be < 2 in the lte_high_band
84 | # So let's just sum (for now, TODO) the surplus for all the VSWRs that exceed it:
85 | low = lte_high_band[0]
86 | high = lte_high_band[1]
87 | count = high-low
88 |
89 | try:
90 | nec = geometry_monopole_ground(design_freq_mhz, monopole_length, ground_wire_length, nr_segments)
91 | nec.set_frequencies_linear(low, high, count=count)
92 |
93 | nec.xq_card(0) # Execute simulation
94 | except:
95 | print("Caught exception")
96 | return float('inf')
97 |
98 | for idx in range(0, count):
99 | ipt = nec.get_input_parameters(idx)
100 | z = ipt.get_impedance()
101 | s = vswr(z, system_impedance)
102 | if s > 2:
103 | result += s - 2
104 |
105 | # And then maximize the gain in the horizontal plane (theta = pi/2 (90))
106 | # TODO: might this be possible in one step?
107 | gains_db = []
108 | for freq in range(low, high+1):
109 | nec = geometry_monopole_ground(design_freq_mhz, monopole_length, ground_wire_length, nr_segments)
110 |
111 | nec.set_frequency(freq)
112 | nec.radiation_pattern(thetas=Range(-90, -90, count=1), phis=Range(90,90,count=1))
113 | rp = nec.context.get_radiation_pattern(0)
114 |
115 | gains_db.append(rp.get_gain()[0][0])
116 |
117 | #print gains_db
118 | result -= np.exp(max(gains_db)) # maximize gain, hence the minus
119 |
120 | global sampled_monopole_lenths, sampled_ground_wire_lenths, sampled_results
121 | sampled_monopole_lenths.append(monopole_length)
122 | sampled_ground_wire_lenths.append(ground_wire_length)
123 | sampled_results.append(result)
124 |
125 | print(result)
126 |
127 | return result
128 | return target
129 |
130 | if (__name__ == '__main__'):
131 |
132 | wavelength = 299792e3/(design_freq_mhz*1000000)
133 |
134 | initial_length = wavelength / 4 # quarter-wavelength monopole
135 |
136 | print("Wavelength is %0.4fm, initial length is %0.4fm" % (wavelength, initial_length))
137 |
138 | nr_segments = 15 # int(math.ceil(50*initial_length/wavelength))
139 | #print nr_segments
140 |
141 | ground_wire_length = 0.02
142 | z = simulate_and_get_impedance(geometry_monopole_ground(design_freq_mhz, initial_length, ground_wire_length, nr_segments))
143 |
144 | print("Initial impedance: (%6.1f,%+6.1fI) Ohms" % (z.real, z.imag))
145 | print("VSWR @ 50 Ohm is %6.6f" % vswr(z, 50))
146 |
147 | target = create_optimization_target(design_freq_mhz, nr_segments)
148 | optimized_result = scipy.optimize.minimize(target, np.array([initial_length, ground_wire_length]), method='Nelder-Mead')
149 |
150 | optimized_length, optimized_ground_wire_length = optimized_result.x[0], optimized_result.x[1]
151 |
152 | geo_opt = geometry_monopole_ground(design_freq_mhz, optimized_length, optimized_ground_wire_length, nr_segments)
153 | z = simulate_and_get_impedance(geo_opt)
154 |
155 | print("Optimized length %6.6f m and ground screen radials of length %6.6f m, which gives an impedance of: (%6.4f,%+6.4fI) Ohms" % (optimized_length, optimized_ground_wire_length, z.real, z.imag))
156 | print("Mismatch @ 50 Ohm is %6.6f" % mismatch(z, 50))
157 | print("VSWR @ 50 Ohm is %6.6f" % vswr(z, 50))
158 |
159 | geo_opt = geometry_monopole_ground(design_freq_mhz, optimized_length, optimized_ground_wire_length, nr_segments)
160 | geo_opt.set_frequency(design_freq_mhz)
161 | geo_opt.radiation_pattern(thetas=Range(-90, 90, count=180), phis=Range(0,0,count=1))
162 |
163 | #get the radiation_pattern
164 | rp = geo_opt.context.get_radiation_pattern(0)
165 |
166 | # Gains are in decibels
167 | gains_db = rp.get_gain()[:,0] # Is an array of theta,phi -> gain. In this case we only have one phi
168 | thetas = rp.get_theta_angles() * 3.1415 / 180.0
169 | phis = rp.get_phi_angles() * 3.1415 / 180.0
170 |
171 | max_idx = gains_db.argmax()
172 | max_gain = gains_db[max_idx]
173 | max_theta = thetas[max_idx]
174 | #print gains_db
175 | print("Maximal gain is %2.2f dBi, at an angle of %2.2f" % (max_gain, max_theta * 180.0 / np.pi))
176 |
177 | # Plot stuff
178 |
179 | ax = plt.subplot(111, polar=True)
180 |
181 | ax.plot(thetas, gains_db, color='r', linewidth=3)
182 | ax.set_xticks(np.pi/180. * np.linspace(180, -180, 8, endpoint=False))
183 | ax.set_theta_zero_location("N")
184 | ax.set_rlim((-24.0, 9.0)) # TODO: automate. TODO: 4nec2 cheats and makes the lowest points (-999) the same as the lowest non-999 point :)
185 | ax.set_rticks(np.linspace(-24, 9, 10, endpoint=False))
186 | ax.grid(True)
187 |
188 | ax.set_title("Gain pattern in the vertical plane", va='bottom')
189 | plt.show()
190 |
191 |
192 | low = lte_high_band[0]
193 | high = lte_high_band[1]
194 | count = high-low
195 |
196 | # Reset the geometry, so that there is no spurious FR card left. (TODO this should really be not necessary)
197 | geo_opt = geometry_monopole_ground(design_freq_mhz, optimized_length, optimized_ground_wire_length, nr_segments)
198 | geo_opt.set_frequencies_linear(low, high, count=count)
199 | geo_opt.xq_card(0) # Execute simulation
200 |
201 | freqs = []
202 | vswrs = []
203 | for idx in range(0, count):
204 | ipt = geo_opt.get_input_parameters(idx)
205 | z = ipt.get_impedance()
206 |
207 | freqs.append(ipt.get_frequency() / 1000000)
208 | vswrs.append(vswr(z, system_impedance))
209 |
210 | ax = plt.subplot(111)
211 | ax.plot(freqs, vswrs)
212 | ax.set_xlabel("Frequency (MHz)")
213 | ax.set_ylabel("VSWR")
214 | ax.grid(True)
215 | plt.show()
216 |
217 | ax = plt.subplot(111)
218 | #print sampled_monopole_lenths
219 | #print sampled_ground_wire_lenths
220 | sampled_results = np.log(4 + np.array(sampled_results))
221 | #print sampled_results
222 | norm = mpl.colors.Normalize(vmin=min(sampled_results), vmax=max(sampled_results))
223 | plt.scatter(sampled_monopole_lenths, sampled_ground_wire_lenths, c=sampled_results, cmap=mpl.cm.cool, norm=norm, edgecolors='None', alpha=0.75)
224 | ax.set_title("Optimization path")
225 | plt.colorbar()
226 | plt.show()
227 |
228 |
--------------------------------------------------------------------------------
/PyNEC/example/optimized.py:
--------------------------------------------------------------------------------
1 | #
2 | # Automatically tune antenna
3 | #
4 | import argparse
5 | import scipy.optimize
6 | import numpy as np
7 |
8 | import monopole
9 | from antenna_util import reflection_coefficient
10 |
11 | # A function that will be minimized when the impedance is 50 Ohms
12 | # We convert the height and antenna length to positive
13 | # numbers using exp. because otherwise the antenna will lie
14 | # below ground and cause an error in simulation.
15 | def target(x):
16 | global freq, target_impedance
17 | base_height = np.exp(x[0]) # Make it positive
18 | length = np.exp(x[1]) # Make it positive
19 | if (length > 10.0):
20 | return 100
21 | try:
22 | z = monopole.impedance(freq, base_height, length)
23 | return reflection_coefficient(z, z0=target_impedance)
24 | except RuntimeError as re:
25 | return 100
26 |
27 | def print_result(x, f, accepted):
28 | log_base, log_length = x
29 | base_height = np.exp(log_base)
30 | length = np.exp(log_length)
31 |
32 | if accepted:
33 | print("Optimium base_height=%fm, h=%fm, impedance=%s Ohms" % \
34 | (base_height, length, monopole.impedance(freq, base_height, length)))
35 | else:
36 | print("Local_minimum=%fm, h=%fm, impedance=%s Ohms" % \
37 | (base_height, length, monopole.impedance(freq, base_height, length)))
38 |
39 | if __name__=="__main__":
40 | parser = argparse.ArgumentParser(description='Optimize a monopole antenna.')
41 | parser.add_argument('--target-impedance', type=float, default=50.0, help='Target for the optimized impedance')
42 | parser.add_argument('--basinhopping', action="store_true", help='Use basinhopping')
43 | args = parser.parse_args()
44 |
45 | # Starting value
46 | freq = 134.5
47 | x0 = [-2.0, 1.0]
48 | target_impedance = args.target_impedance
49 |
50 | # Carry out the minimization
51 |
52 | if args.basinhopping:
53 | result = scipy.optimize.basinhopping(target, x0, disp=True, T=1.0, niter_success=10)
54 | else:
55 | result = scipy.optimize.minimize(target, x0, method='Nelder-Mead')
56 |
57 | print("")
58 | print("***********************************************************************")
59 | print("* OPTIMIZATION COMPLETED *")
60 | print("***********************************************************************")
61 | print_result(result.x, None, True)
62 |
--------------------------------------------------------------------------------
/PyNEC/example/radiation_pattern.py:
--------------------------------------------------------------------------------
1 | #
2 | # Simple vertical monopole antenna simulation using python-necpp
3 | # pip install necpp
4 | #
5 | from PyNEC import *
6 |
7 | import math
8 |
9 | def geometry(freq, base, length):
10 | conductivity = 1.45e6 # Stainless steel
11 | ground_conductivity = 0.002
12 | ground_dielectric = 10
13 |
14 | wavelength = 3e8/(1e6*freq)
15 | n_seg = int(math.ceil(50*length/wavelength))
16 |
17 | #nec = context_clean(nec_context())
18 | nec = nec_context()
19 |
20 | geo = nec.get_geometry()
21 | geo.wire(1, n_seg, 0, 0, base, 0, 0, base+length, 0.002, 1.0, 1.0)
22 | nec.geometry_complete(1)
23 |
24 | nec.ld_card(5, 0, 0, 0, conductivity, 0.0, 0.0)
25 | nec.gn_card(0, 0, ground_dielectric, ground_conductivity, 0, 0, 0, 0)
26 | nec.fr_card(0, 1, freq, 0)
27 |
28 | # Voltage excitation one third of the way along the wire
29 | nec.ex_card(0, 0, int(n_seg/3), 0, 1.0, 0, 0, 0, 0, 0)
30 |
31 | return nec
32 |
33 | nec = geometry(freq=123.4, base=0.5, length=4.0)
34 | nec.rp_card(calc_mode=0, n_theta=30, n_phi=30, output_format=0, normalization=0, D=0, A=0, theta0=0, delta_theta=10, phi0=0, delta_phi=5, radial_distance=0, gain_norm=0)
35 | nec.xq_card(0) # Execute simulation
36 |
37 | ipt = nec.get_input_parameters(0)
38 | z = ipt.get_impedance()
39 | print(("Impedance is {}".format(z)))
40 |
41 | rpt = nec.get_radiation_pattern(0)
42 |
43 | complex_e_field = rpt.get_e_theta()
44 | e = complex_e_field.reshape((30,30))
45 |
46 | print((complex_e_field.size))
47 |
48 | for t in range(30):
49 | for p in range(30):
50 | pass
51 | print(e[t, p])
52 |
53 | print(dir(rpt))
54 |
--------------------------------------------------------------------------------
/PyNEC/example/test_ai.py:
--------------------------------------------------------------------------------
1 | #instructions to compile :
2 | #
3 | #swig -c++ -python PyNEC.i
4 | #g++ -c nec_context.cpp PyNEC_wrap.cxx -I/usr/local/include/python2.4 -I/usr/local/lib/python2.4/config -DHAVE_CONFIG_H
5 | #g++ -shared -lstdc++ nec_context.o nec_output.o c_plot_card.o c_geometry.o misc.o nec_exception.o nec_ground.o c_ggrid.o matrix_algebra.o nec_radiation_pattern.o nec_structure_currents.o c_evlcom.o PyNEC_wrap.o -o _PyNEC.so
6 |
7 | #example2.nec (modified in order to get several excitations) :
8 | #
9 | #CMEXAMPLE 2. CENTER FED LINEAR ANTENNA.
10 | #CM CURRENT SLOPE DISCONTINUITY SOURCE.
11 | #CM 1. THIN PERFECTLY CONDUCTING WIRE
12 | #CE 2. THIN ALUMINUM WIRE
13 | #GW 0 8 0. 0. -.25 0. 0. .25 .00001
14 | #GE
15 | #FR 0 3 0 0 200. 50.
16 | #EX 5 0 5 1 1. 0. 50.
17 | #EX 5 0 4 1 1. 0. 50.
18 | #XQ
19 | #EN
20 |
21 | from PyNEC import *
22 |
23 | #creation of a nec context
24 | context=nec_context()
25 |
26 | #get the associated geometry
27 | geo = context.get_geometry()
28 |
29 | #add a wire to the geometry
30 | geo.wire(0, 8, 0, 0, -.25, 0, 0, .25, .00001, 1, 1)
31 |
32 | #end of the geometry input
33 | context.geometry_complete(0)
34 |
35 | #add a "fr" card to specify the frequency
36 | context.fr_card(0, 3, 200.0, 50)
37 |
38 | #add a "ex" card to specify an excitation
39 | context.ex_card(5, 0, 5, 0, 0, 1, 0, 0, 0, 0, 0)
40 |
41 | #add an other "ex" card to specify a second excitation
42 | context.ex_card(5, 0, 4, 0, 0, 1, 0, 0, 0, 0, 0)
43 |
44 | #add a "xq" card to force the simulation execution
45 | context.xq_card(0)
46 |
47 | #get the first antenna_input (there are several ones, each one corresponding to one single frequency)
48 | ai=context.get_input_parameters(0)
49 |
--------------------------------------------------------------------------------
/PyNEC/example/test_charge_densities.py:
--------------------------------------------------------------------------------
1 | #instructions to compile :
2 | #
3 | #swig -c++ -python PyNEC.i
4 | #g++ -c nec_context.cpp PyNEC_wrap.cxx -I/usr/local/include/python2.4 -I/usr/local/lib/python2.4/config -DHAVE_CONFIG_H
5 | #g++ -shared -lstdc++ nec_context.o nec_output.o c_plot_card.o c_geometry.o misc.o nec_exception.o nec_ground.o c_ggrid.o matrix_algebra.o nec_radiation_pattern.o nec_structure_currents.o c_evlcom.o PyNEC_wrap.o -o _PyNEC.so
6 |
7 | #dipole_anim.nec
8 | #
9 | #CM Simple dipole, with calculation of currents, charges and near field.
10 | #CE
11 | #GW 1 21 0 -0.25 0.0 0 0.25 0.0 0.001
12 | #GE
13 | #EX 0 1 11 00 1 0
14 | #PQ 0, 0
15 | #NE 0, 1,20,20, 0,0.05,0.05, 0,0.05,0.05
16 | #NH 0, 1,20,20, 0,0.05,0.05, 0,0.05,0.05
17 | #RP 0, 19, 36, 1000, 0, 0, 10, 10
18 | #EN
19 |
20 | from PyNEC import *
21 |
22 | #creation of a nec context
23 | context=nec_context()
24 |
25 | #get the associated geometry
26 | geo = context.get_geometry()
27 |
28 | #add a wire to the geometry
29 | geo.wire(1, 21, 0, -0.25, 0.0, 0, 0.25, 0.0, 0.001, 1, 1)
30 |
31 | #end of the geometry input
32 | context.geometry_complete(0)
33 |
34 | #add a "ex" card to specify an excitation
35 | context.ex_card(0, 1, 11, 0, 0, 1, 0, 0, 0, 0, 0)
36 |
37 | #add a "pq" card to ask for the charge densities to be computed
38 | context.pq_card(0, 0, 0, 0)
39 |
40 | #add a "ne" card to ask for the "near electric field pattern" to be computed
41 | context.ne_card(0, 1, 20, 20, 0, 0.05, 0.05, 0, 0.05, 0.05)
42 |
43 | #add a "nh" card to ask for the "near magnetic field pattern" to be computed
44 | context.nh_card(0, 1, 20, 20, 0, 0.05, 0.05, 0, 0.05, 0.05)
45 |
46 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
47 | context.rp_card(0, 19, 36, 1, 0, 0, 0, 0, 0, 10, 10, 0, 0)
48 |
49 | #get the currents
50 | sc = context.get_structure_currents(0)
51 |
--------------------------------------------------------------------------------
/PyNEC/example/test_ne_nh.py:
--------------------------------------------------------------------------------
1 | #instructions to compile :
2 | #
3 | #swig -c++ -python PyNEC.i
4 | #g++ -c nec_context.cpp PyNEC_wrap.cxx -I/usr/local/include/python2.4 -I/usr/local/lib/python2.4/config -DHAVE_CONFIG_H
5 | #g++ -shared -lstdc++ nec_context.o nec_output.o c_plot_card.o c_geometry.o misc.o nec_exception.o nec_ground.o c_ggrid.o matrix_algebra.o nec_radiation_pattern.o nec_structure_currents.o c_evlcom.o PyNEC_wrap.o -o _PyNEC.so
6 |
7 | #dipole_anim.nec
8 | #
9 | #CM Simple dipole, with calculation of currents, charges and near field.
10 | #CE
11 | #GW 1 21 0 -0.25 0.0 0 0.25 0.0 0.001
12 | #GE
13 | #EX 0 1 11 00 1 0
14 | #PQ 0, 0
15 | #NE 0, 1,20,20, 0,0.05,0.05, 0,0.05,0.05
16 | #NH 0, 1,20,20, 0,0.05,0.05, 0,0.05,0.05
17 | #RP 0, 19, 36, 1000, 0, 0, 10, 10
18 | #EN
19 |
20 | from PyNEC import *
21 |
22 | #creation of a nec context
23 | context=nec_context()
24 |
25 | #get the associated geometry
26 | geo = context.get_geometry()
27 |
28 | #add a wire to the geometry
29 | geo.wire(1, 21, 0, -0.25, 0.0, 0, 0.25, 0.0, 0.001, 1, 1)
30 |
31 | #end of the geometry input
32 | context.geometry_complete(0)
33 |
34 | #add a "ex" card to specify an excitation
35 | context.ex_card(0, 1, 11, 0, 0, 1, 0, 0, 0, 0, 0)
36 |
37 | #add a "pq" card to ask for the charge densities to be computed
38 | context.pq_card(0, 0, 0, 0)
39 |
40 | #add a "ne" card to ask for the "near electric field pattern" to be computed
41 | context.ne_card(0, 1, 20, 20, 0, 0.05, 0.05, 0, 0.05, 0.05)
42 |
43 | #add a "nh" card to ask for the "near magnetic field pattern" to be computed
44 | context.nh_card(0, 1, 20, 20, 0, 0.05, 0.05, 0, 0.05, 0.05)
45 |
46 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
47 | context.rp_card(0, 19, 36, 1, 0, 0, 0, 0, 0, 10, 10, 0, 0)
48 |
49 | #get the currents
50 | sc = context.get_structure_currents(0)
51 |
52 | #get the near electric and magnetic field :
53 | ne = context.get_near_field_pattern(0)
54 | nh = context.get_near_field_pattern(1)
55 |
56 | print(ne)
--------------------------------------------------------------------------------
/PyNEC/example/test_nrp.py:
--------------------------------------------------------------------------------
1 | #instructions to compile :
2 | #
3 | #swig -c++ -python PyNEC.i
4 | #g++ -c nec_context.cpp PyNEC_wrap.cxx -I/usr/local/include/python2.4 -I/usr/local/lib/python2.4/config -DHAVE_CONFIG_H
5 | #g++ -shared -lstdc++ nec_context.o nec_output.o c_plot_card.o c_geometry.o misc.o nec_exception.o nec_ground.o c_ggrid.o matrix_algebra.o nec_radiation_pattern.o nec_structure_currents.o c_evlcom.o PyNEC_wrap.o -o _PyNEC.so
6 |
7 | #example3.nec (modified)
8 | #
9 | #CMEXAMPLE 3. VERTICAL HALF WAVELENGTH ANTENNA OVER GROUND
10 | #CM EXTENDED THIN WIRE KERNEL USED
11 | #CM 1. PERFECT GROUND
12 | #CM 2. IMPERFECT GROUND INCLUDING GROUND WAVE AND RECEIVING
13 | #CE PATTERN CALCULATIONS
14 | #GW 0 9 0. 0. 2. 0. 0. 7. .3
15 | #GE 1
16 | #EK
17 | #FR 0 1 0 0 30.
18 | #EX 0 0 5 0 1.
19 | #GN 1
20 | #RP 0 10 2 1301 0. 0. 10. 90.
21 | #GN 0 0 0 0 6. 1.000E-03
22 | #RP 0 10 2 1301 0. 0. 10. 90.
23 | #RP 1 10 1 0 1. 0. 2. 0. 1.000E+05
24 | #EX 1 10 1 0 0. 0. 0. 10.
25 | #PT 2 0 5 5
26 | #XQ
27 | #EN
28 |
29 | from PyNEC import *
30 |
31 | #creation of a nec context
32 | context=nec_context()
33 |
34 | #get the associated geometry
35 | geo = context.get_geometry()
36 |
37 | #add a wire to the geometry
38 | geo.wire(0, 9, 0, 0, 2, 0, 0, 7, .3, 1, 1)
39 |
40 | #end of the geometry input
41 | context.geometry_complete(0)
42 |
43 | #add a "ek" card to initiate use of the extended thin-wire kernal
44 | context.ek_card(1)
45 |
46 | #add a "fr" card to specify the frequency
47 | context.fr_card(0, 1, 30.0, 0)
48 |
49 | #add a "gn" card to specify the ground parameters
50 | context.gn_card(0, 0, 6., 0.001, 0, 0, 0, 0)
51 |
52 | #add a "ex" card to specify an excitation
53 | context.ex_card(1, 10, 3, 0, 0, 0, 0, 0, 10, 20, 0)
54 |
55 | #add a "pt" card to control the printing of currents on wire segments
56 | context.pt_card(2, 0, 5, 5)
57 |
58 | #add a "xq" card to force the simulation execution
59 | context.xq_card(0)
60 |
61 | #get the norm_rx_pattern
62 | nrp = context.get_norm_rx_pattern(0)
63 |
--------------------------------------------------------------------------------
/PyNEC/example/test_rp.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | from PyNEC import *
4 | import numpy as np
5 |
6 | #creation of a nec context
7 | context=nec_context()
8 |
9 | #get the associated geometry
10 | geo = context.get_geometry()
11 |
12 | #add wires to the geometry
13 | geo.wire(0, 36, 0, 0, 0, -0.042, 0.008, 0.017, 0.001, 1.0, 1.0)
14 | context.geometry_complete(0)
15 |
16 | context.gn_card(-1, 0, 0, 0, 0, 0, 0, 0)
17 |
18 | #add a "ex" card to specify an excitation
19 | context.ex_card(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
20 |
21 | #add a "fr" card to specify the frequency
22 | context.fr_card(0, 2, 2400, 100.0e6)
23 |
24 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
25 | context.rp_card(0, 91, 1, 0, 5, 0, 0, 0.0, 45.0, 4.0, 2.0, 1.0, 0.0)
26 |
27 | #get the radiation_pattern
28 | rp = context.get_radiation_pattern(0)
29 |
30 | # Gains are in decibels
31 | gains_db = rp.get_gain()
32 | gains = 10.0**(gains_db / 10.0)
33 | thetas = rp.get_theta_angles() * 3.1415 / 180.0
34 | phis = rp.get_phi_angles() * 3.1415 / 180.0
35 |
36 |
37 | # Plot stuff
38 | import matplotlib.pyplot as plt
39 |
40 | ax = plt.subplot(111, polar=True)
41 | ax.plot(thetas, gains[:,0], color='r', linewidth=3)
42 | ax.grid(True)
43 |
44 | ax.set_title("Gain at an elevation of 45 degrees", va='bottom')
45 | plt.savefig('RadiationPattern.png')
46 | plt.show()
47 |
48 |
--------------------------------------------------------------------------------
/PyNEC/example/test_rp2.py:
--------------------------------------------------------------------------------
1 | from PyNEC import *
2 |
3 | #creation of a nec context
4 | context=nec_context()
5 |
6 | #get the associated geometry
7 | geo = context.get_geometry()
8 |
9 | #add wires to the geometry
10 | geo.wire(0, 36, -0.0001, -0.0001, -0.0001, -0.0002, -0.0002, -0.0001, 0.001, 1.0, 1.0)
11 |
12 | #end of the geometry input
13 | context.geometry_complete(0)
14 |
15 | #add a "gn" card to specify the ground parameters
16 | context.gn_card(2, 0, 100., 50., 25., 10., 0.7, 0.6)
17 |
18 | #add a "ld" card for "loading"
19 | context.ld_card(5, 0, 0, 0, 3.72e7, 0.0, 0.0)
20 |
21 | #add a "pt" card to ask for Control for Current on Wires to be printed
22 | context.pt_card(-1, 0, 0, 0)
23 |
24 | #add a "ex" card to specify an excitation
25 | context.ex_card(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
26 |
27 | #add a "fr" card to specify the frequency
28 | context.fr_card(0, 2, 2400.0, 100.0e6)
29 |
30 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
31 | context.rp_card(2, 3, 2, 0, 5, 0, 0, 90.0, 90.0, 10.0, 10.0, 0.0, 0.0)
32 |
33 | #get the radiation_pattern
34 | rp = context.get_radiation_pattern(0)
35 |
36 | #get the associated ground
37 | gr = rp.get_ground()
38 |
--------------------------------------------------------------------------------
/PyNEC/example/test_se.py:
--------------------------------------------------------------------------------
1 | #instructions to compile :
2 | #
3 | #swig -c++ -python PyNEC.i
4 | #g++ -c nec_context.cpp PyNEC_wrap.cxx -I/usr/local/include/python2.4 -I/usr/local/lib/python2.4/config -DHAVE_CONFIG_H
5 | #g++ -shared -lstdc++ nec_context.o nec_output.o c_plot_card.o c_geometry.o misc.o nec_exception.o nec_ground.o c_ggrid.o matrix_algebra.o nec_radiation_pattern.o nec_structure_currents.o c_evlcom.o PyNEC_wrap.o -o _PyNEC.so
6 |
7 | #example5.nec (modified because there was a little bug in the last TL card...)
8 | #
9 | #CM 12 ELEMENT LOG PERIODIC ANTENNA IN FREE SPACE
10 | #CM 78 SEGMENTS. SIGMA=O/L RECEIVING AND TRANS. PATTERNS.
11 | #CM DIPOLE LENGTH TO DIAMETER RATIO=150.
12 | #CE TAU=0.93. SIGMA=0.70. BOOM IMPEDANCE=50. OHMS.
13 | #GW 1 5 0.0000 -1.0000 0.0000000 0.00000 1.0000 0.000 .00667
14 | #GW 2 5 -.7527 -1.0753 0. -.7527 1.0753 0. .00717
15 | #GW 3 5 -1.562 -1.1562 0. -1.562 1.1562 0. .00771
16 | #GW 4 5 -2.4323 -1.2432 0. -2.4323 1.2432 0. .00829
17 | #GW 5 5 -3.368 -1.3368 0. -3.368 1.3368 0. .00891
18 | #GW 6 7 -4.3742 -1.4374 0. -4.3742 1.4374 0. .00958
19 | #GW 7 7 -5.4562 -1.5456 0. -5.4562 1.5456 0. .0103
20 | #GW 8 7 -6.6195 -1.6619 0. -6.6195 1.6619 0. .01108
21 | #GW 9 7 -7.8705 -1.787 0. -7.8705 1.787 0. .01191
22 | #GW 10 7 -9.2156 -1.9215 0. -9.2156 1.9215 0. .01281
23 | #GW 11 9 -10.6619 -2.0662 0. -10.6619 2.0662 0. .01377
24 | #GW 12 9 -12.2171 -2.2217 0. -12.2171 2.2217 0. .01481
25 | #GE
26 | #FR 0 0 0 0 46.29 0.
27 | #TL 1 3 2 3 -50.
28 | #TL 2 3 3 3 -50.
29 | #TL 3 3 4 3 -50.
30 | #TL 4 3 5 3 -50.
31 | #TL 5 3 6 4 -50.
32 | #TL 6 4 7 4 -50.
33 | #TL 7 4 8 4 -50.
34 | #TL 8 4 9 4 -50.
35 | #TL 9 4 10 4 -50.
36 | #TL 10 4 11 5 -50.
37 | #TL 11 5 12 5 -50. 0. 0. 0. .02
38 | #EX 0 1 3 10 1 0
39 | #RP 0 37 1 1110 90. 0. -5. 0.
40 | #EN
41 |
42 | from PyNEC import *
43 |
44 | #creation of a nec_context
45 | context=nec_context()
46 |
47 | #get the associated geometry
48 | geo = context.get_geometry()
49 |
50 | #add wires to the geometry
51 | geo.wire(1, 5, 0, -1, 0, 0, 1, 0, .00667, 1, 1)
52 | geo.wire(2, 5, -.7527, -1.0753,0, -.7527, 1.0753, 0, .00717, 1, 1)
53 | geo.wire(3, 5, -1.562, -1.1562, 0, -1.562, 1.1562, 0, .00771, 1, 1)
54 | geo.wire(4, 5, -2.4323, -1.2432, 0, -2.4323, 1.2432, 0,.00829, 1, 1)
55 | geo.wire(5, 5, -3.368, -1.3368, 0, -3.368, 1.3368, 0, .00891, 1, 1)
56 | geo.wire(6, 7, -4.3742, -1.4374, 0, -4.3742, 1.4374, 0, .00958, 1, 1)
57 | geo.wire(7, 7, -5.4562, -1.5456, 0, -5.4562, 1.5456, 0, .0103, 1, 1)
58 | geo.wire(8, 7, -6.6195, -1.6619, 0, -6.6195, 1.6619, 0, .01108, 1, 1)
59 | geo.wire(9, 7, -7.8705, -1.787, 0, -7.8705, 1.787, 0, .01191, 1, 1)
60 | geo.wire(10, 7, -9.2156, -1.9215, 0, -9.2156, 1.9215, 0, .01281, 1, 1)
61 | geo.wire(11, 9, -10.6619, -2.0662, 0, -10.6619, 2.0662, 0, .01377, 1, 1)
62 | geo.wire(12, 9, -12.2171, -2.2217, 0, -12.2171, 2.2217, 0,.01481, 1, 1)
63 |
64 | #end of the geometry input
65 | context.geometry_complete(0)
66 |
67 | #add a "fr" card to specify the frequency
68 | context.fr_card(0, 0, 46.29e6, 0)
69 |
70 | #add "tl" cards to generate transmission lines
71 | context.tl_card(1, 3, 2, 3, -50, 0.7527, 0, 0, 0, 0)
72 | context.tl_card(2, 3, 3, 3, -50, 0.8093, 0, 0, 0, 0)
73 | context.tl_card(3, 3, 4, 3, -50, 0.8703, 0, 0, 0, 0)
74 | context.tl_card(4, 3, 5, 3, -50, 0.9357, 0, 0, 0, 0)
75 | context.tl_card(5 ,3 ,6, 4, -50, 1.0062, 0, 0, 0, 0)
76 | context.tl_card(6, 4, 7, 4, -50, 1.082, 0, 0, 0, 0)
77 | context.tl_card(7, 4, 8, 4, -50, 1.1633, 0, 0, 0, 0)
78 | context.tl_card(8, 4, 9, 4, -50, 1.251, 0, 0, 0, 0)
79 | context.tl_card(9, 4, 10, 4, -50, 1.3451, 0, 0, 0, 0)
80 | context.tl_card(10, 4 ,11 , 5, -50, 1.4463, 0, 0, 0, 0)
81 | context.tl_card(11, 5, 12, 5, -50, 1.5552, 0, 0, 0, 0.02)
82 |
83 | #add a "ex" card to specify an excitation
84 | context.ex_card(0, 1, 3, 1, 0, 1, 0, 0, 0, 0, 0)
85 |
86 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
87 | context.rp_card(0, 37, 1, 1, 1, 1, 0, 90, 0, -5, 0, 0, 0)
88 |
89 | #get structure excitation
90 | se = context.get_structure_excitation(0)
91 |
--------------------------------------------------------------------------------
/PyNEC/example/test_structure_currents.py:
--------------------------------------------------------------------------------
1 | #instructions to compile :
2 | #
3 | #swig -c++ -python PyNEC.i
4 | #g++ -c nec_context.cpp PyNEC_wrap.cxx -I/usr/local/include/python2.4 -I/usr/local/lib/python2.4/config -DHAVE_CONFIG_H
5 | #g++ -shared -lstdc++ nec_context.o nec_output.o c_plot_card.o c_geometry.o misc.o nec_exception.o nec_ground.o c_ggrid.o matrix_algebra.o nec_radiation_pattern.o nec_structure_currents.o c_evlcom.o PyNEC_wrap.o -o _PyNEC.so
6 |
7 | #example3.nec
8 | #
9 | #CMEXAMPLE 3. VERTICAL HALF WAVELENGTH ANTENNA OVER GROUND
10 | #CM EXTENDED THIN WIRE KERNEL USED
11 | #CM 1. PERFECT GROUND
12 | #CM 2. IMPERFECT GROUND INCLUDING GROUND WAVE AND RECEIVING
13 | #CE PATTERN CALCULATIONS
14 | #GW 0 9 0. 0. 2. 0. 0. 7. .3
15 | #GE 1
16 | #EK
17 | #PT 0 0 3 4
18 | #LD 0 0 0 0 1000 1 1
19 | #FR 0 1 0 0 30.
20 | #EX 0 0 5 0 1.
21 | #GN 1
22 | #RP 0 10 2 1301 0. 0. 10. 90.
23 | #GN 0 0 0 0 6. 1.000E-03
24 | #RP 0 10 2 1301 0. 0. 10. 90.
25 | #RP 1 10 1 0 1. 0. 2. 0. 1.000E+05
26 | #EX 2 10 1 0 0. 0. 0.1 10. 0. 0.6
27 | #PT 2 0 5 5
28 | #XQ
29 | #EN
30 |
31 | from PyNEC import *
32 |
33 | #creation of a nec context
34 | context=nec_context()
35 |
36 | #get the associated geometry
37 | geo = context.get_geometry()
38 |
39 | #add wires to the geometry
40 | geo.wire(0, 9, 0, 0, 2, 0, 0, 7, .3, 1, 1)
41 |
42 | #end of the geometry input
43 | context.geometry_complete(0)
44 |
45 | #add a "ek" card to initiate use of the extended thin-wire kernal
46 | context.ek_card(1)
47 |
48 | #add a "pt" card to control the printing of currents on wire segments
49 | context.pt_card(0, 0, 3, 4)
50 |
51 | #add a "ld" card for "loading"
52 | context.ld_card(0, 0, 0, 0, 1000, 1, 1)
53 |
54 | #add a "fr" card to specify the frequency
55 | context.fr_card(0, 1, 30e6, 0)
56 |
57 | #add a "ex" card to specify an excitation
58 | context.ex_card(0, 0, 5, 0, 0, 1, 0, 0, 0, 0, 0)
59 |
60 | #add a "gn" card to specify the ground parameters
61 | context.gn_card(1, 0, 0, 0, 0, 0, 0, 0)
62 |
63 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
64 | context.rp_card(0, 10, 2, 1, 3, 0, 1, 0, 0, 10.0, 90.0, 0.0, 0.0)
65 |
66 | #add a "gn" card to specify the ground parameters
67 | context.gn_card(0, 0, 6, 1.000e-3, 0, 0, 0, 0)
68 |
69 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
70 | context.rp_card(0, 10, 2, 1, 3, 0, 1, 0, 0, 10.0, 90.0, 0.0, 0.0)
71 |
72 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
73 | context.rp_card(1, 10, 1, 0, 0, 0, 0, 1, 0, 2, 0, 1.000e5, 0.0)
74 |
75 | #add a "ex" card to specify an excitation
76 | context.ex_card(2, 10, 1, 0, 0, 0, 0, 0.1, 10, 0, 0.6)
77 |
78 | #add a "pt" card to control the printing of currents on wire segments
79 | context.pt_card(2, 0, 5, 5)
80 |
81 | #get the currents
82 | sc = context.get_structure_currents(0)
83 |
--------------------------------------------------------------------------------
/PyNEC/example/test_surface_patch_currents.py:
--------------------------------------------------------------------------------
1 | #instructions to compile :
2 | #
3 | #swig -c++ -python PyNEC.i
4 | #g++ -c nec_context.cpp PyNEC_wrap.cxx -I/usr/local/include/python2.4 -I/usr/local/lib/python2.4/config -DHAVE_CONFIG_H
5 | #g++ -shared -lstdc++ nec_context.o nec_output.o c_plot_card.o c_geometry.o misc.o nec_exception.o nec_ground.o c_ggrid.o matrix_algebra.o nec_radiation_pattern.o nec_structure_currents.o c_evlcom.o PyNEC_wrap.o -o _PyNEC.so
6 |
7 | #example6.nec
8 | #
9 | #CECYLINDER WITH ATTACHED WIRES
10 | #SP 0 0 10 0 7.3333 0. 0. 38.4
11 | #SP 0 0 10 0 0. 0. 0. 38.4
12 | #SP 0 0 10 0 -7.3333 0. 0. 38.4
13 | #GM 0 1 0. 0. 30.
14 | #SP 0 0 6.89 0. 11. 90. 0. 44.88
15 | #SP 0 0 6.89 0. -11. -90. 0. 44.88
16 | #GR 0 6
17 | #SP 0 0 0. 0. 11. 90. 0. 44.89
18 | #SP 0 0 0. 0. -11. -90. 0. 44.89
19 | #GW 1 4 0. 0. 11. 0. 0. 23. .1
20 | #GW 2 5 10. 0. 0. 27.6 0. 0. .2
21 | #GS 0 0 .01
22 | #GE
23 | #FR 0 1 0 0 465.84
24 | #CP 1 1 2 1
25 | #EX 0 1 1 0 1.
26 | #RP 0 73 1 1000 0. 0. 5. 0.
27 | #EX 0 2 1 0 1.
28 | #XQ
29 | #EN
30 |
31 | from PyNEC import *
32 |
33 | #creation of a nec context
34 | context=nec_context()
35 |
36 | #get the associated geometry
37 | geo = context.get_geometry()
38 |
39 | #add a patch to the geometry
40 | geo.arbitrary_shaped_patch(10, 0, 7.3333, 0., 0., 38.4)
41 |
42 | #add a patch to the geometry
43 | geo.arbitrary_shaped_patch(10, 0, 0, 0., 0., 38.4)
44 |
45 | #add a patch to the geometry
46 | geo.arbitrary_shaped_patch(10, 0, -7.3333, 0., 0., 38.4)
47 |
48 | #move the structure (here the structure is copied, its copy is rotated by 30 degrees about Z-axis)
49 | geo.move(0, 0, 30, 0, 0, 0, 0, 1, 0)
50 |
51 | #add a patch to the geometry
52 | geo.arbitrary_shaped_patch(6.89, 0., 11., 90., 0., 44.88)
53 |
54 | #add a patch to the geometry
55 | geo.arbitrary_shaped_patch(6.89, 0., -11., -90., 0., 44.88)
56 |
57 | #ask for a cylindrical structure to be generated from the existing structure
58 | geo.generate_cylindrical_structure(0, 6)
59 |
60 | #add a patch to the geometry
61 | geo.arbitrary_shaped_patch(0, 0, 11, 90, 0, 44.89)
62 |
63 | #add a patch to the geometry
64 | geo.arbitrary_shaped_patch(0, 0, -11, -90, 0, 44.89)
65 |
66 | #add a wire to the geometry
67 | geo.wire(1, 4, 0, 0, 11, 0, 0, 23, .1, 1, 1)
68 |
69 | #add a wire to the geometry
70 | geo.wire(2, 5, 10, 0, 0, 27.6, 0, 0, .2, 1, 1)
71 |
72 | #scale all the structure dimensions by a constant
73 | geo.scale(0.01)
74 |
75 | #end of the geometry input
76 | context.geometry_complete(0)
77 |
78 | #add a "fr" card to specify the frequency
79 | context.fr_card(0, 1, 465.84e6, 0)
80 |
81 | #add a "ex" card to specify an excitation
82 | context.ex_card(0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0)
83 |
84 | #add a "rp" card to specify radiation pattern sampling parameters and to cause program execution
85 | context.rp_card(0, 73, 1, 1, 0, 0, 0, 0, 0, 5, 0, 0, 0)
86 |
87 | #add a "ex" card to specify an excitation
88 | context.ex_card(0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0)
89 |
90 | #add a "xq" card to force the simulation execution
91 | context.xq_card(0)
92 |
93 | #get the currents
94 | sc = context.get_structure_currents(0)
95 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/c_geometry.i:
--------------------------------------------------------------------------------
1 | class c_geometry
2 | {
3 | public:
4 |
5 | /*! Add a wire to the geometry,
6 |
7 | All coordinates are in meters.
8 |
9 | \param tag_id The tag ID.
10 | \param segment_count The number of segments.
11 |
12 | \param xw1 The x coordinate of the wire starting point.
13 | \param yw1 The y coordinate of the wire starting point.
14 | \param zw1 The z coordinate of the wire starting point.
15 |
16 | \param xw2 The x coordinate of the wire ending point.
17 | \param yw2 The y coordinate of the wire ending point.
18 | \param zw2 The z coordinate of the wire ending point.
19 |
20 | \param rad The wire radius (meters)
21 | \param rdel For tapered wires, the. Otherwise set to 1.0
22 | \param rrad For tapered wires, the. Otherwise set to 1.0
23 | */
24 | void wire( int tag_id, int segment_count,
25 | nec_float xw1, nec_float yw1, nec_float zw1,
26 | nec_float xw2, nec_float yw2, nec_float zw2,
27 | nec_float rad,
28 | nec_float rdel, nec_float rrad);
29 |
30 |
31 |
32 | /*! Add an arc to the geometry,
33 |
34 | All coordinates are in meters and angles are in degrees.
35 |
36 | \param tag_id The tag ID.
37 | \param segment_count The number of segments.
38 |
39 | \param rada The radius.
40 | \param ang1 The angle of the arc starting point.
41 | \param ang2 The angle of the arc end point.
42 | \param rad The wire radius.
43 | */
44 | void arc( int tag_id, int segment_count, nec_float rada,
45 | nec_float ang1, nec_float ang2, nec_float rad );
46 |
47 |
48 |
49 | /*! Add an helix to the geometry,
50 |
51 | \remark The helix is a versatile m_geometry->element. For example, to generate a spiral printed circuit antenna, use a helix of zero height.
52 |
53 | All coordinates are in meters.
54 |
55 | \param tag_id The tag ID.
56 | \param segment_count The number of segments.
57 | \param s The turn spacing.
58 | \param h1 The total length of the helix (negative for a left-handed helix).
59 |
60 | \param a1 x-start radius.
61 | \param b1 y-start radius.
62 |
63 | \param a2 x-end radius.
64 | \param b2 y-end radius.
65 |
66 | \param rad The wire radius.
67 | */
68 | void helix( nec_float s, nec_float hl, nec_float a1, nec_float b1,
69 | nec_float a2, nec_float b2, nec_float rad, int segment_count, int tag_id );
70 |
71 |
72 |
73 | /*! Scale all dimensions of a structure by a constant.
74 |
75 | \param xw1 All structure dimensions, including wire radius, are multiplied by xw1.
76 | */
77 | void scale( nec_float xw1);
78 |
79 | /*! Reflects partial structure along x,y, or z axes.
80 |
81 | \param ix If ix = 1 then the structure is reflected along X axis.
82 | \param iy If iy = 1 then the structure is reflected along Y axis.
83 | \param iz If iz = 1 then the structure is reflected along Z axis.
84 | \param itx The tag number increment.
85 | */
86 | void reflect(int ix, int iy, int iz, int itx);
87 |
88 |
89 |
90 | /*! Rotates structure along Z-axis to complete a symmetric structure.
91 |
92 | \param itx The tag number increment.
93 | \param nop The total number of times that the structure is to occur in the cylindrical array.
94 | */
95 | void generate_cylindrical_structure(int itx, int nop);
96 |
97 | void sp_card( int ns,
98 | nec_float in_x1, nec_float in_y1, nec_float in_z1,
99 | nec_float in_x2, nec_float in_y2, nec_float in_z2);
100 |
101 | void gx_card(int card_int_1, int card_int_2);
102 |
103 | %extend{
104 |
105 | /*! Move the structure with respect to its coordinate system or reproduces structure in new positions,
106 |
107 | All coordinates are in meters and angles are in degrees.
108 |
109 | \param rox_deg The angle in degrees through which the structure is rotated about the X-axis. A positive angle causes a right-hand rotation.
110 | \param roy_deg The angle of rotation about Y-axis.
111 | \param roz_deg The angle of rotation about Z-axis.
112 |
113 | \param xs The x component of vector by which the structure is translated with respect to the coordinate system.
114 | \param ys The y component of vector by which the structure is translated.
115 | \param zs The z component of vector by which the structure is translated.
116 |
117 | \param its The tag number of the segments that will be moved. If its = 0 then the entire structure is moved.
118 | \param nrpt The number of new Structures to be generated.
119 | \param itgi The tag number increment.
120 | */
121 | void move( nec_float rox_deg, nec_float roy_deg, nec_float roz_deg, nec_float xs,
122 | nec_float ys, nec_float zs, int its, int nrpt, int itgi )
123 | {
124 | nec_float rox_rad = degrees_to_rad(rox_deg);
125 | nec_float roy_rad = degrees_to_rad(roy_deg);
126 | nec_float roz_rad = degrees_to_rad(roz_deg);
127 |
128 | return self->move( rox_rad, roy_rad, roz_rad, xs, ys, zs, its, nrpt, itgi );
129 | }
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | /*! Add a arbitrary-shaped patch to the geometry
138 |
139 | All coordinates are in meters, angles are in degrees.
140 |
141 | \param ax1 The x-coordinate of patch center.
142 | \param ay1 The y-coordinate of patch center.
143 | \param az1 The z-coordinate of patch center.
144 |
145 | \param ax2_deg The elevation angle above the X-Y plane of outward normal vector.
146 | \param ay2_deg The azimuth angle from X-axis of outward normal vector.
147 |
148 | \param az2 The patch area if ny=1.
149 | */
150 | void arbitrary_shaped_patch( nec_float ax1, nec_float ay1, nec_float az1,
151 | nec_float ax2_deg, nec_float ay2_deg, nec_float az2 )
152 | {
153 | nec_float ax2_rad = degrees_to_rad(ax2_deg);
154 | nec_float ay2_rad = degrees_to_rad(ay2_deg);
155 |
156 | return self->patch( 0, 1, ax1, ay1, az1, ax2_rad, ay2_rad, az2, 0, 0, 0, 0, 0, 0 );
157 | }
158 |
159 |
160 |
161 | /*! Add a rectangular patch to the geometry
162 |
163 | All coordinates are in meters.
164 |
165 | \param ax1 The x-coordinate of corner 1.
166 | \param ay1 The y-coordinate of corner 1.
167 | \param az1 The z-coordinate of corner 1.
168 |
169 | \param ax2 The x_coordinate of corner 2.
170 | \param ay2 The y_coordinate of corner 2.
171 | \param az2 The z-coordinate of corner 2.
172 |
173 | \param ax3 The x_coordinate of corner 3.
174 | \param ay3 The y_coordinate of corner 3.
175 | \param az3 The z_coordinate of corner 3.
176 | */
177 | void rectangular_patch( nec_float ax1, nec_float ay1, nec_float az1,
178 | nec_float ax2, nec_float ay2, nec_float az2,
179 | nec_float ax3, nec_float ay3, nec_float az3 )
180 | {
181 | return self->patch( 0, 2, ax1, ay1, az1, ax2, ay2, az2, ax3, ay3, az3, 0, 0, 0 );
182 | }
183 |
184 |
185 |
186 | /*! Add a triangular patch to the geometry
187 |
188 | All coordinates are in meters.
189 |
190 | \param ax1 The x-coordinate of corner 1.
191 | \param ay1 The y-coordinate of corner 1.
192 | \param az1 The z-coordinate of corner 1.
193 |
194 | \param ax2 The x_coordinate of corner 2.
195 | \param ay2 The y_coordinate of corner 2.
196 | \param az2 The z-coordinate of corner 2.
197 |
198 | \param ax3 The x_coordinate of corner 3.
199 | \param ay3 The y_coordinate of corner 3.
200 | \param az3 The z_coordinate of corner 3.
201 | */
202 | void triangular_patch( nec_float ax1, nec_float ay1, nec_float az1,
203 | nec_float ax2, nec_float ay2, nec_float az2,
204 | nec_float ax3, nec_float ay3, nec_float az3 )
205 | {
206 | return self->patch( 0, 3, ax1, ay1, az1, ax2, ay2, az2, ax3, ay3, az3, 0, 0, 0 );
207 | }
208 |
209 |
210 |
211 | /*! Add a quadrilateral patch to the geometry
212 |
213 | All coordinates are in meters.
214 |
215 | \param ax1 The x-coordinate of corner 1.
216 | \param ay1 The y-coordinate of corner 1.
217 | \param az1 The z-coordinate of corner 1.
218 |
219 | \param ax2 The x_coordinate of corner 2.
220 | \param ay2 The y_coordinate of corner 2.
221 | \param az2 The z-coordinate of corner 2.
222 |
223 | \param ax3 The x_coordinate of corner 3.
224 | \param ay3 The y_coordinate of corner 3.
225 | \param az3 The z_coordinate of corner 3.
226 |
227 | \param ax4 The x_coordinate of corner 4.
228 | \param ay4 The x_coordinate of corner 4.
229 | \param az4 The x_coordinate of corner 4.
230 | */
231 | void quadrilateral_patch( nec_float ax1, nec_float ay1, nec_float az1,
232 | nec_float ax2, nec_float ay2, nec_float az2,
233 | nec_float ax3, nec_float ay3, nec_float az3,
234 | nec_float ax4, nec_float ay4, nec_float az4 )
235 | {
236 | return self->patch( 0, 4, ax1, ay1, az1, ax2, ay2, az2, ax3, ay3, az3, ax4, ay4, az4 );
237 | }
238 |
239 |
240 |
241 | /*! Add a multiple patch to the geometry.
242 |
243 | All coordinates are in meters.
244 |
245 | \param nx The rectangular surface is divided into nx patches from corner 1 to corner 2.
246 | \param ny The rectangular surface is divided into ny patches from corner 2 to corner 3.
247 | \param ax1 The x-coordinate of corner 1.
248 | \param ay1 The y-coordinate of corner 1.
249 | \param az1 The z-coordinate of corner 1.
250 |
251 | \param ax2 The x_coordinate of corner 2.
252 | \param ay2 The y_coordinate of corner 2.
253 | \param az2 The z-coordinate of corner 2.
254 |
255 | \param ax3 The x_coordinate of corner 3.
256 | \param ay3 The y_coordinate of corner 3.
257 | \param az3 The z_coordinate of corner 3.
258 | */
259 | void multiple_patch( int nx, int ny,
260 | nec_float ax1, nec_float ay1, nec_float az1,
261 | nec_float ax2, nec_float ay2, nec_float az2,
262 | nec_float ax3, nec_float ay3, nec_float az3 )
263 | {
264 | return self->patch( nx, ny, ax1, ay1, az1, ax2, ay2, az2, ax3, ay3, az3, 0, 0, 0 );
265 | }
266 | }
267 |
268 | };
269 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/math_util.i:
--------------------------------------------------------------------------------
1 | /* these typedefs can be moved to an other interface file. They have been left
2 | here to respect the structure of the original code. */
3 |
4 | typedef double nec_float;
5 | typedef std::complex nec_complex;
6 |
7 | typedef safe_array int_array;
8 | typedef safe_array real_array;
9 | typedef safe_array complex_array;
10 |
11 | typedef safe_matrix real_matrix;
12 | typedef safe_matrix complex_matrix;
13 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/nec_antenna_input.i:
--------------------------------------------------------------------------------
1 | class nec_antenna_input
2 | {
3 | public:
4 |
5 | /*! Returns the frequency in Herz. */
6 | nec_float get_frequency();
7 |
8 |
9 | /*! Returns the array of segment tag numbers. */
10 | vector get_tag();
11 |
12 |
13 | /*! Returns the array of segment numbers. */
14 | vector get_segment();
15 |
16 |
17 | /*! Returns the array of complex currents in Ampere. */
18 | vector get_current();
19 |
20 |
21 | /*! Returns the array of complex voltages in Volt. */
22 | vector get_voltage();
23 |
24 |
25 | /*! Returns the array of power in Watt. */
26 | vector get_power();
27 |
28 | vector get_impedance();
29 | };
30 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/nec_ground.i:
--------------------------------------------------------------------------------
1 | %nodefault;
2 | class nec_ground
3 | {
4 | public:
5 | /*! Returns the relative dielectric constant (no units) of the ground medium 1. */
6 | nec_float get_relative_dielectric_constant();
7 |
8 |
9 | /*! Returns the conductivity in Siemens/meter of the ground medium 1. */
10 | nec_float get_conductivity();
11 |
12 |
13 | /*! Returns the number of radial wires in the ground screen approximation. If it's zero then this approximation has not been used.*/
14 | int get_radial_wire_count();
15 |
16 |
17 | /*! Returns the length of radial wires used in the ground screen approximation - provided this approximation has been used. */
18 | nec_float get_radial_wire_length();
19 |
20 |
21 | /*! Returns the radius of radial wires in the ground screen approximation - provided this approximation has been used. */
22 | nec_float get_radial_wire_radius();
23 |
24 |
25 | /*! If there's a cliff problem, returns the distance from the origin of the coordinate system to join between medium 1 and 2.
26 | This distance is either the radius of the circle where the two media join or the distance from the X axis to where
27 | the two media join in a line parallel to the Y axis. Specification of the circular or linear option is on the RP card.
28 | */
29 | nec_float get_cliff_edge_distance();
30 |
31 |
32 | /*! If there's a cliff problem, returns the distance (positive or zero) by which the surface of medium 2 is below medium 1. */
33 | nec_float get_cliff_height();
34 |
35 |
36 | /*! If there's a cliff problem, returns the relative dielectric constant (no units) of the ground medium 2. */
37 | nec_float get_relative_dielectric_constant2();
38 |
39 |
40 | /*! If there's a cliff problem, returns the conductivity in Siemens/meter of the ground medium 2. */
41 | nec_float get_conductivity2();
42 | };
43 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/nec_near_field_pattern.i:
--------------------------------------------------------------------------------
1 | class nec_near_field_pattern
2 | {
3 | public:
4 |
5 | /*! Returns the frequency in Herz. */
6 | nec_float get_frequency();
7 |
8 |
9 | /*! Returns the flag indicating whether the result is a near electric or magnetic field pattern. */
10 | int get_nfeh();
11 |
12 |
13 | /*! Returns the array of x-coordinate in meters of field points. */
14 | vector get_x();
15 |
16 |
17 | /*! Returns the array of y-coordinate in meters of field points. */
18 | vector get_y();
19 |
20 |
21 | /*! Returns the array of z-coordinate in meters of field points. */
22 | vector get_z();
23 |
24 |
25 | /*! Returns the array of x_components of the electric or magnetic field. */
26 | vector get_field_x();
27 |
28 |
29 | /*! Returns the array of y_components of the electric or magnetic field. */
30 | vector get_field_y();
31 |
32 |
33 | /*! Returns the array of z_components of the electric or magnetic field. */
34 | vector get_field_z();
35 |
36 |
37 |
38 | /*this private method won't be wrapped, but allow an error in the compilation
39 | process to be avoided.*/
40 | private:
41 |
42 | nec_near_field_pattern(int nfeh);
43 |
44 | };
45 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/nec_norm_rx_pattern.i:
--------------------------------------------------------------------------------
1 | %nodefault;
2 | class nec_norm_rx_pattern
3 | {
4 | public:
5 |
6 | /*! Returns the frequency in Herz. */
7 | nec_float get_frequency();
8 |
9 |
10 | /*! Returns the number of theta angles. */
11 | int get_n_theta();
12 |
13 |
14 | /*! Returns the number of phi angles. */
15 | int get_n_phi();
16 |
17 |
18 | /*! Returns the first value of theta in degrees. */
19 | nec_float get_theta_start();
20 |
21 |
22 | /*! Returns the first value of phi angles in degrees. */
23 | nec_float get_phi_start();
24 |
25 |
26 | /*! Returns the increment for theta in degrees. */
27 | nec_float get_delta_theta();
28 |
29 |
30 | /*! Returns the increment for phi in degrees. */
31 | nec_float get_delta_phi();
32 |
33 |
34 | /*! Returns the value of eta in degrees. */
35 | nec_float get_eta();
36 |
37 |
38 | /*! Returns the axial ratio (no units). */
39 | nec_float get_axial_ratio();
40 |
41 |
42 | /*! Returns the segment number. */
43 | int get_segment_number();
44 |
45 |
46 | /*! Return the polarization type. */
47 | string get_type();
48 |
49 |
50 | /*! Returns the normalization factor in dB. */
51 | nec_float get_norm_factor();
52 |
53 |
54 | /*! Returns the array of receiving gains not yet normalized. */
55 | real_array get_mag();
56 |
57 | };
58 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/nec_radiation_pattern.i:
--------------------------------------------------------------------------------
1 | %nodefault;
2 | class nec_radiation_pattern
3 | {
4 | public:
5 |
6 | /*! Returns the frequency in Herz. */
7 | nec_float get_frequency();
8 |
9 |
10 | /*! Returns the associated ground object. */
11 | nec_ground get_ground();
12 |
13 |
14 | /*! Returns the radial distance in meters ( or the rho cylindrical coordinate in meters if the calculation mode chosen is mode 1 ). */
15 | nec_float get_range();
16 |
17 |
18 | /*! Returns the 2-D array of gains in dB used in the normalization process. */
19 | real_matrix get_gain();
20 |
21 |
22 | /*! Returns the array of vertical (or major axis, depending on the output format chosen) gains in dB. */
23 | real_array get_gain_vert();
24 |
25 |
26 | /*! Returns the array of horizontal (or minor axis, depending on the output format chosen) gains in dB*/
27 | real_array get_gain_horiz();
28 |
29 |
30 | /*! Returns the array of total gains in dB*/
31 | real_array get_gain_tot();
32 |
33 |
34 | /*! Returns the array of polarization axial ratios (no units). */
35 | real_array get_pol_axial_ratio();
36 |
37 |
38 | /*! Returns the array of polarization tilts in degrees. */
39 | real_array get_pol_tilt();
40 |
41 |
42 | /*! Returns the array of polarization sense indexes (no units). The relationship between the index and the actual sense is the following :
43 | 0 : linear
44 | 1 : right
45 | 2 : left
46 | */
47 | int_array get_pol_sense_index();
48 |
49 |
50 | /*! Returns the array of complex theta-components of electric field E in Volt/meter. */
51 | complex_array get_e_theta();
52 |
53 |
54 | /*! Returns the array of complex phi-components of electric field E in Volt/meter. */
55 | complex_array get_e_phi();
56 |
57 |
58 | /*! Returns the array of complex radial-components of electric field E in Volt/meter - only available for the calculation mode 1. */
59 | complex_array get_e_r();
60 |
61 |
62 | /*! Returns the normalization factors in dB provided a normalization has been requested. */
63 | nec_float get_normalization_factor();
64 |
65 | /*! Return all the theta angles. */
66 | real_array get_theta_angles() const;
67 |
68 | /*! Returns the increment for theta in degrees (or for z in meters if the calculation mode chosen is mode 1 ). */
69 | nec_float get_delta_theta();
70 |
71 |
72 | /*! Returns the first value of theta in degrees (or of z in meters if the calculation mode chosen is mode 1 ). */
73 | nec_float get_theta_start();
74 |
75 | /*! Return all the phi angles. */
76 | real_array get_phi_angles() const;
77 |
78 | /*! Returns the increment for phi in degrees. */
79 | nec_float get_delta_phi();
80 |
81 |
82 | /*! Returns the first value of phi in degrees. */
83 | nec_float get_phi_start();
84 |
85 |
86 | /*! Returns the number of theta angles. */
87 | int get_ntheta() const;
88 |
89 |
90 | /*! Returns the number of phi angles. */
91 | int get_nphi() const;
92 |
93 |
94 | /*! Returns the array of average power gains in dB, provided its computation has been requested. */
95 | nec_float get_average_power_gain();
96 |
97 |
98 | /*! Returns the solid angle in steradians used in the averaging process, provided the computation of an average gain has been requested. */
99 | nec_float get_average_power_solid_angle();
100 |
101 |
102 | /*! Returns the flag (no units) which indicates the calculation mode chosen. */
103 | int get_ifar();
104 |
105 |
106 | /*! Returns the flag (no units) which indicates the target of the normalization process. */
107 | int get_rp_normalization();
108 |
109 |
110 | /*! Returns the flag (no units) which indicates the output format chosen. */
111 | int get_rp_output_format();
112 |
113 |
114 | /*! Returns the flag (no units) which indicates whether the average gain will be computed or not. */
115 | int get_rp_power_average();
116 |
117 |
118 | /*! Returns the flag (no units) which indicates the type of gain computed : power or directive gain. */
119 | int get_rp_ipd();
120 | };
121 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/nec_structure_currents.i:
--------------------------------------------------------------------------------
1 | class nec_structure_currents
2 | {
3 | public:
4 |
5 | /*! Returns the frequency in Herz. */
6 | nec_float get_frequency();
7 |
8 |
9 | /*! Returns the flag which controls the printing of the currents. */
10 | int get_iptflg();
11 |
12 |
13 | /*! Returns the flag which controls the printing of charge densities. */
14 | int get_iptflq();
15 |
16 |
17 | /*! Returns the number the wire segments in the geometry. */
18 | int get_n();
19 |
20 |
21 | /*! Returns the number of patches in the geometry. */
22 | int get_m();
23 |
24 |
25 | /*! Returns the array of segment numbers for the printing of currrents. */
26 | vector get_current_segment_number();
27 |
28 |
29 | /*! Returns the array of segment tag numbers for the printing of currents, provided the standard output format has been requested. */
30 | vector get_current_segment_tag();
31 |
32 |
33 | /*! Returns the array of x-coordinate of segment centers in meters for the printing of currents, provided the standard output format has been requested. */
34 | vector get_current_segment_center_x();
35 |
36 |
37 | /*! Returns the array of y-coordinate of segment centers in meters for the printing of currents, provided the standard output format has been requested. */
38 | vector get_current_segment_center_y();
39 |
40 |
41 | /*! Returns the array of z-coordinate of segment centers in meters for the printing of currents, provided the standard output format has been requested. */
42 | vector get_current_segment_center_z();
43 |
44 |
45 | /*! Returns the array of segment lengths in meters for the printing of currents, provided the standard output format has been requested. */
46 | vector get_current_segment_length();
47 |
48 |
49 | /*! Returns the array of theta angles in degrees for the printing of currents, provided the format designed for a receiving pattern has been requested. */
50 | vector get_current_theta();
51 |
52 |
53 | /*! Returns the array of phi angles in degrees for the printing of currents, provided the format designed for a receiving pattern has been requested. */
54 | vector get_current_phi();
55 |
56 |
57 | /*! Returns the array of complex currents in Ampere. */
58 | vector get_current();
59 |
60 |
61 | /*! Returns the array of segment numbers for the printing of charge densities. */
62 | vector get_q_density_segment_number();
63 |
64 |
65 | /*! Returns the array of segment tag numbers for the printing of charge densities. */
66 | vector get_q_density_segment_tag();
67 |
68 |
69 | /*! Returns the array of x-coordinate of segment centers in meters for the printing of charge densities. */
70 | vector get_q_density_segment_center_x();
71 |
72 |
73 | /*! Returns the array of y-coordinate of segment centers in meters for the printing of charge densities. */
74 | vector get_q_density_segment_center_y();
75 |
76 |
77 | /*! Returns the array of z-coordinate of segment centers in meters for the printing of charge densities. */
78 | vector get_q_density_segment_center_z();
79 |
80 |
81 | /*! Returns the array of segment lengths in meters for the printing of charge densities. */
82 | vector get_q_density_segment_length();
83 |
84 |
85 | /*! Returns the array of complex charge densities in Coulomb/meter. */
86 | vector get_q_density();
87 |
88 |
89 | /*! Returns the array of patch numbers. */
90 | vector get_patch_number();
91 |
92 |
93 | /*! Returns the array of x-coordinate of patch centers. */
94 | vector get_patch_center_x();
95 |
96 |
97 | /*! Returns the array of y-coordinate of patch centers. */
98 | vector get_patch_center_y();
99 |
100 |
101 | /*! Returns the array of z-coordinate of patch centers. */
102 | vector get_patch_center_z();
103 |
104 |
105 | /*! Returns the array of complex tangent vector 1 of the patches. */
106 | vector get_patch_tangent_vector1();
107 |
108 |
109 | /*! Returns the array of complex tangent vector 2 of the patches. */
110 | vector get_patch_tangent_vector2();
111 |
112 |
113 | /*! Returns the complex x-component of the electric field E. */
114 | vector get_patch_e_x();
115 |
116 |
117 | /*! Returns the complex y-component of the electric field E. */
118 | vector get_patch_e_y();
119 |
120 |
121 | /*! Returns the complex z-component of the electric field E. */
122 | vector get_patch_e_z();
123 |
124 |
125 |
126 | /*this private method won't be wrapped, but allow an error in the compilation
127 | process to be avoided.*/
128 | private:
129 |
130 | nec_structure_currents(nec_context * in_context, char * in_pattype,
131 | int in_nload,
132 | nec_float in_xpr3, nec_float in_xpr6);
133 |
134 | };
135 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/nec_structure_excitation.i:
--------------------------------------------------------------------------------
1 | class nec_structure_excitation
2 | {
3 | public:
4 |
5 | /*! Returns the frequency in Herz */
6 | nec_float get_frequency();
7 |
8 |
9 | /*! Returns the array of segment tag numbers. */
10 | vector get_tag();
11 |
12 |
13 | /*! Returns the array of segment numbers. */
14 | vector get_segment();
15 |
16 |
17 | /*! Returns the array of complex currents in Ampere. */
18 | vector get_current();
19 |
20 |
21 | /*! Returns the array of complex voltages in Volt. */
22 | vector get_voltage();
23 |
24 |
25 | /*! Returns the array of power in Watt. */
26 | vector get_power();
27 | };
28 |
--------------------------------------------------------------------------------
/PyNEC/interface_files/safe_array.i:
--------------------------------------------------------------------------------
1 | template
2 | class safe_array {
3 | public:
4 | T& getItem(int64_t i);
5 | };
6 |
7 | template
8 | class safe_matrix {
9 | public:
10 | T& getItem(int32_t i, int32_t j);
11 | int32_t rows();
12 | int32_t cols();
13 | };
--------------------------------------------------------------------------------
/PyNEC/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools", "wheel", "numpy"]
3 | build-backend = "setuptools.build_meta"
4 |
--------------------------------------------------------------------------------
/PyNEC/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | setup.py file for PyNEC Python module
5 |
6 | Author Tim Molteno. tim@molteno.net
7 | """
8 |
9 | import distutils.sysconfig
10 | from glob import glob
11 | import os
12 | import numpy as np
13 | import setuptools
14 |
15 | # Remove silly flags from the compilation to avoid warnings.
16 | #cfg_vars = distutils.sysconfig.get_config_vars()
17 | #for key, value in cfg_vars.items():
18 | # if type(value) == str:
19 | # cfg_vars[key] = value.replace("-Wstrict-prototypes", "")
20 |
21 | # Generate a list of the sources.
22 | nec_sources = []
23 | nec_sources.extend([fn for fn in glob('necpp_src/src/*.cpp')
24 | if not os.path.basename(fn).endswith('_tb.cpp')
25 | if not os.path.basename(fn).startswith('net_solve.cpp')
26 | if not os.path.basename(fn).startswith('nec2cpp.cpp')
27 | if not os.path.basename(fn).startswith('necDiff.cpp')])
28 | nec_sources.extend(glob("PyNEC_wrap.cxx"))
29 |
30 | nec_headers = []
31 | nec_headers.extend(glob("necpp_src/src/*.h"))
32 | nec_headers.extend(glob("necpp_src/config.h"))
33 |
34 |
35 | # At the moment, the config.h file is needed, and this should be generated from the ./configure
36 | # command in the parent directory. Use ./configure --without-lapack to avoid dependance on LAPACK
37 | #
38 | necpp_module = setuptools.Extension('_PyNEC',
39 | sources=nec_sources,
40 |
41 | include_dirs=[np.get_include(), 'necpp_src/src', 'necpp_src/', 'necpp_src/win32/'],
42 | extra_compile_args = ['-fPIC'],
43 | extra_link_args = ['-lstdc++'],
44 | depends=nec_headers,
45 | define_macros=[('BUILD_PYTHON', '1'), ('NPY_NO_DEPRECATED_API','NPY_1_7_API_VERSION')]
46 | )
47 |
48 | with open("README.md", "r") as fh:
49 | long_description = fh.read()
50 |
51 | setuptools.setup (name = 'PyNEC',
52 | version = '1.7.3.6',
53 | author = "Tim Molteno",
54 | author_email = "tim@physics.otago.ac.nz",
55 | url = "http://github.com/tmolteno/python-necpp",
56 | keywords = "nec2 nec2++ antenna electromagnetism radio",
57 | description = "Python Antenna Simulation Module (nec2++) object-oriented interface",
58 | long_description=long_description,
59 | long_description_content_type="text/markdown",
60 | include_package_data=True,
61 | data_files=[('examples', ['example/test_rp.py'])],
62 | ext_modules = [necpp_module],
63 | requires = ['numpy'],
64 | py_modules = ["PyNEC"],
65 | license='GPLv2',
66 | classifiers=[
67 | "Development Status :: 5 - Production/Stable",
68 | "Topic :: Scientific/Engineering",
69 | "Topic :: Communications :: Ham Radio",
70 | "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
71 | 'Programming Language :: Python :: 3',
72 | "Intended Audience :: Science/Research"]
73 | )
74 |
--------------------------------------------------------------------------------
/PyNEC/tests/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
3 |
--------------------------------------------------------------------------------
/PyNEC/tests/Makefile:
--------------------------------------------------------------------------------
1 | test:
2 | python3 -m unittest discover
3 |
--------------------------------------------------------------------------------
/PyNEC/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tmolteno/python-necpp/8c3cf38b5c3a0e37ee390d55af42af488437e343/PyNEC/tests/__init__.py
--------------------------------------------------------------------------------
/PyNEC/tests/test_examples.py:
--------------------------------------------------------------------------------
1 | import PyNEC
2 |
3 |
4 | import unittest
5 |
6 | class TestDipoleGain(unittest.TestCase):
7 |
8 | def test_example1(self):
9 |
10 | nec= PyNEC.nec_context()
11 |
12 | geo = nec.get_geometry()
13 |
14 |
15 | '''
16 | CE EXAMPLE 1. CENTER FED LINEAR ANTENNA
17 | GW 0 7 0. 0. -.25 0. 0. .25 .001
18 | GE
19 | EX 0 0 4 0 1.
20 | XQ
21 | LD 0 0 4 4 10. 3.000E-09 5.300E-11
22 | PQ
23 | NE 0 1 1 15 .001 0 0 0. 0. .01786
24 | EN
25 | '''
26 | geo.wire(0, 7, 0., 0., .75, 0., 0., 1.25, .001, 1.0, 1.0)
27 | nec.geometry_complete(1)
28 | nec.ex_card(0, 0, 4,0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0)
29 | nec.xq_card(0)
30 | nec.ld_card(0, 0, 4, 4, 10., 3.000E-09, 5.300E-11)
31 | nec.pq_card(0, 0, 0, 0)
32 | nec.ne_card(0, 1, 1, 15, .001, 0, 0, 0., 0., .01786)
33 | nec.xq_card(0)
34 |
35 | ipt = nec.get_input_parameters(0)
36 | z = ipt.get_impedance()
37 |
38 | self.assertAlmostEqual(z[0].real,82.69792906662622)
39 | self.assertAlmostEqual(z[0].imag,46.30603888063429)
40 |
41 |
42 | def test_example2(self):
43 | ''' CMEXAMPLE 2. CENTER FED LINEAR ANTENNA.
44 | CM CURRENT SLOPE DISCONTINUITY SOURCE.
45 | CM 1. THIN PERFECTLY CONDUCTING WIRE
46 | CE 2. THIN ALUMINUM WIRE
47 | GW 0 8 0. 0. -.25 0. 0. .25 .00001
48 | GE
49 | FR 0 3 0 0 200. 50.
50 | EX 5 0 5 1 1. 0. 50.
51 | XQ
52 | LD 5 0 0 0 3.720E+07
53 | FR 0 1 0 0 300.
54 | EX 5 0 5 0 1.
55 | XQ
56 | EN
57 | '''
58 | nec = PyNEC.nec_context()
59 | geo = nec.get_geometry()
60 | geo.wire( 0, 8, 0., 0., -.25, 0., 0., .25, .00001, 1.0, 1.0)
61 | nec.geometry_complete(0)
62 | nec.fr_card(0, 3, 200., 50 )
63 | nec.ex_card(5, 0, 5, 1, 1.0, 0.0, 50.0, 0.0, 0.0, 0.0)
64 | nec.xq_card(0)
65 |
66 | ipt = nec.get_input_parameters(0)
67 | z = ipt.get_impedance()
68 |
69 | '''
70 | ----- ANTENNA INPUT PARAMETERS -----
71 | TAG SEG VOLTAGE (VOLTS) CURRENT (AMPS) IMPEDANCE (OHMS) ADMITTANCE (MHOS) POWER
72 | NO. NO. REAL IMAGINARY REAL IMAGINARY REAL IMAGINARY REAL IMAGINARY (WATTS)
73 | 0 5 1.0000E+00 0.0000E+00 6.6413E-05 1.5794E-03 2.6577E+01 -6.3204E+02 6.6413E-05 1.5794E-03 3.3207E-05
74 | '''
75 | self.assertAlmostEqual(z[0].real/26.5762,1.0,4)
76 | self.assertAlmostEqual(z[0].imag/-632.060,1.0,4)
77 |
78 | nec.ld_card(0, 0, 4, 4, 10., 3.000E-09, 5.300E-11)
79 | nec.fr_card(0, 3, 200., 50 )
80 | nec.ex_card(5, 0, 5, 1, 1.0, 0.0, 50.0, 0.0, 0.0, 0.0)
81 | nec.xq_card(0)
82 |
83 | ipt = nec.get_input_parameters(1)
84 | z = ipt.get_impedance()
85 | '''
86 | ----- ANTENNA INPUT PARAMETERS -----
87 | TAG SEG VOLTAGE (VOLTS) CURRENT (AMPS) IMPEDANCE (OHMS) ADMITTANCE (MHOS) POWER
88 | NO. NO. REAL IMAGINARY REAL IMAGINARY REAL IMAGINARY REAL IMAGINARY (WATTS)
89 | 0 5 1.0000E+00 0.0000E+00 6.1711E-04 3.5649E-03 4.7145E+01 -2.7235E+02 6.1711E-04 3.5649E-03 3.0856E-04
90 | '''
91 | self.assertAlmostEqual(z[0].real/47.1431, 1.0, 4)
92 | self.assertAlmostEqual(z[0].imag/-272.372, 1.0, 3)
93 |
94 |
95 | def test_example3(self):
96 | '''
97 | CMEXAMPLE 3. VERTICAL HALF WAVELENGTH ANTENNA OVER GROUND
98 | CM EXTENDED THIN WIRE KERNEL USED
99 | CM 1. PERFECT GROUND
100 | CM 2. IMPERFECT GROUND INCLUDING GROUND WAVE AND RECEIVING
101 | CE PATTERN CALCULATIONS
102 | GW 0 9 0. 0. 2. 0. 0. 7. .03
103 | GE 1
104 | EK
105 | FR 0 1 0 0 30.
106 | EX 0 0 5 0 1.
107 | GN 1
108 | RP 0 10 2 1301 0. 0. 10. 90.
109 | GN 0 0 0 0 6. 1.000E-03
110 | RP 0 10 2 1301 0. 0. 10. 90.
111 | RP 1 10 1 0 1. 0. 2. 0. 1.000E+05
112 | EX 1 10 1 0 0. 0. 0. 10.
113 | PT 2 0 5 5
114 | XQ
115 | EN
116 | '''
117 | nec = PyNEC.nec_context()
118 | geo = nec.get_geometry()
119 | geo.wire(0, 9, 0., 0.0, 2.0, 0.0, 0.0, 7.0, 0.03, 1.0, 1.0)
120 | nec.geometry_complete(1)
121 | nec.set_extended_thin_wire_kernel(True)
122 | nec.fr_card(0, 1, 30., 0 )
123 | nec.ex_card(0, 0, 5, 0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0)
124 | nec.gn_card(1, 0, 0, 0, 0, 0, 0, 0)
125 | nec.rp_card(0,10,2,1,3,0,1,0.0,0.0,10.0,90.0, 0, 0)
126 |
127 |
128 | ipt = nec.get_input_parameters(0)
129 | z = ipt.get_impedance()
130 |
131 | self.assertAlmostEqual(z[0].real,83.7552291016712)
132 | self.assertAlmostEqual(z[0].imag,45.32205265591289)
133 | #self.assertAlmostEqual(nec_gain_max(nec,0),8.393875976328134)
134 |
135 | nec.gn_card(0, 0, 6.0, 1.000E-03, 0, 0, 0, 0)
136 | nec.rp_card(0,10,2,1,3,0,1, 0.0,0.0,10.0,90.0, 0, 0)
137 |
138 |
139 | ipt = nec.get_input_parameters(1)
140 | z = ipt.get_impedance()
141 |
142 | self.assertAlmostEqual(z[0].real,86.415,3)
143 | self.assertAlmostEqual(z[0].imag,47.822,3)
144 | #self.assertAlmostEqual(nec_gain_max(nec,1),1.44837,3)
145 |
146 | nec.rp_card(1,10,1,0,0,0,0, 1.0,0.0,2.0,0.0, 1.000E+05, 0)
147 | # Not sure what to check here.
148 |
149 | nec.ex_card(1, 10, 1, 0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0)
150 | nec.pt_card(2, 0, 5, 5)
151 | # Not sure what to check here.
152 |
153 |
154 |
155 | def test_example4(self):
156 | '''
157 | CEEXAMPLE 4. T ANTENNA ON A BOX OVER PERFECT GROUND
158 | SP 0 0 .1 .05 .05 0. 0. .01
159 | SP 0 0 .05 .1 .05 0. 90. .01
160 | GX 0 110
161 | SP 0 0 0. 0. .1 90. 0. .04
162 | GW 1 4 0. 0. .1 0. 0. .3 .001
163 | GW 2 2 0. 0. .3 .15 0. .3 .001
164 | GW 3 2 0. 0. .3 -.15 0. .3 .001
165 | GE 1
166 | GN 1
167 | EX 0 1 1 0 1.
168 | RP 0 10 4 1001 0. 0. 10. 30.
169 | EN
170 | '''
171 | nec = PyNEC.nec_context()
172 | geo = nec.get_geometry()
173 | geo.sp_card(0, 0.1, 0.05, 0.05, 0.0, 0.0, 0.01)
174 | geo.sp_card(0, .05, .1, .05, 0.0, 90.0, 0.01)
175 | geo.gx_card(0, 110)
176 | geo.sp_card(0, 0.0, 0.0, 0.1, 90.0, 0.0, 0.04)
177 |
178 | geo.wire(1, 4, 0., 0.0, 0.1, 0.0, 0.0, 0.3, .001, 1.0, 1.0)
179 | geo.wire(2, 2, 0., 0.0, 0.3, 0.15, 0.0, 0.3, .001, 1.0, 1.0)
180 | geo.wire(3, 2, 0., 0.0, 0.3, -.15, 0.0, 0.3, .001, 1.0, 1.0)
181 |
182 | nec.geometry_complete(1)
183 | nec.gn_card(1, 0, 0, 0, 0, 0, 0, 0)
184 |
185 | nec.ex_card(0, 1, 1, 0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0)
186 | nec.rp_card(0,10,4,1,0,0,1,0.0,0.0,10.0,30.0, 0, 0)
187 |
188 | ipt = nec.get_input_parameters(0)
189 | z = ipt.get_impedance()
190 |
191 | #self.assertAlmostEqual(nec_gain_max(nec,0),5.076,3)
192 | self.assertAlmostEqual(z[0].real,180.727,3)
193 | self.assertAlmostEqual(z[0].imag,217.654,3)
194 |
195 |
196 | if __name__ == '__main__':
197 | unittest.main()
--------------------------------------------------------------------------------
/PyNEC/tests/test_get_gain.py:
--------------------------------------------------------------------------------
1 | import PyNEC
2 |
3 |
4 | import unittest
5 |
6 | class TestDipoleGain(unittest.TestCase):
7 |
8 |
9 | def test_example4(self):
10 | '''
11 | CEEXAMPLE 4. T ANTENNA ON A BOX OVER PERFECT GROUND
12 | SP 0 0 .1 .05 .05 0. 0. .01
13 | SP 0 0 .05 .1 .05 0. 90. .01
14 | GX 0 110
15 | SP 0 0 0. 0. .1 90. 0. .04
16 | GW 1 4 0. 0. .1 0. 0. .3 .001
17 | GW 2 2 0. 0. .3 .15 0. .3 .001
18 | GW 3 2 0. 0. .3 -.15 0. .3 .001
19 | GE 1
20 | GN 1
21 | EX 0 1 1 0 1.
22 | RP 0 10 4 1001 0. 0. 10. 30.
23 | EN
24 | '''
25 | nec= PyNEC.nec_context()
26 |
27 | geo = nec.get_geometry()
28 |
29 | geo.sp_card(0, 0.1, 0.05, 0.05, 0.0, 0.0, 0.01)
30 | geo.sp_card(0, .05, .1, .05, 0.0, 90.0, 0.01)
31 | geo.gx_card(0, 110)
32 | geo.sp_card(0, 0.0, 0.0, 0.1, 90.0, 0.0, 0.04)
33 |
34 | geo.wire(1, 4, 0., 0.0, 0.1, 0.0, 0.0, 0.3, .001, 1.0, 1.0)
35 | geo.wire(2, 2, 0., 0.0, 0.3, 0.15, 0.0, 0.3, .001, 1.0, 1.0)
36 | geo.wire(3, 2, 0., 0.0, 0.3, -.15, 0.0, 0.3, .001, 1.0, 1.0)
37 |
38 | nec.geometry_complete(1)
39 | nec.gn_card(1, 0, 0, 0, 0, 0, 0, 0)
40 |
41 | nec.ex_card(0, 1, 1, 0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0)
42 | nec.rp_card(0,10,4,1,0,0,1,0.0,0.0,10.0,30.0, 0, 0)
43 |
44 | self.assertAlmostEqual(nec.get_gain_max(0),5.076,3)
45 |
46 | gmax = -999.0
47 |
48 | for theta_index in range(0,10):
49 | for phi_index in range(0,4):
50 | g = nec.get_gain(0,theta_index, phi_index)
51 | gmax = max(g, gmax)
52 |
53 | self.assertAlmostEqual(gmax, nec.get_gain_max(0), 5 )
54 |
55 |
56 | if __name__ == '__main__':
57 | unittest.main()
58 |
--------------------------------------------------------------------------------
/PyNEC/tests/test_multiple_sc_cards.py:
--------------------------------------------------------------------------------
1 | from necpp import *
2 |
3 |
4 | import unittest
5 |
6 |
7 | class TestSurfacePatches(unittest.TestCase):
8 |
9 |
10 | ''' CM W2IMU 10GHz F=0.55
11 | CE ************************************
12 | SP 0 3 0.019000 -0.001424 0.078830 0.019000 0.001424 0.078830
13 | SC 0 3 0.019000 0.001424 0.076180 0.019000 -0.001424 0.076180
14 | SC 0 3 0.019000 0.001424 0.073530 0.019000 -0.001424 0.073530
15 | SC 0 3 0.019000 0.001424 0.070880 0.019000 -0.001424 0.070880
16 | SC 0 3 0.019000 0.001424 0.068230 0.019000 -0.001424 0.068230
17 | SC 0 3 0.019000 0.001424 0.065580 0.019000 -0.001424 0.065580
18 | SC 0 3 0.019000 0.001424 0.062930 0.019000 -0.001424 0.062930
19 | SC 0 3 0.019000 0.001424 0.060280 0.019000 -0.001424 0.060280
20 | SC 0 3 0.019000 0.001424 0.057630 0.019000 -0.001424 0.057630
21 | SC 0 3 0.019000 0.001424 0.054980 0.019000 -0.001424 0.054980
22 | SC 0 3 0.019000 0.001424 0.052330 0.019000 -0.001424 0.052330
23 | SC 0 3 0.019000 0.001424 0.049680 0.019000 -0.001424 0.049680
24 | SC 0 3 0.019000 0.001424 0.047030 0.019000 -0.001424 0.047030
25 | SC 0 3 0.019000 0.001424 0.044380 0.019000 -0.001424 0.044380
26 | SC 0 3 0.019000 0.001424 0.041730 0.019000 -0.001424 0.041730
27 | SC 0 3 0.019000 0.001424 0.039080 0.019000 -0.001424 0.039080
28 | SC 0 3 0.019000 0.001424 0.036430 0.019000 -0.001424 0.036430
29 | SC 0 3 0.017283 0.001295 0.033856 0.017283 -0.001295 0.033856
30 | SC 0 3 0.015566 0.001167 0.031282 0.015566 -0.001167 0.031282
31 | SC 0 3 0.013849 0.001038 0.028708 0.013849 -0.001038 0.028708
32 | SC 0 3 0.012132 0.000909 0.026134 0.012132 -0.000909 0.026134
33 | SC 0 3 0.010415 0.000780 0.023560 0.010415 -0.000780 0.023560
34 | SC 0 3 0.010415 0.000780 0.020942 0.010415 -0.000780 0.020942
35 | SC 0 3 0.010415 0.000780 0.018324 0.010415 -0.000780 0.018324
36 | SC 0 3 0.010415 0.000780 0.015707 0.010415 -0.000780 0.015707
37 | SC 0 3 0.010415 0.000780 0.013089 0.010415 -0.000780 0.013089
38 | SC 0 3 0.010415 0.000780 0.010471 0.010415 -0.000780 0.010471
39 | SC 0 3 0.010415 0.000780 0.007853 0.010415 -0.000780 0.007853
40 | SC 0 3 0.010415 0.000780 0.005236 0.010415 -0.000780 0.005236
41 | SC 0 3 0.010415 0.000780 0.002618 0.010415 -0.000780 0.002618
42 | SC 0 3 0.010415 0.000780 0.000000 0.010415 -0.000780 0.000000
43 | SC 0 3 0.007811 0.000585 0.000000 0.007811 -0.000585 0.000000
44 | SC 0 3 0.005208 0.000390 0.000000 0.005208 -0.000390 0.000000
45 | SC 0 3 0.002604 0.000195 0.000000 0.002604 -0.000195 0.000000
46 | SC 0 3 0.000026 0.000002 0.000000 0.000026 -0.000002 0.000000
47 | GM 0 41 0.000000 0.000000 8.571429
48 | GW 1 15 0.000000 0.005829 0.004961 0.000000 -0.005829 0.004961 0.000248
49 | GM 0 0 0.000000 0.000000 0.000000 0.000000 0.000000 -0.078830
50 | GE
51 | FR 0 1 0 0 10450.000000
52 | EX 0 1 8 0 1.000000 0.000000
53 | LD 5 0 0 0 3.720000E+07
54 | PT -1
55 | RP 0 361 3 1500 0.000000 0.000000 1.000000 45.000000
56 | EN
57 | '''
58 | def test_patch(self):
59 | nec = nec_create()
60 |
61 | self.handle_nec(nec_sp_card(nec, 3, 0.019000, -0.001424, 0.078830, 0.019000, 0.001424, 0.078830))
62 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.076180, 0.019000, -0.001424, 0.076180))
63 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.073530, 0.019000, -0.001424, 0.073530))
64 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.070880, 0.019000, -0.001424, 0.070880))
65 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.068230, 0.019000, -0.001424, 0.068230))
66 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.065580, 0.019000, -0.001424, 0.065580))
67 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.062930, 0.019000, -0.001424, 0.062930))
68 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.060280, 0.019000, -0.001424, 0.060280))
69 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.057630, 0.019000, -0.001424, 0.057630))
70 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.054980, 0.019000, -0.001424, 0.054980))
71 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.052330, 0.019000, -0.001424, 0.052330))
72 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.049680, 0.019000, -0.001424, 0.049680))
73 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.047030, 0.019000, -0.001424, 0.047030))
74 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.044380, 0.019000, -0.001424, 0.044380))
75 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.041730, 0.019000, -0.001424, 0.041730))
76 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.039080, 0.019000, -0.001424, 0.039080))
77 | self.handle_nec(nec_sc_card(nec, 3, 0.019000, 0.001424, 0.036430, 0.019000, -0.001424, 0.036430))
78 | self.handle_nec(nec_sc_card(nec, 3, 0.017283, 0.001295, 0.033856, 0.017283, -0.001295, 0.033856))
79 | self.handle_nec(nec_sc_card(nec, 3, 0.015566, 0.001167, 0.031282, 0.015566, -0.001167, 0.031282))
80 | self.handle_nec(nec_sc_card(nec, 3, 0.013849, 0.001038, 0.028708, 0.013849, -0.001038, 0.028708))
81 | self.handle_nec(nec_sc_card(nec, 3, 0.012132, 0.000909, 0.026134, 0.012132, -0.000909, 0.026134))
82 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.023560, 0.010415, -0.000780, 0.023560))
83 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.020942, 0.010415, -0.000780, 0.020942))
84 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.018324, 0.010415, -0.000780, 0.018324))
85 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.015707, 0.010415, -0.000780, 0.015707))
86 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.013089, 0.010415, -0.000780, 0.013089))
87 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.010471, 0.010415, -0.000780, 0.010471))
88 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.007853, 0.010415, -0.000780, 0.007853))
89 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.005236, 0.010415, -0.000780, 0.005236))
90 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.002618, 0.010415, -0.000780, 0.002618))
91 | self.handle_nec(nec_sc_card(nec, 3, 0.010415, 0.000780, 0.000000, 0.010415, -0.000780, 0.000000))
92 | self.handle_nec(nec_sc_card(nec, 3, 0.007811, 0.000585, 0.000000, 0.007811, -0.000585, 0.000000))
93 | self.handle_nec(nec_sc_card(nec, 3, 0.005208, 0.000390, 0.000000, 0.005208, -0.000390, 0.000000))
94 | self.handle_nec(nec_sc_card(nec, 3, 0.002604, 0.000195, 0.000000, 0.002604, -0.000195, 0.000000))
95 | self.handle_nec(nec_sc_card(nec, 3, 0.000026, 0.000002, 0.000000, 0.000026, -0.000002, 0.000000))
96 |
97 | self.handle_nec(nec_gm_card(nec, 0,41,0.000000,0.000000,8.571429, 0,0,0,0))
98 | self.handle_nec(nec_wire(nec, 1,15,0.000000,0.005829,0.004961,0.000000,-0.005829,0.004961,0.000248,1,1))
99 | self.handle_nec(nec_gm_card(nec, 0,0, 0.000000,0.000000,0.000000, 0.000000,0.000000,-0.078830, 0))
100 | self.handle_nec(nec_geometry_complete(nec, 0)) # GE
101 | self.handle_nec(nec_fr_card(nec, 0,1, 10450.000000, 0))
102 | self.handle_nec(nec_ex_card(nec,0,1,8,0,1.000000,0.000000, 0, 0, 0, 0))
103 | self.handle_nec(nec_ld_card(nec,5,0,0,0,3.720000E+07, 0, 0))
104 | self.handle_nec(nec_pt_card(nec,-1, 0, 0, 0))
105 | self.handle_nec(nec_rp_card(nec, 0,361,3,1,5,0,0,0.000000,0.000000,1.000000,45.000000, 0, 0))
106 |
107 | '''
108 | ----- ANTENNA INPUT PARAMETERS -----
109 | TAG SEG VOLTAGE (VOLTS) CURRENT (AMPS) IMPEDANCE (OHMS) ADMITTANCE (MHOS) POWER
110 | NO. NO. REAL IMAGINARY REAL IMAGINARY REAL IMAGINARY REAL IMAGINARY (WATTS)
111 | 1 8 1.0000E+00 0.0000E+00 9.2145E-03 6.7375E-04 1.0795E+02 -7.8930E+00 9.2145E-03 6.7375E-04 4.6072E-03
112 | '''
113 | self.assertAlmostEqual(nec_impedance_real(nec,0),1.0795E+02,3)
114 | self.assertAlmostEqual(nec_impedance_imag(nec,0),-7.8930E+00,3)
115 | self.assertAlmostEqual(nec_gain_max(nec, 0), 10.3332, 4)
116 |
117 | self.handle_nec(nec_delete(nec))
118 |
119 |
120 | if __name__ == '__main__':
121 | unittest.main()
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # python-necpp: Antenna simulation in python
2 |
3 | This repository contains two wrappers for the [http://github.com/tmolteno/necpp nec2++] antenna simulation package:
4 |
5 | * necpp/ contains a wrapper using SWIG of the C interface (Python module name: necpp).
6 | * PyNEC/ contains a wrapper of the C++ interfaces (Python module name: PyNEC). The example/ directory furthermore contains some nicer, more readable Python wrappers that make toying around with NEC a less painful experience.
7 |
8 | Both are based on Tim Molteno (tim@physics.otago.ac.nz)'s code with major cleanup by Bart Coppens.
9 |
10 | ## TODOs
11 |
12 | The cleaner API should really be **ported to C++**, so the clean wrappers get automatically generated, and C++ can use the same cleaner interface. But for now, I'm happy with the Python wrapper :)
13 |
--------------------------------------------------------------------------------
/build_wheels.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e -x
3 |
4 | # Install a system package required by our library
5 | # yum install -y atlas-devel
6 |
7 | # Compile wheels
8 | for PYBIN in /opt/python/*/bin; do
9 | "${PYBIN}/pip" install -r /io/dev-requirements.txt
10 | "${PYBIN}/pip" wheel -e /io/PyNEC/ -w wheelhouse/
11 | done
12 |
13 | # Bundle external shared libraries into the wheels
14 | for whl in wheelhouse/*.whl; do
15 | auditwheel repair "$whl" --plat $PLAT -w /io/wheelhouse/
16 | done
17 |
18 | # Install packages and test
19 | for PYBIN in /opt/python/*/bin/; do
20 | "${PYBIN}/pip" install PyNEC --no-index -f /io/wheelhouse
21 | (cd "$HOME"; "${PYBIN}/nosetests" pymanylinuxdemo)
22 | done
23 |
--------------------------------------------------------------------------------
/dev-requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 |
--------------------------------------------------------------------------------
/necpp/.gitignore:
--------------------------------------------------------------------------------
1 | necpp_wrap.c
2 | necpp_src
3 | necpp.py
4 |
--------------------------------------------------------------------------------
/necpp/INSTALL.md:
--------------------------------------------------------------------------------
1 | # python-necpp
2 | PyPI module for nec2++
3 |
4 | This module allows you to do antenna simulations in Python using the nec2++ antenna
5 | simulation package. This is a wrapper using SWIG of the C interface, so the syntax
6 | is quite simple. Have a look at the file test.py, for an example of how this
7 | library can be used. Other examples are in the 'examples' directory.
8 |
9 | ### Author
10 |
11 | Tim Molteno. tim@physics.otago.ac.nz
12 |
13 | ## Instructions
14 |
15 | To use this python module, you must have the necpp library installed on your system. This can
16 | be installed in the main part of the necpp code distribution.
17 |
18 | ### NEC2++ source distribution
19 |
20 | This is included as a git submodule
21 |
22 | git clone https://github.com/tmolteno/python-necpp.git
23 | git submodule init
24 | git submodule update --remote
25 |
26 | To update the submodule to the latest necpp
27 |
28 | git submodule update --remote
29 |
30 | ### Converting from MarkDown
31 |
32 | sudo aptitude install pandoc swig
33 |
34 | pandoc --to=rst README.md > README.txt
35 |
36 | ### Testing
37 |
38 | Then you can do the usual
39 |
40 | ./build.sh
41 |
42 | This will run SWIG a source distribution tarball
43 |
44 | ### Uploading to PyPI.
45 |
46 | http://peterdowns.com/posts/first-time-with-pypi.html
47 |
48 | python setup.py sdist upload -r pypitest
49 |
--------------------------------------------------------------------------------
/necpp/LICENCE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/necpp/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENCE.txt
2 |
3 | include necpp_src/src/*.h
4 | include necpp_src/config.h
5 |
6 | include necpp_src/example/test.py
7 | include example/*.py
8 |
--------------------------------------------------------------------------------
/necpp/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | sh build.sh
3 | python3 setup.py sdist
4 | #python3 setup.py bdist_wheel
5 |
6 | clean:
7 | rm -rf necpp_src
8 | rm -rf build
9 | rm -rf dist
10 |
11 | test-upload:
12 | python3 -m pip install --user --upgrade twine
13 |
14 | python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
15 |
16 | upload:
17 | python3 -m twine upload dist/*
18 |
19 | test-install:
20 | python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps necpp --upgrade
21 |
--------------------------------------------------------------------------------
/necpp/README.md:
--------------------------------------------------------------------------------
1 | # python-necpp: Antenna simulation in python
2 |
3 | This module allows you to do antenna simulations in Python using the nec2++ antenna
4 | simulation package.
5 |
6 | This is a wrapper using SWIG of the C interface, so the syntax
7 | is quite simple. Have a look at the file necpp_src/example/test.py, for an example of how this
8 | library can be used.
9 |
10 | Tim Molteno. tim@physics.otago.ac.nz
11 |
12 | ## NEWS
13 |
14 | * Version 1.7.3 Includes Python3 support. Also some bug fixes and updating nec++ to the
15 | latest version.
16 | * Version 1.7.0.3 includes nec_medium_parameters(). You could simulate an antenna in seawater!
17 | * Version 1.7.0 includes support for getting elements of radiation patterns. At the moment
18 | this is just through the function nec_get_gain().
19 |
20 |
21 | ## Install
22 |
23 | As of version 1.6.1.2 swig is no longer required for installation. Simply use PIP as
24 | follows:
25 |
26 | pip install necpp
27 |
28 | ## Documentation
29 |
30 | Try help(necpp) to list the available functions. The functions available are documented in the C-style API of nec2++.
31 | This is [available here](http://tmolteno.github.io/necpp/libnecpp_8h.html)
32 |
33 | ## Using
34 |
35 | The following code calculates the impedance of a simple vertical monopole antenna
36 | over a perfect ground.
37 |
38 | import necpp
39 |
40 | def handle_nec(result):
41 | if (result != 0):
42 | print necpp.nec_error_message()
43 |
44 | def impedance(frequency, z0, height):
45 |
46 | nec = necpp.nec_create()
47 | handle_nec(necpp.nec_wire(nec, 1, 17, 0, 0, z0, 0, 0, z0+height, 0.1, 1, 1))
48 | handle_nec(necpp.nec_geometry_complete(nec, 1, 0))
49 | handle_nec(necpp.nec_gn_card(nec, 1, 0, 0, 0, 0, 0, 0, 0))
50 | handle_nec(necpp.nec_fr_card(nec, 0, 1, frequency, 0))
51 | handle_nec(necpp.nec_ex_card(nec, 0, 0, 1, 0, 1.0, 0, 0, 0, 0, 0))
52 | handle_nec(necpp.nec_rp_card(nec, 0, 90, 1, 0,5,0,0, 0, 90, 1, 0, 0, 0))
53 | result_index = 0
54 |
55 | z = complex(necpp.nec_impedance_real(nec,result_index),
56 | necpp.nec_impedance_imag(nec,result_index))
57 |
58 | necpp.nec_delete(nec)
59 | return z
60 |
61 | if (__name__ == 'main'):
62 | z = impedance(frequency = 34.5, z0 = 0.5, height = 4.0)
63 | print "Impedance \t(%6.1f,%+6.1fI) Ohms" % (z.real, z.imag)
64 |
65 | ## More Information
66 |
67 | Have a look at [http://github.com/tmolteno/necpp] for more information on using nec2++.
68 |
--------------------------------------------------------------------------------
/necpp/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Script to build the nec2++ python module.
3 | git submodule update --remote
4 | ln -s ../necpp_src .
5 | DIR=`pwd`
6 | cd necpp_src
7 | make -f Makefile.git
8 | ./configure --without-lapack
9 | cd ${DIR}
10 | PYTHON=python3
11 | swig3.0 -v -Inecpp_src/src/ -python necpp.i
12 | python3 setup.py build
13 | #sudo python setup.py install
14 |
--------------------------------------------------------------------------------
/necpp/example/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/necpp/example/Makefile:
--------------------------------------------------------------------------------
1 | all: plot
2 | python monopole.py
3 |
4 | plot:
5 | python impedance_plot.py
--------------------------------------------------------------------------------
/necpp/example/README.md:
--------------------------------------------------------------------------------
1 | # Using python-necpp
2 |
3 | Installation is easy with the python pip installer. Install python-necpp with
4 |
5 | pip install necpp
6 |
7 | This will download and compile the python-necpp package.
8 |
9 | ## A Simple Monopole
10 |
11 | NEC2 was based on punch cards and an antenna model was described as a series of Cards.
12 | These are well documented. Nec2++ replaces these cards with function calls each
13 | function call the equivalent of an nec2 card
14 |
15 | The file monopole.py shows how to model a simple vertical whip antenna.
16 |
17 | python monopole.py
18 |
19 | will print the impedance
20 |
21 | Impedance at base_height=0.50, length=4.00 : ( 142.2,-422.5I) Ohms
22 |
23 |
24 | ## Impedance Mismatch
25 |
26 | Radio recevers and transmitters are designed to operate with antennas of a specific impedance (Z0). If the antenna
27 | has a different impedance (Z_ant), this impedance mismatch causes loss of signal.
28 |
29 | The reflection coefficient measures how much signal is reflected at the junction between the antenna and the radio.
30 | The reflection coefficient (Gamma) is given by
31 |
32 | Gamma = (Z_ant - Z0)/(Z_ant + Z0)
33 |
34 | The transmission coefficient is (1.0 - Gamma) and represents how much of the original signal makes it
35 | through this junction.
36 |
37 | ## Searching for an optimum antenna
38 |
39 | If we minimize the reflection coefficient, then the performance of the antenna will be optimized.
40 | This is a relatively easy optimization. We can use matplotlib to plot the reflection coefficient
41 | as a function of length, with the base_height of the antenna fixed.
42 |
43 | python impedance_plot.py
44 |
45 | This shows that for short lengths, less than 10 percent of the signal makes it through. There is a local minimum (of approximately 0.3)
46 | that occurs around 1.1m for which around 70 percent of the signal makes it through.
47 |
--------------------------------------------------------------------------------
/necpp/example/antenna_util.py:
--------------------------------------------------------------------------------
1 | #
2 | # Some antenna utility functions
3 | #
4 | import numpy as np
5 |
6 | def reflection_coefficient(z, z0):
7 | return np.abs((z - z0) / (z + z0))
8 |
--------------------------------------------------------------------------------
/necpp/example/different_material.py:
--------------------------------------------------------------------------------
1 | from necpp import *
2 | import math
3 |
4 | def handle_nec(result):
5 | if (result != 0):
6 | print(nec_error_message())
7 |
8 | def geometry(freq, base, length):
9 |
10 | conductivity = 1.45e6 # Stainless steel
11 | ground_conductivity = 0.002
12 | ground_dielectric = 10
13 |
14 | wavelength = 3e8/(1e6*freq)
15 | n_seg = int(math.ceil(50*length/wavelength))
16 | nec = nec_create()
17 |
18 | '''
19 | \brief Set the prameters of the medium (permittivity and permeability)
20 |
21 | \param permittivity The electric permittivity of the medium (in farads per meter)
22 | \param permeability The magnetic permeability of the medium (in henries per meter)
23 |
24 | \remark From these parameters a speed of light is chosen.
25 | '''
26 | permittivity = 8.8e-12 # Farads per meter
27 | permeability = 4*math.pi*1e-7
28 | handle_nec(nec_medium_parameters(nec, 2.0*permittivity, permeability))
29 |
30 | handle_nec(nec_wire(nec, 1, n_seg, 0, 0, base, 0, 0, base+length, 0.002, 1.0, 1.0))
31 | handle_nec(nec_geometry_complete(nec, 1))
32 | handle_nec(nec_ld_card(nec, 5, 0, 0, 0, conductivity, 0.0, 0.0))
33 | handle_nec(nec_gn_card(nec, 0, 0, ground_dielectric, ground_conductivity, 0, 0, 0, 0))
34 | handle_nec(nec_fr_card(nec, 0, 1, freq, 0))
35 | # Voltage excitation one third of the way along the wire
36 | handle_nec(nec_ex_card(nec, 0, 0, int(n_seg/3), 0, 1.0, 0, 0, 0, 0, 0))
37 |
38 | return nec
39 |
40 | def impedance(freq, base, length):
41 | nec = geometry(freq, base, length)
42 | handle_nec(nec_xq_card(nec, 0)) # Execute simulation
43 | index = 0
44 | z = complex(nec_impedance_real(nec,index), nec_impedance_imag(nec,index))
45 | nec_delete(nec)
46 | return z
47 |
48 | if (__name__ == '__main__'):
49 | z = impedance(freq = 134.5, base = 0.5, length = 4.0)
50 | print("Impedance at base=%0.2f, length=%0.2f : (%6.1f,%+6.1fI) Ohms" % (0.5, 4.0, z.real, z.imag))
51 |
--------------------------------------------------------------------------------
/necpp/example/impedance_plot.py:
--------------------------------------------------------------------------------
1 | #
2 | # Plot of reflection coefficient vs antenna length for a fixed base height.
3 | #
4 | import monopole
5 | import numpy as np
6 | import pylab as plt
7 | from antenna_util import reflection_coefficient
8 |
9 | lengths = np.linspace(0.2, 5.0, 270)
10 | reflections = []
11 | z0 = 50
12 |
13 | for l in lengths:
14 | freq = 134.5
15 | z = monopole.impedance(freq, base=0.5, length=l)
16 | reflections.append(reflection_coefficient(z, z0))
17 |
18 | plt.plot(lengths, reflections)
19 | plt.xlabel("Antenna length (m)")
20 | plt.ylabel("Reflection coefficient")
21 | plt.title("Reflection coefficient vs length (base_height=0.5m)")
22 | plt.grid(True)
23 | plt.show()
24 | plt.savefig("reflection_coefficient.png")
25 |
26 |
--------------------------------------------------------------------------------
/necpp/example/monopole.py:
--------------------------------------------------------------------------------
1 | #
2 | # Simple vertical monopole antenna simulation using python-necpp
3 | # pip install necpp
4 | #
5 | from necpp import *
6 | import math
7 |
8 | def handle_nec(result):
9 | if (result != 0):
10 | print(nec_error_message())
11 |
12 | def geometry(freq, base, length):
13 |
14 | conductivity = 1.45e6 # Stainless steel
15 | ground_conductivity = 0.002
16 | ground_dielectric = 10
17 |
18 | wavelength = 3e8/(1e6*freq)
19 | n_seg = int(math.ceil(50*length/wavelength))
20 | nec = nec_create()
21 | handle_nec(nec_wire(nec, 1, n_seg, 0, 0, base, 0, 0, base+length, 0.002, 1.0, 1.0))
22 | handle_nec(nec_geometry_complete(nec, 1))
23 | handle_nec(nec_ld_card(nec, 5, 0, 0, 0, conductivity, 0.0, 0.0))
24 | handle_nec(nec_gn_card(nec, 0, 0, ground_dielectric, ground_conductivity, 0, 0, 0, 0))
25 | handle_nec(nec_fr_card(nec, 0, 1, freq, 0))
26 | # Voltage excitation one third of the way along the wire
27 | handle_nec(nec_ex_card(nec, 0, 0, int(n_seg/3), 0, 1.0, 0, 0, 0, 0, 0))
28 |
29 | return nec
30 |
31 | def impedance(freq, base, length):
32 | nec = geometry(freq, base, length)
33 | handle_nec(nec_xq_card(nec, 0)) # Execute simulation
34 | index = 0
35 | z = complex(nec_impedance_real(nec,index), nec_impedance_imag(nec,index))
36 | nec_delete(nec)
37 | return z
38 |
39 | if (__name__ == '__main__'):
40 | z = impedance(freq = 134.5, base = 0.5, length = 4.0)
41 | print("Impedance at base=%0.2f, length=%0.2f : (%6.1f,%+6.1fI) Ohms" % (0.5, 4.0, z.real, z.imag))
42 |
--------------------------------------------------------------------------------
/necpp/example/optimized.py:
--------------------------------------------------------------------------------
1 | #
2 | # Automatically tune antenna
3 | #
4 | import monopole
5 | import scipy.optimize
6 | import numpy as np
7 | from antenna_util import reflection_coefficient
8 |
9 | # A function that will be minimized when the impedance is 50 Ohms
10 | # We convert the height and antenna length to positive
11 | # numbers using exp. because otherwise the antenna will lie
12 | # below ground and cause an error in simulation.
13 | def target(x):
14 | global freq
15 | base_height = np.exp(x[0]) # Make it positive
16 | length = np.exp(x[1]) # Make it positive
17 | z = monopole.impedance(freq, base_height, length)
18 | return reflection_coefficient(z, z0=50.0)
19 |
20 |
21 | # Starting value
22 | freq = 134.5
23 | x0 = [-2.0, 0.0]
24 | # Carry out the minimization
25 | log_base, log_length = scipy.optimize.fmin(target, x0)
26 |
27 | base_height = np.exp(log_base)
28 | length = np.exp(log_length)
29 |
30 | print("Optimium base_height={}m, h={}m, impedance={} Ohms".format(base_height, length, monopole.impedance(freq, base_height, length)))
31 |
--------------------------------------------------------------------------------
/necpp/necpp.i:
--------------------------------------------------------------------------------
1 | %module necpp
2 | /* Part of the Python binding code for nec2++
3 | Copyright (C) 2008-2010,2015 Tim Molteno. tim@physics.otago.ac.nz
4 | Released under the GPL v3.
5 | */
6 | %{
7 | #include
8 | %}
9 |
10 | %include "typemaps.i"
11 |
12 | /* Used for functions that output a new opaque pointer */
13 | %typemap(in,numinputs=0) opaque_t *OUTPUT (opaque_t retval)
14 | {
15 | /* OUTPUT in */
16 | retval = NULL;
17 | $1 = &retval;
18 | }
19 |
20 | /* used for functions that take in an opaque pointer (or NULL)
21 | and return a (possibly) different pointer */
22 | %typemap(argout) opaque_t *OUTPUT, opaque_t *INOUT
23 | {
24 | /* OUTPUT argout */
25 | %append_output(SWIG_NewPointerObj(SWIG_as_voidptr(retval$argnum), $1_descriptor, 0));
26 | }
27 |
28 | %typemap(in) opaque_t *INOUT (opaque_t retval)
29 | {
30 | /* INOUT in */
31 | SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&retval), 0, 0);
32 | $1 = &retval;
33 | }
34 |
35 | /* No need for special IN typemap, it works anyway */
36 |
37 | %include
38 |
--------------------------------------------------------------------------------
/necpp/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.txt
3 |
--------------------------------------------------------------------------------
/necpp/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | setup.py file for necpp Python module.
5 | """
6 |
7 | from setuptools import setup, Extension
8 | from glob import glob
9 | import os
10 |
11 | nec_sources = []
12 | nec_sources.extend([fn for fn in glob('necpp_src/src/*.cpp')
13 | if not os.path.basename(fn).endswith('_tb.cpp')
14 | if not os.path.basename(fn).startswith('net_solve.cpp')
15 | if not os.path.basename(fn).startswith('nec2cpp.cpp')
16 | if not os.path.basename(fn).startswith('necDiff.cpp')])
17 | nec_sources.extend(glob("necpp_wrap.c"))
18 |
19 | nec_headers = []
20 | nec_headers.extend(glob("necpp_src/src/*.h"))
21 | nec_headers.extend(glob("necpp_src/config.h"))
22 |
23 |
24 | # At the moment, the config.h file is needed, and this should be generated from the ./configure
25 | # command in the parent directory. Use ./configure --without-lapack to avoid dependance on LAPACK
26 | #
27 | necpp_module = Extension('_necpp',
28 | sources=nec_sources,
29 | include_dirs=['necpp_src/src/', 'necpp_src/'],
30 | depends=nec_headers,
31 | define_macros=[('BUILD_PYTHON', '1')]
32 | )
33 |
34 | with open('README.md') as f:
35 | readme = f.read()
36 |
37 | setup (name = 'necpp',
38 | version = '1.7.3.5',
39 | author = "Tim Molteno",
40 | author_email = "tim@physics.otago.ac.nz",
41 | url = "http://github.com/tmolteno/necpp",
42 | keywords = "nec2 nec2++ antenna electromagnetism radio",
43 | description = "Python Antenna Simulation Module (nec2++) C-style interface",
44 | long_description=readme,
45 | long_description_content_type="text/markdown",
46 | include_package_data=True,
47 | data_files=[('examples', ['necpp_src/example/test.py'])],
48 | ext_modules = [necpp_module],
49 | py_modules = ["necpp"],
50 | license='GPLv2',
51 | classifiers=[
52 | "Development Status :: 5 - Production/Stable",
53 | "Topic :: Scientific/Engineering",
54 | "Topic :: Communications :: Ham Radio",
55 | "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
56 | 'Programming Language :: Python :: 2',
57 | 'Programming Language :: Python :: 2.7',
58 | 'Programming Language :: Python :: 3',
59 | 'Programming Language :: Python :: 3.3',
60 | 'Programming Language :: Python :: 3.4',
61 | 'Programming Language :: Python :: 3.5',
62 | "Intended Audience :: Science/Research"]
63 | )
64 |
--------------------------------------------------------------------------------