GeezerVison is showing github.com — Some additional cards ported to the cleaner context API · saharbin/python-necpp@65601c1
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: PyNECPP/example/antenna_util.py +24Lines changed: 24 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 5 | 5 |
| 6 | 6 | def reflection_coefficient(z, z0): | 7 | 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 |
Collapse file Copy file name to clipboardExpand all lines: PyNECPP/example/context_clean.py +96-3Lines changed: 96 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 |
| 3 | 3 | # Currently these contain only the subset of cards that I needed | 4 | 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 | + | 5 | 24 | class context_clean(object): | 6 | 25 | def __init__(self, context): | 7 | 26 | self.context = context | @@ -14,6 +33,8 @@ def set_wire_conductivity(self, conductivity, wire_tag=None): | 14 | 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). """ | 15 | 34 | if wire_tag is None: | 16 | 35 | wire_tag = 0 | | 36 | + | | 37 | + debug("LD", 5, wire_tag, 0, 0, conductivity, 0, 0) | 17 | 38 | self.context.ld_card(5, wire_tag, 0, 0, conductivity, 0, 0) | 18 | 39 |
| 19 | 40 | def set_all_wires_conductivity(self, conductivity): | @@ -37,6 +58,7 @@ def set_frequencies_linear(self, start_frequency, stop_frequency, count=None, st | 37 | 58 | count = int(count) | 38 | 59 |
| 39 | 60 | ifrq_linear_step = 0 | | 61 | + debug("FR", ifrq_linear_step, count, start_frequency, step_size, 0, 0, 0) | 40 | 62 | self.context.fr_card(ifrq_linear_step, count, start_frequency, step_size) | 41 | 63 |
| 42 | 64 | def set_frequency(self, frequency): | @@ -46,19 +68,30 @@ def clear_ground(self): | 46 | 68 | gn_nullify_ground = -1 | 47 | 69 | self.context.gn_card(gn_nullify_ground, 0, 0, 0, 0, 0, 0, 0) | 48 | 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 | + | 49 | 73 | # TODO: gn card is iffy, check! | 50 | 74 | def set_finite_ground(self, ground_dielectric, ground_conductivity): | 51 | 75 | gn_finite_ground = 0 | 52 | 76 | no_ground_screen = 0 | 53 | 77 |
| 54 | 78 | self.context.gn_card(gn_finite_ground, no_ground_screen, ground_dielectric, ground_conductivity, 0, 0, 0, 0) | 55 | 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 | + | 56 | 88 | # TODO: i1 = 5 is also a voltage excitation | 57 | 89 | def voltage_excitation(self, wire_tag, segment_nr, voltage): | 58 | 90 | ex_voltage_excitation = 0 | 59 | 91 | no_action = 0 # TODO configurable | 60 | 92 | option_i3i4 = 10*no_action + no_action | 61 | 93 |
| | 94 | + debug("EX", ex_voltage_excitation, wire_tag, segment_nr, option_i3i4, voltage.real, voltage.imag, 0, 0, 0, 0) | 62 | 95 | self.context.ex_card(ex_voltage_excitation, wire_tag, segment_nr, option_i3i4, voltage.real, voltage.imag, 0, 0, 0, 0) | 63 | 96 |
| 64 | 97 | def get_geometry(self): | @@ -67,16 +100,74 @@ def get_geometry(self): | 67 | 100 |
| 68 | 101 | def set_extended_thin_wire_kernel(self, enable): | 69 | 102 | if enable: | | 103 | + debug ("EK", 0) | 70 | 104 | self.context.set_extended_thin_wire_kernel(1) | 71 | 105 | else: | | 106 | + debug ("EK", -1) | 72 | 107 | self.context.set_extended_thin_wire_kernel(0) | 73 | 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 | + | 74 | 168 | # Some simple wrappers for context... | 75 | 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, | 76 | 170 | # and then the wrappers are immediately correct and nice | 77 | | - | 78 | | - def geometry_complete(self, *args): | 79 | | - return self.context.geometry_complete(*args) | 80 | 171 | def xq_card(self, *args): | 81 | 172 | return self.context.xq_card(*args) | 82 | 173 | def get_input_parameters(self, *args): | @@ -88,4 +179,6 @@ def __init__(self, geometry): | 88 | 179 |
| 89 | 180 | def wire(self, tag_id, nr_segments, src, dst, radius, length_ratio=1.0, radius_ratio=1.0): | 90 | 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 | + | 91 | 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) |
Collapse file Copy file name to clipboardExpand all lines: README.md +6-4Lines changed: 6 additions & 4 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 |
| 3 | 3 | This repository contains two wrappers for the nec2++ antenna simulation package: | 4 | 4 |
| 5 | | -* PyNEC/ contains a wrapper using SWIG of the C interface (Python module name: necpp) | 6 | | -* PyNECPP/ contains a wrapper of the C++ interfaces (Python module name: PyNEC). | | 5 | +* PyNEC/ contains a wrapper using SWIG of the C interface (Python module name: necpp). | | 6 | +* PyNECPP/ 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 | 7 |
| 8 | 8 | | 9 | 9 |
| 10 | | -Personally, I (Bart Coppens, [email protected]) am more interested in a slightly cleaned-up version of the C++ interface wrappers. (I might still rename the Python module names to match the directory names, or rename the directories, or both) | | 10 | +Personally, I (Bart Coppens, [email protected]) am more interested in a slightly cleaned-up version of the C++ interface wrappers. (I might still rename the Python module names to match the directory names, or rename the directories, or both). In particular, as mentioned above, PyNECPP/example contains context_clean.py, which separates some of the NEC cards with multiple options into multiple functions (with named arguments that make sense, rather than itmp1, itmp2, etc). I've only really done this for the code I was playing around with in the example/ subdirectory, though, lots of cards (and sub-functionalities of cards) remain unwrapped/unported. Still, I found it to be nicer. | | 11 | + | | 12 | +PyNECPP/example/logperiodic.py furthermore contains an example on how to combine PyNECPP with scipy.optimize to use a genetic algorithm to **optimize an antenna for multiple frequency bands** at the same time (which I thin is not possible in 4nec2). | 11 | 13 |
| 12 | 14 | ## TODOs | 13 | | -The cleaner API should really be ported to C++, so the clean wrappers get automatically generated, and C++ can use the same cleaner interface | | 15 | +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 :) |
|