├── .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 | --------------------------------------------------------------------------------