A NEC2 Optimizer (sort of)

This is a sandbox to start you thinking, not a finished program.

I discovered MMANA-GAL a few years ago, but since I've given up using Windows in my retirement, I had to find a different way to do things. NEC2 predates MMANA-GAL, which is based on MININEC, and it's in the public domain. There's some version in the standard ports/packages collections of most Unix-type operating systems or you can download it and compile it yourself. I've got 3 versions running: 1 compiled from FORTRAN, 1 that's rewritten in C using all double precision variables, and 1 that's been rewritten in C++. All of them have been pretty much abandoned I think, but combined with XNecview they aren't a bad way to do things.

In my early dabblings I thought MMANA-GAL's optimizer was magical. I'd get something close, push that button, and most of the time it would work. But I never understood much about how to run it beyond pushing the button and frankly MMANA-GAL's English documentation leaves a little to be desired. Supporting as many languages as they do something had to give. 4nec2 also has an optimizer if you use Windows.

My frustrations with MMANA-GAL go beyond trying to use the optimizer though, not that it's worth complaining about them in the Yahoo group for it. It doesn't seem well set up for multiple frequencies, compared to XNecview for one thing. Also now that I'm trying to do a little radio astronomy, I don't know how to tell MMANA-GAL that up is a desirable direction to point an antenna. Usually you maximize what's about 5 degrees above the horizon. Also everything is metric, with NEC2 you can use any unit you want as long as you provide a conversion factor.

There's an old adage that a patch panel will always be more versatile than a box of switches, just because you might think of connecting things some way the switch box designer didn't think of so he didn't build that capability in. The same sort of situation applies to NEC2 vs. MMANA-GAL. NEC2 can be very versatile, in fact since it's open source you can tinker with the way it works. There are error messages in there for example that don't tell which card/line the error was on, that can be fixed.

So here it is 2012 and I'm looking at antennas for Jove, I stumble on this basic picture of a collinear from an old ARRL Antenna Book:

collinear image

I found this already on the web, so I didn't violate ARRL copyrights by stealing it. Anyway, notice there's nothing specific about it: it can be used at any frequency and it's pretty simple. The only real question is where to put X that's the feedpoint: how far from the bottom of that center 1/4 wave section. I thought either NEC2 or MMANA-GAL should be able to give me an estimate.

I wrote this program mostly to make it easy to change things about the design of the collinear itself, I didn't intend to make it into something that would actually run NEC and parse the output. What I wanted to look at changing was the widths of the 1/4 wave sections. From an idea I got somewhere else those could be coiled-up sections of zip cord as long as they're 1/4 wave long, they just need to provide the needed delay. At the Jove frequency of 20.15 MHz, a quarter wavelength is about 11 feet, and I want to mount the collinear about 3 feet above ground. They also don't have to stick down, off to the side (away from you or toward you) is just as good.

Once you get everything else right look at using the FR card to specify multiple frequencies. XNecview will show plots of how the antenna behaves at those different frequencies, and you can change the frequency in the 3D far field view among them by using page up and page down. They have to have been in the NEC file though for that to work. There's some bug somewhere so that if you try to use more than 6 frequencies with XNecview under OpenBSD it crashes. The animated GIF here was made under Linux and uses about 50 frequencies. That page also shows an example of some of the plots XNecview does. It doesn't make animated GIFs though, that was a whole bunch of screen captures cropped and fed through Gifsicle.

So here's a plot of varying the distance of the feedpoint from X=0, the line defined by the top of the collinear in the picture above. I'm looking at the real and imaginary impedances here. The real wants to be close to 300 ohms, the imaginary (reactance) wants to be close to 0. At the left is the full 1/4 wave depth (in meters), at the right is having the feed inline with the X=0 line.

plot of feedpoint

So there isn't any easy answer, and I could have spent forever guessing if I hadn't done this. It looks like there might be multiple points that might work. Trying to zoom in on one, here at 2.8548 meters "down" looks like this, and we're getting down to the point of trying to hit a spot that's maybe only about a millimeter wide in trying to make the actual physical adjustment.

zoomed in image

So what happens if I fix one side of the feed wire and move the other? Then I get 2 spots to put it at: (these plots are looking at different peaks, that's why the numbers are different)

plot of
double quandry

It's worse than trying to zoom in on a fractal. The shape of this reminded me of something though, and it's a tan(x) plot:

tan(x) plot

tan(x) isn't quite it, but it's something like that. You can get closer and closer to that magic point, but you can't actually get there. But this page is about the program, not the collinear.

I set up the wires as an array of structs. The XYZ coordinates for both ends are in there, as well as the radius and number of segments. When in doubt about the number of segments, make them about 0.05 wavelengths, but round the count up to an odd number. I used doubles because I saw errors in trying to reproduce 5 digit numbers without.

Whenever possible the wire end points are defined in terms of something before that. I think I always went back to the original, there shouldn't be any 2nd generation copies here. The antenna height is 1 meter above ground (I'm looking at the sky remember). So wires[0].z1 is set to 1, and everything else references that for a z value. If you change wires[0].z1 you move the whole antenna up and down. The bottoms of the 1/4 wave gaps are all set by gap_depth so if you change that you change all of them. When you run that part of the program it spits out a file to run NEC2. I tried to set up variables for everything I anticipated changing. You'll want to get your antenna design into this form.

I set that up before I decided to run NEC2 from the program and parse the NEC2 output file. It only took about an hour to change it. There's a huge amount of output from NEC2, but only a few lines I care about. As I discovered years ago parsing the output from another program is usually fairly easy because it's always exactly the same each time. I was intending to have this usable at multiple frequencies (as defined by the FR card/line) so I grab the lines with "FREQUENCY : " which pertain to the next section. Then there's a line starting with "No:   No:     REAL   IMAGINARY     REAL       IMAGINARY" and that sets a flag to pull the next line and parse it. The lines are space-delimited, so frequency always starts in column 44.

There's no SWR in the NEC2 output as such, but it can be derived from other numbers. What I was mostly interested in here was the real and imaginary impedance. The real impedance should be a close match to the transmission line impedance. The imaginary impedance is the reactance, so negative values are capacitive and positive are inductive, which can tell if part of the antenna is too short or too long.

I define a variable called halfwave but that's mostly a nominative thing I could have called something else. Where something in the diagram is a half wavelength, I used this value. If it's a quarterwave I used half this value. I ended up tweaking it by multiplying by 0.83 to get the resonance right. Changing it changes almost all the sizes of things. How do you know what to set it to? Think about it: it isn't changing anything but the dimensions that get fed to NEC2. If those dimensions are right it doesn't matter what you had to set halfwave to to get them, the actual values are in the NEC file.

NEC dates back to punchcard days, so the lines in the input file can also be called cards. Certain cards are optional, but the order is fairly fixed. If you're just starting with NEC2, a useful site is nec2.org where you can find documentation and links to programs if not the programs themselves. I don't know why somebody rewrote NEC2 in C++ (I don't like C++), but the pdf that comes with that version is useful. So is the 3rd part of the original manual, which I got a copy of from nec2.org. I had a badly scanned one before, but somebody typed or OCRed this so it's searchable. That's the authoritative source of information on how to use NEC2 itself. From what I've seen the C and C++ versions work about the same way as the original, except for comments. All lines have to start with one of the two-letter codes that specify what type of card it is, most have integer fields at the beginning followed by float (aka real) fields. Columns aren't significant anymore and zeroes can act as blank fields. One quirk is that nec2c will add ".out" onto the name you use for an output file, the others don't, so you can get something.out.out.

With the original version comments were limited to cards at the beginning of the deck that stated with CM and a CE (comment end) card had to follow that section. The recent versions allow # as a comment marker, and they can appear anywhere. This is very handy to comment out a line and add a changed version. That's not possible in MMANA-GAL I think.

Also, ground used to be specfied with a GN card, now using a 1 or -1 on the GE (geometry end) card specifies that there is a ground or ground plane. A GN 1 card later will specify a perfect ground, I haven't tried imperfect grounds. There was a Sommerfield program for doing grounds, which is now built in. The FORTRAN version, nec2a, won't allow a GE -1 card, it expects just GE.

Both MMANA-GAL and NEC2 use what's called a "method of moments" way of predicting things, so things that don't work right in one probably won't work right in the other either. There's a bunch of NEC and MININEC stuff here. L.B. Cebik did a lot with antenna modeling too. NEC2 went on to become NEC3 and NEC4, but those aren't in the public domain yet and probably cost more than my car. A completely different method is called finite-difference time-domain (FDTD) but that's mostly for small things like microwaves and waveguides. Look at Meep and OpenEMS but you need to learn linear algebra and neither of these are easy either.

NEC2 used to be considered a big and cumbersome program. It's still cumbersome but by today's standards it isn't so big. I didn't check exactly but it can run somewhere around 100 times a minute in a loop on a 3.2 GHz single core P4.

To use the program, first get it set up so it compiles and runs. I'd set it so it only runs NEC once to start with, if at all. This is set to use nec2c but you can change it to nec2++ or even nec2a (the FORTRAN version). It should make a file called coll.nec. That should work with your NEC and you should be able to use xnecview on both the input and output files together and have that show at least something. Set whatever field you want to change in the antenna so it's controlled by the var variable and set the limits of the for loop in the bottom part of the program to use the range you want applied. If you follow this pattern of making something variable temporarily, then setting it to the best value you found and going on to something else you've got an optimizer. You can set wires[0].z1 to var to see the effect of putting the antenna at different heights. BUT: what you set to var has to be within the loop or it won't change. Setting halfwave to var (by itself) doesn't accomplish much, you'd have to put halfwave = var within the loop (and remember to take it back out again later).

When this runs it puts output in the out.tab file. Right now that output is the frequency, the value of the var variable, then the real and imaginary impedances. That file is plotted by Gunplot using the plotfile called plotthis.gnu and that makes a GIF file called nec.gif which you should look at with an image viewer. From that you may be able to see what value of the var variable worked best. You can look at out.tab if you want non-graphical output like to find the exact value of var that gave what you wanted. You can change the range of the for loop to give a different range, or set things up to vary a different parameter.

So what happened with the collinear? Maybe now I can get back to it. The gain figure below has to be taken with a grain of salt, but I've seen such nonsense from MMANA-GAL too. The vgain in XNecview is the gain in the direction toward the viewer and changes when you tilt and rotate the antenna.

collinear FF image #1 collinear FF image #2

Oh yes, the tarball

AB1JX / toys