├── .gitignore
├── LICENSE
├── LPF_DGS_bowtie.py
├── README.md
├── __init__.py
├── examples
├── .gitignore
├── LPF_DGS_bowtie_15.py
├── LPF_DGS_bowtie_7.py
├── README.md
├── capacitor_ground_cutout.py
├── field_replaceable_oshpark_flex.py
├── idbpf_tap_oshpark
│ ├── README.md
│ └── filter.py
├── images
│ └── vert_connector_ms_oshpark.png
├── microstrip.py
├── microstrip_PML.py
├── miter.py
├── openems
├── semi_rigid_047_oshpark_flex.py
├── semi_rigid_086_oshpark_flex.py
├── sma_el.py
├── sma_th.py
├── sss_idbpf.py
├── stripline
│ └── line.py
├── stripline_ecbpf.py
├── stripline_ss100.py
├── transition.py
├── vert_connector_ms_oshpark.kicad_mod
├── vert_connector_ms_oshpark.py
├── via_1_3_oshpark.py
├── via_1_4_oshpark.py
├── wilkinson_1.py
├── wilkinson_2.py
└── wilkinson_3.py
├── geometries.py
├── idbpf.py
├── idbpf_tapped.py
├── kicadfpwriter.py
├── miter.py
├── polygon.py
├── ratrace_folded.py
└── wilkinson.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 | *~
3 | *.kicad_mod
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Packages
9 | *.egg
10 | *.egg-info
11 | dist
12 | build
13 | eggs
14 | parts
15 | bin
16 | var
17 | sdist
18 | develop-eggs
19 | .installed.cfg
20 | lib
21 | lib64
22 | __pycache__
23 |
24 | # Installer logs
25 | pip-log.txt
26 |
27 | # Unit test / coverage reports
28 | .coverage
29 | .tox
30 | nosetests.xml
31 |
32 | # Translations
33 | *.mo
34 |
35 | # Mr Developer
36 | .mr.developer.cfg
37 | .project
38 | .pydevproject
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/LPF_DGS_bowtie.py:
--------------------------------------------------------------------------------
1 | from openems import Box, Port, Polygon, Metal, arc
2 | import numpy as np
3 |
4 | def generate(
5 | em,
6 | sub,
7 | mask,
8 | min_width, # minimum copper width, typically design rule minimum
9 | cutout_width, # width of ground plane cutouts
10 | inductors,
11 | capacitors,
12 | z, # bottom of [air below, bottom metal, substrate, top metal, air above, lid]
13 | port_length,
14 | ms_width,
15 | box_width,
16 | half_fan_angle=0.25*np.pi):
17 |
18 | em.mesh.AddLine('z', z[0]) # air above, below
19 | em.mesh.AddLine('z', z[5])
20 | em.mesh.AddLine('z', 0.5*(z[2]+z[3]))
21 | em.mesh.AddLine('z', 0.25*(3*z[2]+z[3]))
22 | em.mesh.AddLine('z', 0.25*(z[2]+3*z[3]))
23 | em.mesh.AddLine('y', -0.5*min_width)
24 | em.mesh.AddLine('y', 0.5*min_width)
25 |
26 | box_length = 2.0*(np.sum(inductors) + capacitors[0] + 0.5e-3)
27 |
28 | x0 = -0.5*box_length
29 | x1 = x0 + port_length
30 | x2 = x1 + ms_width
31 |
32 | yb = 0.5*cutout_width
33 | yo = -0.1e-3 # overlap
34 |
35 | pec = Metal(em, 'pec')
36 |
37 | # substrate
38 | start = np.array([ 0.5*box_length, 0.5*box_width, z[2]])
39 | stop = np.array([-0.5*box_length, -0.5*box_width, z[3]])
40 | Box(sub, 1, start, stop)
41 | # solder mask
42 | if mask != None:
43 | start[2] = z[3] + 25e-6
44 | Box(mask, 1, start, stop)
45 |
46 | # top copper polygon
47 | points = np.zeros((6+10*len(inductors),2))
48 | points[0] = [x1, 0.5*ms_width]
49 | x = -np.sum(inductors)
50 | points[1] = [x - 0.5*ms_width, 0.5*ms_width]
51 | points[2:10] = arc(x, 0, capacitors[0],
52 | 0.5*np.pi+half_fan_angle,
53 | 0.5*np.pi-half_fan_angle,
54 | npoints = 8)
55 | points[10] = [x + 0.5*min_width, 0.5*min_width]
56 | i = 11
57 | x += inductors[0]
58 | for j in range(1, len(inductors)):
59 | points[i+0] = [x-0.5*min_width, 0.5*min_width]
60 | points[i+1:i+9] = arc(x, 0, capacitors[j],
61 | 0.5*np.pi+half_fan_angle,
62 | 0.5*np.pi-half_fan_angle,
63 | npoints = 8)
64 | points[i+9] = [x+0.5*min_width, 0.5*min_width]
65 | i += 10
66 | x += inductors[j]
67 | # center cap
68 | points[i+0] = [-0.5*min_width, 0.5*min_width]
69 | points[i+1:i+5] = arc(0, 0, capacitors[-1],
70 | 0.5*np.pi+half_fan_angle,
71 | 0.5*np.pi + 0.001, npoints = 4)
72 | print(points)
73 | points = np.concatenate((points, points[::-1]*[-1,1]))
74 | points = np.concatenate((points, points[::-1]*[1,-1]))
75 | Polygon(pec, points, z[3:5], priority=9, pcb_layer = 'F.Cu')
76 |
77 | # ground plane
78 | gpec = Metal(em, 'ground_plane')
79 | points = np.zeros((2+6*len(inductors),2))
80 | points[0] = [x0, 0.5*box_width]
81 | points[1] = [x0, yo]
82 | i = 2
83 | x = -np.sum(inductors)
84 | for l in inductors:
85 | hmw = 0.5*min_width
86 | xl = x + hmw
87 | points[i+0] = [xl,yo]
88 | points[i+1] = [xl,hmw]
89 | xl = x + yb
90 | points[i+2] = [xl,yb]
91 | xl = x + l - yb
92 | points[i+3] = [xl,yb]
93 | xl = x + l - hmw
94 | points[i+4] = [xl,hmw]
95 | points[i+5] = [xl,yo]
96 | i += 6
97 | x += l
98 | print("ground plane",points)
99 | for ym in [1,-1]:
100 | Polygon(gpec,
101 | points = [1,ym] * np.concatenate((points, points[::-1]*[-1,1])),
102 | priority = 9,
103 | elevation = z[1:3],
104 | pcb_layer = 'B.Cu',
105 | is_custom_pad = True,
106 | x=0,
107 | y=ym*(yb+0.1e-3),
108 | pad_name='3',
109 | )
110 |
111 | for (xm,padname) in [(-1,2),(1,1)]:
112 | # main line ports
113 | start = [x0*xm, -0.5*ms_width, z[3]]
114 | stop = [x1*xm, 0.5*ms_width, z[4]]
115 | Port(em, start, stop, direction='x', z=50)
116 | # pads
117 | start[0] = x2*xm
118 | l1 = Box(pec, 9, start, stop, padname=padname)
119 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pyopenems
2 |
3 | A wrapper for the OpenEMS FDTD solver adding Kicad footprint generation for the simulated object.
4 |
5 | This is mostly an internal tool at Harmon Instruments and is likely to have the interfaces change at any time.
6 |
7 | ## [Examples](examples)
8 |
9 | ## Dependencies
10 | ### [OpenEMS](http://openems.de) > 0.35 with Python support.
11 |
12 | Install on Debian Buster:
13 |
14 | There is currently no installer, you need to place the repo somewhere in your python path.
15 |
16 | ```bash
17 | sudo apt build-dep openems
18 | sudo apt install cython3 build-essential cython3 python3-numpy python3-matplotlib
19 | sudo apt install python3-scipy python3-h5py
20 |
21 | git clone https://github.com/thliebig/openEMS-Project.git
22 | cd openEMS-Project
23 | git submodule init
24 | git submodule update
25 | export OPENEMS=$HOME/software/openems
26 | ./update_openEMS.sh $OPENEMS
27 | cd CSXCAD/python; python3 setup.py build_ext -I$OPENEMS/include -L$OPENEMS/lib -R$OPENEMS/lib; sudo python3 setup.py install; cd ../..
28 | cd openEMS/python; python3 setup.py build_ext -I$OPENEMS/include -L$OPENEMS/lib -R$OPENEMS/lib; sudo python3 setup.py install; cd ../..
29 | ```
30 |
31 | Install on Ubuntu 18.04 (user submitted):
32 |
33 | instead of `sudo apt build-dep openems`
34 |
35 | ```bash
36 | sudo apt install libtinyxml-dev libhdf5-serial-dev libcgal-dev vtk6 libvtk6-qt-dev
37 | sudo python3 -m pip install --upgrade pip
38 | sudo python3 -m pip install vtk scipy matplotlib h5py
39 | ```
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os, tempfile
3 | import numpy as np
4 | from scipy.constants import pi, c, epsilon_0, mu_0
5 | mm = 0.001
6 | import matplotlib.pyplot
7 | import openems.kicadfpwriter
8 |
9 | from CSXCAD import ContinuousStructure
10 | from openEMS import openEMS
11 |
12 | np.set_printoptions(precision=8)
13 |
14 | # convert an array of complex numbers to an array of [x,y]
15 | def complex_to_xy(a):
16 | rv = np.zeros((len(a),2))
17 | for i in range(len(a)):
18 | rv[i][0] = a[i].real
19 | rv[i][1] = a[i].imag
20 | return rv
21 |
22 | # generate an array of [x,y] for an arc
23 | def arc(x, y, r, a0, a1, npoints=32):
24 | angles = np.linspace(a0,a1,npoints)
25 | return complex_to_xy(r*np.exp(1j*angles) + x + 1j*y)
26 |
27 | def mirror(point, axes):
28 | retval = np.array(point).copy()
29 | if 'x' in axes:
30 | retval *= np.array([-1,1,1])
31 | if 'y' in axes:
32 | retval *= np.array([1,-1,1])
33 | if 'z' in axes:
34 | retval *= np.array([1,1,-1])
35 | return retval
36 |
37 | def db_angle(s):
38 | logmag = float(20.0*np.log10(np.abs(s)))
39 | angle = float(180.0*np.angle(s)/pi)
40 | return "{:>12f} {:>12f}".format(logmag, angle)
41 |
42 | def save_s1p(f, s11, filename, z):
43 | fdata = "# GHz S DB R {}\n".format(z)
44 | for i in range(len(f)):
45 | fdata += "{0:>12f} {1}\n".format(f[i]/1e9, db_angle(s11[i]))
46 | with open(filename, "w") as f:
47 | f.write(fdata)
48 |
49 | def save_s2p_symmetric(f, s11, s21, filename, z):
50 | print("warning: save_s2p_symmetric is only valid for a symmetric 2 port")
51 | fdata = "# GHz S DB R {}\n".format(z)
52 | for i in range(len(f)):
53 | fdata += "{0:>12f} {1} {2} {2} {1}\n".format(f[i]/1e9, db_angle(s11[i]), db_angle(s21[i]))
54 | with open(filename, "w") as f:
55 | f.write(fdata)
56 |
57 | class Material():
58 | def __init__(self, em, name):
59 | self.lossy = False
60 | self.em = em
61 | self.name = name
62 | def AddBox(self, start, stop, priority, **kwargs):
63 | return Box(self, start=start, stop=stop, priority=priority, **kwargs)
64 | def AddPolygon(self, **kwargs):
65 | return Polygon(self, **kwargs)
66 | def AddCylinder(self, start, stop, radius, priority):
67 | return Cylinder(self, priority, start, stop, radius)
68 |
69 | class Dielectric(Material):
70 | def __init__(self, em, name, eps_r=1.0, kappa = 0, ur=1.0, tand=0.0, fc = 0):
71 | if tand > 0.0:
72 | kappa = tand * 2*pi*fc * epsilon_0 * eps_r
73 | self.material = em.CSX.AddMaterial(name, epsilon = eps_r, kappa = kappa, mue = ur)
74 | self.em = em
75 | self.name = name
76 | self.eps_r = eps_r
77 | self.ur = ur
78 | self.kappa = kappa
79 | self.type = 'dielectric'
80 | self.lossy = False # metal loss
81 | # magnetic loss
82 |
83 | class LumpedElement(Material):
84 | """ element_type = 'R' or 'C' or 'L' """
85 | def __init__(self, em, name=None, element_type='R', value=50.0, direction = 'x'):
86 | self.em = em
87 | self.name = self.em.get_name(name)
88 | self.element_type = element_type
89 | self.value = value
90 | self.direction = direction
91 | self.material = em.CSX.AddLumpedElement(name=self.name, caps=False, ny = self.direction, R=self.value)
92 |
93 | class Metal(Material):
94 | def __init__(self, em, name):
95 | self.lossy = False
96 | self.em = em
97 | self.name = name
98 | self.type = 'metal'
99 | self.material=em.CSX.AddMetal(name)
100 |
101 | class LossyMetal(Material):
102 | def __init__(self, em, name, conductivity=56e6, frequency=None, thickness=None, ur=1.0):
103 | self.conductivity = conductivity
104 | if not thickness:
105 | # compute the skin depth
106 | self.thickness = np.sqrt((2.0/conductivity)/(2*pi*frequency*mu_0*ur))
107 | print('thickness = {}'.format(self.thickness))
108 | else:
109 | self.thickness = thickness
110 | self.em = em
111 | self.name = name
112 | self.type = 'metal'
113 | self.lossy = True
114 | self.material = em.CSX.AddConductingSheet(name, conductivity=self.conductivity, thickness=self.thickness)
115 |
116 | class Object():
117 | def generate_kicad(self, g):
118 | pass
119 |
120 | from .polygon import Polygon
121 |
122 | class Box(Object):
123 | def __init__(self, material, priority, start, stop, padname = '1', pcb_layer='F.Cu', mirror='', mesh=True):
124 | mirror = [-1 if 'x' in mirror else 1, -1 if 'y' in mirror else 1, -1 if 'z' in mirror else 1]
125 | self.priority = priority
126 | self.material = material
127 | self.start = np.array(start) * mirror
128 | self.stop = np.array(stop) * mirror
129 | self.em = material.em
130 | self.name = self.em.get_name(None)
131 | self.padname = padname
132 | self.layer = pcb_layer
133 | self.em.objects[self.name] = self
134 | self.mesh = mesh
135 | def generate_kicad(self, g):
136 | if self.material.__class__.__name__ == 'Dielectric':
137 | return
138 | if self.padname == None:
139 | return
140 | if self.padname == 'poly': # use a polygon rather than a pad
141 | x1 = self.start[0]
142 | x2 = self.stop[0]
143 | y1 = self.start[1]
144 | y2 = self.stop[1]
145 | points = np.array([[x1,y1],[x2,y1],[x2,y2],[x1,y2]])
146 | g.add_polygon(points = 1000.0 * points, layer = self.layer, width = 1e-6)
147 | return
148 | g.add_pad(self.padname,
149 | layer = self.layer,
150 | x = 500.0 * (self.start[0] + self.stop[0]), # mm
151 | y = 500.0 * (self.start[1] + self.stop[1]), # mm
152 | xsize = 1000.0 * abs(self.start[0] - self.stop[0]), # mm
153 | ysize = 1000.0 * abs(self.start[1] - self.stop[1]))
154 | def generate_octave(self):
155 | self.material.material.AddBox(start=self.start, stop=self.stop,
156 | priority=self.priority)
157 | if self.mesh:
158 | for vertex in [self.start, self.stop]:
159 | self.em.mesh.AddLine('x', vertex[0])
160 | self.em.mesh.AddLine('y', vertex[1])
161 | self.em.mesh.AddLine('z', vertex[2])
162 |
163 | class Cylinder(Object):
164 | def __init__(self, material, priority, start, stop, radius):
165 | self.material = material
166 | self.start = np.array(start)
167 | self.stop = np.array(stop)
168 | self.em = material.em
169 | self.name = self.em.get_name(None)
170 | self.radius = radius
171 | self.padname = '1'
172 | self.priority = priority
173 | self.em.objects[self.name] = self
174 | def generate_octave(self):
175 | self.material.material.AddCylinder(priority=self.priority,
176 | start=self.start,
177 | stop=self.stop,
178 | radius=self.radius)
179 |
180 | class Via(Object):
181 | """PCB via in Z direction
182 | em = OpenEMS instance
183 | name = any string for reference
184 | material = a string matching a defined material (see Material class)
185 | priority = integer, when objects overlap, higher takes precedence
186 | x, y = position
187 | z[0] = [barrel top, barrel bottom]
188 | z[1:] = [pad top, pad bottom]
189 | drill radius
190 | pad rad
191 | """
192 | def __init__(self, material, priority, x, y, z, drillradius, padradius, padname='1',
193 | wall_thickness=0):
194 | self.material = material
195 | self.priority = priority
196 | self.x = x
197 | self.y = y
198 | self.z = z
199 | self.drillradius = drillradius
200 | self.wall_thickness = wall_thickness
201 | self.padradius = padradius
202 | self.em = material.em
203 | self.name = self.em.get_name(None)
204 | self.padname = padname
205 | self.em.objects[self.name] = self
206 | def generate_kicad(self, g):
207 | g.add_pad(x = self.x * 1000.0,
208 | y = self.y * 1000.0,
209 | diameter = self.padradius * 2000.0, # footgen uses mm
210 | drill = self.drillradius * 2000.0,
211 | shape = "circle",
212 | name = self.padname)
213 | def generate_octave(self):
214 | start = [self.x + self.em.via_offset_x, self.y + self.em.via_offset_y, self.z[0][0]]
215 | stop = [self.x + self.em.via_offset_x, self.y + self.em.via_offset_y, self.z[0][1]]
216 | self.material.material.AddCylinder(start=start, stop=stop, priority=self.priority,
217 | radius = self.drillradius + self.wall_thickness)
218 | for z in self.z[1:]:
219 | start = [self.x, self.y, z[0]]
220 | stop = [self.x, self.y, z[1]]
221 | self.material.material.AddCylinder(start=start, stop=stop, priority=self.priority, radius = self.padradius)
222 |
223 | class RoundPad(Object):
224 | """PCB pad in Z direction
225 | em = OpenEMS instance
226 | material = a string matching a defined material (see Material class)
227 | priority = integer, when objects overlap, higher takes precedence
228 | x, y = position
229 | z[0] = [barrel top, barrel bottom]
230 | z[1:] = [pad top, pad bottom]
231 | drill radius
232 | pad rad
233 | """
234 | def __init__(self, em, material, priority, x, y, z, padradius, padname='1'):
235 | self.material = material
236 | self.priority = priority
237 | self.x = x
238 | self.y = y
239 | self.z = z
240 | self.padradius = padradius
241 | self.em = em
242 | self.name = self.em.get_name(None)
243 | self.padname = padname
244 | em.objects[self.name] = self
245 | def generate_kicad(self, g):
246 | g.add_pad(x = self.x * 1000.0,
247 | y = self.y * 1000.0,
248 | diameter = self.padradius * 2000.0, # footgen uses mm
249 | mask_clearance = 0.0,
250 | shape = "circle",
251 | name = self.padname)
252 | def generate_octave(self):
253 | self.material.material.AddCylinder(start = [self.x, self.y, self.z[0]],
254 | stop = [self.x, self.y, self.z[1]],
255 | priority=self.priority,
256 | radius = self.padradius)
257 |
258 | def Resistor(em, origin=np.array([0,0,0]), direction='x', value=100.0, invert=False, priority=9, dielectric=None, metal=None, element_down=False, size='0201', thickness=None, FC=False):
259 | zm = -1 if invert else 1
260 | def orient(x):
261 | if not 'y' in direction:
262 | return np.array([x[1], x[0], x[2]])
263 | return x
264 | if thickness is None:
265 | if size == '0402':
266 | thickness = 0.35e-3
267 | thickness = 0.25e-3
268 | x1 = 0.15e-3
269 | y1 = 0.3e-3
270 | y3 = 125e-6
271 | x2 = 0.1e-3 # element half width
272 | if size == '0402':
273 | x1 = 0.25e-3
274 | y1 = 0.5e-3
275 | y3 = 250e-6
276 | x2 = 0.2e-3
277 | y2 = y1-30e-6
278 |
279 | """ currently only supports 'x', 'y' for direction """
280 | element = LumpedElement(
281 | em, name=em.get_name(None), element_type='R', value=value, direction=direction)
282 | # resistor end caps
283 | start = np.array([-x1, -y1, 0])
284 | stop = np.array([ x1, -0.25*mm/2, 20e-6 if FC else thickness])
285 | if size == '0402':
286 | stop[1] = -0.25*mm
287 | for m in [np.array([1,-1,zm]), np.array([1,1,zm])]:
288 | Box(metal, priority, origin+orient(start*m), origin+orient(stop*m), padname = None)
289 | # resistor body
290 | start = np.array([-x1, -y2, 20e-6*zm])
291 | stop = np.array([ x1, y2, (thickness - 20e-6)*zm])
292 | body = Box(dielectric, priority+1, origin+orient(start), origin+orient(stop), padname=None)
293 | # resistor element
294 | zoff = 0.0 if element_down else thickness - 20e-6
295 | start = np.array([-x2, -y3, zoff*zm])
296 | stop = np.array([ x2, y3, (20e-6+zoff)*zm])
297 | Box(element, priority+1, origin+orient(start), origin+orient(stop), padname = None)
298 |
299 | class Port(Object):
300 | def __init__(self, em, start, stop, direction, z, padname = None, layer = 'F.Cu', mirror = ''):
301 | self.em = em
302 | mirror = [-1 if 'x' in mirror else 1, -1 if 'y' in mirror else 1, 1]
303 | self.start = np.array(start)*mirror
304 | self.stop = np.array(stop)*mirror
305 | self.direction = direction
306 | self.z = z
307 | self.padname = padname
308 | self.layer = layer
309 | self.portnumber = len(em.ports)
310 | name = "p" + str(self.portnumber)
311 | em.objects[name] = self
312 | em.ports.append(self)
313 | def generate_octave(self):
314 | self.port = self.em.FDTD.AddLumpedPort(
315 | self.portnumber,
316 | R=self.z,
317 | start=self.start,
318 | stop=self.stop,
319 | p_dir=self.direction,
320 | excite=1.0 if self.em.excitation_port == self.portnumber else 0)
321 | def generate_kicad(self, g):
322 | if self.padname == None:
323 | return
324 | g.add_pad(self.padname,
325 | layer = self.layer,
326 | x = 500.0 * (self.start[0] + self.stop[0]), # mm
327 | y = 500.0 * (self.start[1] + self.stop[1]), # mm
328 | xsize = 1000.0 * abs(self.start[0] - self.stop[0]), # mm
329 | ysize = 1000.0 * abs(self.start[1] - self.stop[1]))
330 |
331 | class OpenEMS:
332 | def __init__(self, name, fmin=0, fmax=50e9,
333 | NrTS=1e6,
334 | EndCriteria=1e-6,
335 | #BC = {xmin xmax ymin ymax zmin zmax};
336 | boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'],
337 | fsteps = 1601
338 | ):
339 | self.FDTD = openEMS(NrTS=NrTS, EndCriteria=EndCriteria)
340 | self.FDTD.SetGaussExcite((fmin+fmax)/2.0, (fmax-fmin)/2.0)
341 | self.FDTD.SetBoundaryCond(boundaries)
342 | self.CSX = ContinuousStructure()
343 | self.FDTD.SetCSX(self.CSX)
344 | self.mesh = self.CSX.GetGrid()
345 | self.mesh.SetDeltaUnit(1.0) # specify everything in m
346 | self.fmin = fmin
347 | self.fmax = fmax
348 | self.fsteps = fsteps
349 | self.objects = {}
350 | self.name = name
351 | self.ports = []
352 | self.excitation_port = 0
353 | self.excite_ports = [1]
354 | self.via_offset_x = 0.0
355 | self.via_offset_y = 0.0
356 | self.xgrid = None # for plot
357 | self.ygrid = None
358 | self.legend_location = 2 # upper left
359 | self.options = ''
360 | self.name_count = 0
361 | self.resolution = 0.0001
362 |
363 | def AddPort(self, start, stop, direction, z):
364 | return Port(self, start, stop, direction, z)
365 |
366 | def get_name(self, name):
367 | if name:
368 | return name
369 | self.name_count += 1
370 | return "pad_{}".format(self.name_count)
371 |
372 | def write_kicad(self, fpname, mirror=""):
373 | g = kicadfpwriter.Generator(fpname)
374 | g.mirror = mirror
375 | for object in self.objects:
376 | g.drill = 0
377 | self.objects[object].generate_kicad(g)
378 | fp = g.finish()
379 | with open(self.name+".kicad_mod", "w") as f:
380 | f.write(fp)
381 |
382 | def run_openems(self, options='view solve', z=50, initialize=True, show_plot=True, numThreads=8):
383 | cwd = os.getcwd()
384 | basename = cwd + '/' + self.name
385 | simpath = r'/tmp/openems_data' + self.name.split("/")[-1]
386 | if not os.path.exists(simpath):
387 | os.mkdir(simpath)
388 |
389 | for object in self.objects:
390 | self.objects[object].generate_octave()
391 |
392 | import collections.abc
393 | if not isinstance(self.resolution, collections.abc.Sequence):
394 | self.resolution = np.ones(3) * self.resolution
395 |
396 | ratio = 1.5
397 | for i in range(3):
398 | if self.resolution[i] is not None:
399 | self.mesh.SmoothMeshLines('xyz'[i], self.resolution[i], ratio)
400 |
401 | if 'view' in options:
402 | CSX_file = simpath + '/csx.xml'
403 | self.CSX.Write2XML(CSX_file)
404 | os.system(r'AppCSXCAD "{}"'.format(CSX_file))
405 | if 'solve' in options:
406 | self.FDTD.Run(simpath, verbose=3, cleanup=True, numThreads=numThreads)
407 | f = np.linspace(self.fmin, self.fmax, self.fsteps)
408 | for p in self.ports:
409 | p.port.CalcPort(simpath, f, ref_impedance = z)
410 | nports = len(self.ports)
411 |
412 | s = []
413 |
414 | for p in range(nports):
415 | s.append(self.ports[p].port.uf_ref / self.ports[0].port.uf_inc)
416 |
417 | if nports < 1:
418 | return
419 |
420 | self.frequencies = f
421 | fig, ax = matplotlib.pyplot.subplots()
422 | s11 = s[0]
423 | if nports == 1:
424 | save_s1p(f, s11, basename+".s1p", z=z)
425 | if nports > 1:
426 | s21 = s[1]
427 | ax.plot(f/1e9, 20*np.log10(np.abs(s21)), label = 'dB(s21)')
428 | save_s2p_symmetric(f, s11, s21, basename+".s2p", z=z)
429 | ax.plot(f/1e9, 20*np.log10(np.abs(s11)), label = 'dB(s11)')
430 | if nports > 2:
431 | for i in range(2,nports):
432 | ax.plot(f/1e9, 20*np.log10(np.abs(s[i])), label = f'dB(s{i+1}1)')
433 |
434 | ax.set_xlabel('Frequency (GHz)')
435 | ax.set_ylabel('dB')
436 | if hasattr(self.xgrid, "__len__"):
437 | ax.set_xticks(self.xgrid)
438 | if hasattr(self.ygrid, "__len__"):
439 | ax.set_yticks(self.ygrid)
440 | ax.grid(True)
441 | fig.tight_layout()
442 | ax.legend(loc=self.legend_location)
443 | matplotlib.pyplot.savefig(basename+".png")
444 | matplotlib.pyplot.savefig(basename+".svg")
445 | matplotlib.pyplot.savefig(basename+".pdf")
446 | if show_plot:
447 | matplotlib.pyplot.show()
448 | return s
449 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | *.svg
2 | *.png
3 | *.s2p
4 | *.s1p
5 | *.pdf
--------------------------------------------------------------------------------
/examples/LPF_DGS_bowtie_15.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import openems
4 | import openems.LPF_DGS_bowtie
5 | import numpy as np
6 |
7 | em = openems.OpenEMS('LPF_DGS_bowtie_15', EndCriteria = 1e-6, fmin = 0e6, fmax = 50e9, fsteps = 1601)
8 | em.resolution = [25e-6, 25e-6, 500e-6]
9 |
10 | fc = 6e9
11 |
12 | foil_thickness = 35e-6
13 | substrate_thickness = 100e-6
14 |
15 | z = np.zeros(6)
16 | z[1] = 1.6e-3
17 | z[2] = z[1] + foil_thickness
18 | z[3] = z[2] + substrate_thickness
19 | z[4] = z[3] + foil_thickness
20 | z[5] = z[4] + 1.6e-3
21 |
22 | openems.LPF_DGS_bowtie.generate(
23 | em,
24 | sub = openems.Dielectric(em, 'polyimide', eps_r=3.2, tand=0.002, fc=fc),
25 | mask = openems.Dielectric(em, 'soldermask', eps_r=3.3, tand=0.020, fc=fc),
26 | min_width = 152e-6,
27 | cutout_width = 1.0e-3,
28 | inductors = 0.15e-3 + 0.8*np.array([1.7e-3, 1.96e-3, 2e-3]),
29 | capacitors = np.array([0.47e-3, 0.77e-3, 0.81e-3, 0.82e-3]),
30 | z = z,
31 | port_length = 75e-6,
32 | ms_width = 195e-6,
33 | box_width = 2e-3)
34 |
35 | em.write_kicad(em.name)
36 | command = 'view solve'
37 | if len(sys.argv) > 1:
38 | command = sys.argv[1]
39 | em.run_openems(command)
40 |
--------------------------------------------------------------------------------
/examples/LPF_DGS_bowtie_7.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import openems
4 | import openems.LPF_DGS_bowtie
5 | import numpy as np
6 |
7 | em = openems.OpenEMS('LPF_DGS_bowtie_7', EndCriteria = 1e-5, fmin = 0e6, fmax = 30e9, fsteps = 1601)
8 | em.resolution = [50e-6, 50e-6, 500e-6]
9 |
10 | fc = 6e9
11 |
12 | foil_thickness = 35e-6
13 | substrate_thickness = 100e-6
14 |
15 | z = np.zeros(6)
16 | z[1] = 1.6e-3
17 | z[2] = z[1] + foil_thickness
18 | z[3] = z[2] + substrate_thickness
19 | z[4] = z[3] + foil_thickness
20 | z[5] = z[4] + 1.6e-3
21 |
22 | openems.LPF_DGS_bowtie.generate(
23 | em,
24 | sub = openems.Dielectric(em, 'polyimide', eps_r=3.2, tand=0.002, fc=fc),
25 | mask = openems.Dielectric(em, 'soldermask', eps_r=3.3, tand=0.020, fc=fc),
26 | min_width = 152e-6,
27 | cutout_width = 1.0e-3,
28 | inductors = 0.15e-3 + 1.55*np.array([1.7e-3, 1.96e-3, 2e-3]),
29 | capacitors = 1.45*np.array([0.47e-3, 0.77e-3, 0.81e-3, 0.82e-3]),
30 | z = z,
31 | port_length = 75e-6,
32 | ms_width = 195e-6,
33 | box_width = 3e-3)
34 |
35 | em.write_kicad(em.name)
36 | command = 'view solve'
37 | if len(sys.argv) > 1:
38 | command = sys.argv[1]
39 | em.run_openems(command)
40 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | ## Vertical 2.92 mm connector (vert_connector_ms_oshpark.py)
4 |
5 | Hirose HK-R-SR2-1 2.92 mm connector, footprint is compatible with
6 | HRM(G)-300-468B-1 (SMA) and Rosenberger 03K721-40MS3 (3.5
7 | mm). Transition to microstrip on opposite side of an OSHPark 4 layer
8 | PCB. The [Kicad footprint file](vert_connector_ms_oshpark.kicad_mod) is
9 | manually edited to add mask clearance, copper clearance and mounting
10 | holes. See [Breakout PCBs]( https://gitlab.com/harmoninstruments/breakout-pcbs/tree/master)
11 | for usage in a project.
12 |
13 | ### Response
14 | 
15 |
16 | ## Capacitor Ground Cutout (capacitor_ground_cutout.py)
17 |
18 | ### Usage
19 | ```
20 | $ ./capacitor_ground_cutout.py 0201 view_solve
21 | ```
22 |
23 | '0201' may be substituted for '0402' and refers to the inch capacitor size.
24 |
25 | 'view_solve' may be reduced to just 'view' or 'solve' to only view or only solve.
26 |
--------------------------------------------------------------------------------
/examples/capacitor_ground_cutout.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import openems
4 | import numpy as np
5 |
6 | mm = 1e-3 # default unit is the meter
7 | em = openems.OpenEMS(sys.argv[1], EndCriteria = 1e-6, fmin = 0e6, fmax = 60e9)
8 | em.fsteps = 801
9 | copper = openems.Metal(em, 'copper')
10 | sub = openems.Dielectric(em, 'substrate', eps_r=3.2)
11 | mask = openems.Dielectric(em, 'mask', eps_r=3.3)
12 | air = openems.Dielectric(em, 'air', eps_r=1.0006)
13 |
14 | foil_thickness = 0.035*mm
15 | port_length = 0.05*mm
16 | box_length = 5*mm
17 | box_width = 1.5*mm
18 | ms_width = 0.190*mm
19 | box_top = 1.5*mm
20 |
21 | # dimensions Z
22 | substrate_top = 0.102*mm
23 | foil_top = substrate_top + foil_thickness
24 | em.resolution = 50e-6
25 |
26 | em.mesh.AddLine('z', foil_top + box_top)
27 | em.mesh.AddLine('z', -0.5*mm)
28 |
29 | # substrate
30 | start = np.array([-0.5*box_length, 0.5*box_width, 0])
31 | stop = np.array([0.5*box_length, -0.5*box_width, substrate_top])
32 | sub.AddBox(start, stop, priority=2)
33 | start[2] = stop[2] + 25.4e-6
34 | mask.AddBox(start, stop, priority=0)
35 |
36 | # bottom foil
37 | start = np.array([-0.5*box_length, 0.5*box_width, 0])
38 | stop = np.array([0.5*box_length, -0.5*box_width, -foil_thickness])
39 | copper.AddBox(start, stop, priority=2)
40 |
41 | if sys.argv[1] == '0201':
42 | pad_y = 0.5 * 0.3*mm
43 | pad_x1 = 0.125*mm
44 | pad_x2 = 0.35*mm
45 | body_y = 0.15*mm
46 | body_x = 0.3*mm
47 | body_z = 0.3*mm
48 | cutout_x = 0.5 * 0.6*mm
49 | cutout_y = 0.5 * 0.42*mm
50 |
51 | elif sys.argv[1] == '0402':
52 | pad_y = 0.25*mm
53 | pad_x1 = 0.25*mm
54 | pad_x2 = 0.65*mm
55 | body_y = 0.25*mm
56 | body_x = 0.5*mm
57 | body_z = 0.5*mm
58 | cutout_x = 0.5 * 1.6*mm
59 | cutout_y = 0.5 * 0.6*mm
60 | else:
61 | exit()
62 |
63 | for xm in [-1,1]:
64 | # line
65 | start = np.array([xm*pad_x1, 0.5*ms_width, substrate_top])
66 | stop = np.array([xm*0.5*box_length-port_length, -0.5*ms_width, foil_top])
67 | copper.AddBox(start, stop, priority=9)
68 |
69 | # 0201 pads
70 | start = np.array([xm*pad_x1, pad_y, substrate_top])
71 | stop = np.array([xm*pad_x2, -pad_y, foil_top])
72 | copper.AddBox(start, stop, priority=9)
73 |
74 | # 0201 body
75 | start = np.array([-body_x, -body_y, foil_top + body_z])
76 | stop = np.array([ body_x, body_y, foil_top])
77 | copper.AddBox(start, stop, padname = '1', priority=9)
78 |
79 | # ground plane cutout
80 | start = np.array([-cutout_x, -cutout_y, 0])
81 | stop = np.array([ cutout_x, cutout_y, -foil_thickness])
82 | air.AddBox(start, stop, priority=9)
83 |
84 | # port (ms)
85 | start = [-0.5*box_length, ms_width/2.0, substrate_top]
86 | stop = [-0.5*box_length + port_length, ms_width/-2.0, foil_top]
87 | openems.Port(em, start, stop, direction='x', z=50)
88 |
89 | # port (ms)
90 | start = [0.5*box_length, ms_width/2.0, substrate_top]
91 | stop = [0.5*box_length - port_length, ms_width/-2.0, foil_top]
92 | openems.Port(em, start, stop, direction='x', z=50)
93 |
94 | command = 'view solve'
95 | if len(sys.argv) > 2:
96 | command = sys.argv[2]
97 | em.run_openems(command)
98 |
--------------------------------------------------------------------------------
/examples/field_replaceable_oshpark_flex.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # 0.020" pin to OSHPark flex
3 | import sys
4 | from scipy.constants import pi, c, epsilon_0, mu_0, mil, inch
5 | mm = 0.001
6 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric, Polygon, arc
7 | import numpy as np
8 | use_PML_coax = True
9 |
10 | em = OpenEMS(
11 | 'field_replaceable_to_oshpark_flex',
12 | EndCriteria = 1e-6,
13 | fmin = 0e6,
14 | fmax = 60e9,
15 | fsteps = 1001,
16 | boundaries = ['PEC', 'PML_16' if use_PML_coax else 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'],
17 | )
18 | copper = Metal(em, 'copper')
19 | copper_shield = Metal(em, 'copper_shield')
20 | copper_ms = Metal(em, 'copper_ms')
21 | sub = Dielectric(em, 'ro4350b', eps_r=3.2, tand=0.0035, fc=em.fmax)
22 | teflon = Dielectric(em, 'teflon', eps_r=2.1, tand=0.0002, fc=em.fmax)
23 |
24 | foil_thickness = 0.035*mm
25 | substrate_thickness = 4*mil
26 | port_length = 0.15*mm
27 | box_length = 3*mm
28 | box_width = 2*mm
29 | ms_width = 0.21*mm
30 | board_gap = 0.1*mm
31 |
32 | # coax
33 | pin_radius = 254e-6
34 | dielectric_radius = 0.5*67*mil
35 | coax_port_length = 0.2*mm
36 |
37 | box_height = dielectric_radius
38 |
39 | # dimensions Z
40 | foil_top = -(pin_radius)
41 | substrate_top = foil_top - foil_thickness
42 | substrate_bottom = substrate_top - substrate_thickness
43 |
44 | em.resolution = [50e-6, 15e-6, 15e-6]
45 |
46 | # substrate
47 | start = np.array([-0.5*box_length, 0.5*box_width, substrate_bottom])
48 | stop = np.array([0.0, -0.5*box_width, substrate_top])
49 | Box(sub, 1, start, stop);
50 |
51 | # line
52 | port_length = 0.065*mm
53 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
54 | stop = np.array([0, -0.5*ms_width, foil_top])
55 | Box(copper_ms, 1, start, stop, padname = None)
56 |
57 | # port (ms)
58 | stop[0] = -0.5*box_length
59 | Port(em, start, stop, direction='x', z=50)
60 |
61 | # pad
62 | ypad = 0.38e-3
63 | xpad = -0.62e-3
64 | xpad2 = -0.8e-3
65 |
66 | points = np.array([
67 | [0, ypad], [xpad, ypad], [xpad2, 0], [xpad, -ypad], [0, -ypad]])
68 | Polygon(copper, points, [substrate_top, foil_top], is_custom_pad=True, pad_name='1', x=0.5*xpad, y=0)
69 |
70 | # solder
71 | start = np.array([-0.5*mm, pin_radius, 0])
72 | stop = np.array([0, -pin_radius, foil_top])
73 | Box(Metal(em, 'solder'), 1, start, stop, padname = None)
74 |
75 | z1 = substrate_bottom - foil_thickness
76 | z2 = -(dielectric_radius + 1e-4)
77 |
78 | gp = Metal(em, 'groundplane')
79 | y1 = ypad+20e-6
80 | x1 = xpad2-00e-6
81 | points = np.array([
82 | [x1,y1], [0, y1], [0, 0.5*box_width], [-0.5*box_length, 0.5*box_width]])
83 | Polygon(
84 | copper,
85 | np.concatenate((points, points[::-1]*[1,-1])),
86 | [z1, substrate_bottom], is_custom_pad=True, pcb_layer='B.Cu', pad_name='2', x=x1-0.3e-3, y=0)
87 |
88 | angle = np.arccos(-z1/dielectric_radius)
89 | # in yz plane
90 | points = np.array([
91 | [0.5*box_width,z1],
92 | [0.5*box_width,z2],
93 | [-0.5*box_width,z2],
94 | [-0.5*box_width,z1]])
95 | pin_arc = arc(0,0, dielectric_radius, 1.5*pi-angle, 1.5*pi+angle)
96 | Polygon(copper_shield,
97 | points = np.concatenate((points, pin_arc)),
98 | elevation = [board_gap, -0.5*box_length],
99 | normal_direction = 'x',
100 | pcb_layer=None
101 | )
102 |
103 | # shield
104 | start = np.array([0.5*box_length, 0.5*box_width, dielectric_radius])
105 | stop = np.array([board_gap, -0.5*box_width, -dielectric_radius])
106 | Box(copper_shield, 1, start, stop, padname = None)
107 |
108 | # dielectric
109 | start = np.array([0.5*box_length, 0, 0])
110 | stop = np.array([board_gap, 0, 0])
111 | Cylinder(teflon, 2, start, stop, dielectric_radius)
112 |
113 | # pin
114 | start = np.array([0.5*box_length - coax_port_length, 0, 0])
115 | if use_PML_coax:
116 | start = np.array([0.5*box_length, 0, 0])
117 | stop = np.array([-0.5*mm, 0, 0])
118 | Cylinder(copper, 3, start, stop, pin_radius)
119 |
120 | # port (coax)
121 | start = [0.5*box_length, -0.5*coax_port_length, -0.5*coax_port_length]
122 | stop = [0.5*box_length - coax_port_length, 0.5*coax_port_length, 0.5*coax_port_length]
123 | if not use_PML_coax:
124 | Port(em, start, stop, direction='x', z=50)
125 |
126 | em.write_kicad(em.name)
127 | command = 'view solve'
128 | if len(sys.argv) > 1:
129 | command = sys.argv[1]
130 | print(command)
131 | em.run_openems(command)
132 |
--------------------------------------------------------------------------------
/examples/idbpf_tap_oshpark/README.md:
--------------------------------------------------------------------------------
1 | # Tapped interdigital bandpasses for OSHPark 4 layer
2 |
3 | These use the first inner layer as ground.
4 |
5 | The first argument is the center frequency in GHz. Valid values are 3, 4, 5, 6. The 2nd argument can be view, solve or be left out to do both.
6 |
7 | ```bash
8 | ./filter.py 4 solve
9 | ```
--------------------------------------------------------------------------------
/examples/idbpf_tap_oshpark/filter.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, epsilon_0, mu_0, mil, inch
4 | mm = 0.001
5 | import openems
6 | import openems.idbpf_tapped
7 | import numpy as np
8 |
9 | idx = int(sys.argv[1])
10 | name = 'idbpf_{}'.format(idx)
11 | fmax = [0,0,0,8e9,8e9,10e9,12e9][idx]
12 | em = openems.OpenEMS(name, fmin=0, fmax=fmax, fsteps=400, EndCriteria=1e-5)
13 | fc = [0, 0, 0, 3e9, 4e9, 5e9, 6e9][idx]
14 | pec = openems.Metal(em, 'pec')
15 | sub = openems.Dielectric(em, 'sub', eps_r=3.3, tand = 0.012, fc=fc)
16 |
17 | foil_thickness = 0.04*mm
18 | substrate_thickness = 0.18*mm
19 | ms_air_above = 1.0*mm
20 | via = 0.4*mm
21 | via_pad = 0.8*mm
22 |
23 | # dimensions Z
24 | substrate_bottom = 0.0
25 | substrate_top = substrate_bottom + substrate_thickness
26 | foil_top = substrate_top + foil_thickness
27 | em.mesh.AddLine('z', foil_top + ms_air_above)
28 |
29 | em.resolution = [0.1*mm, 0.04*mm, 0.5*mm]
30 |
31 | rl = [0,0,0,14.4*mm,10.8*mm,8.5*mm,7.1*mm][idx]
32 | rl = [rl]*6
33 | rl[0] += [0,0,0,0.95*mm,0.8*mm,0.6*mm,0.6*mm][idx]
34 |
35 | etch = 0.0 # etch error
36 | openems.idbpf_tapped.idbpf(
37 | em,
38 | sub = sub,
39 | tapoffset=[0,0,0,5.1*mm,3.8*mm,3.05*mm,2.5*mm][idx],
40 | rl = rl,
41 | rw = np.array([0.36*mm]*6) - etch,
42 | space = np.array([0.15*mm, 0.19*mm]) + etch,
43 | via_padradius = 0.5*via_pad,
44 | via_radius = 0.5*via,
45 | feedwidth = 0.36*mm,
46 | portlength = 0.3*mm,
47 | z = [substrate_bottom, substrate_top, foil_top],
48 | endmetal = False
49 | )
50 |
51 | em.write_kicad(name)
52 |
53 | command = 'view solve'
54 | if len(sys.argv) > 2:
55 | command = sys.argv[2]
56 | em.xgrid = np.linspace(0, fmax/1e9, 1+int(fmax/1e9))
57 | em.ygrid = np.linspace(-50.0, 0, 11)
58 | em.run_openems(command)
59 |
--------------------------------------------------------------------------------
/examples/images/vert_connector_ms_oshpark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlharmon/pyopenems/5998cd92fa048b085893cb2977af772264a78c7b/examples/images/vert_connector_ms_oshpark.png
--------------------------------------------------------------------------------
/examples/microstrip.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | mm = 0.001
4 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric
5 | import numpy as np
6 |
7 | em = OpenEMS('microstrip', EndCriteria = 1e-5, fmin = 0e6, fmax = 60e9, fsteps = 1601)
8 | copper = Metal(em, 'copper')
9 | sub = Dielectric(em, 'substrate', eps_r=3.2)
10 |
11 | foil_thickness = 0.036*mm
12 | box_length = 5*mm
13 | box_width = 2.5*mm
14 | box_top = 1.5*mm
15 |
16 | port_length = 0.1*mm
17 | substrate_top = 0.166*mm
18 | ms_width = 0.35*mm
19 |
20 | # oshpark flex
21 | port_length = 0.065*mm
22 | substrate_top = 4*25.4e-6
23 | ms_width = 0.21*mm
24 | box_width = 1.5*mm
25 | box_top = 1*mm
26 |
27 | foil_top = substrate_top + foil_thickness
28 |
29 | em.resolution = 10e-6
30 |
31 | em.mesh.AddLine('z', foil_top + box_top)
32 |
33 | # substrate
34 | start = np.array([-0.5*box_length, 0.5*box_width, 0])
35 | stop = np.array([0.5*box_length, -0.5*box_width, substrate_top])
36 | sub.AddBox(start, stop, priority=2)
37 |
38 | # line
39 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
40 | stop = np.array([0.5*box_length-port_length, -0.5*ms_width, foil_top])
41 | copper.AddBox(start, stop, padname = '1', priority=9)
42 |
43 | # port (ms)
44 | start = [-0.5*box_length, ms_width/2.0, substrate_top]
45 | stop = [-0.5*box_length + port_length, ms_width/-2.0, foil_top]
46 | Port(em, start, stop, direction='x', z=50)
47 |
48 | # port (ms)
49 | start = [0.5*box_length, ms_width/2.0, substrate_top]
50 | stop = [0.5*box_length - port_length, ms_width/-2.0, foil_top]
51 | Port(em, start, stop, direction='x', z=50)
52 |
53 | command = 'view solve'
54 | if len(sys.argv) > 1:
55 | command = sys.argv[1]
56 | em.run_openems(command)
57 |
--------------------------------------------------------------------------------
/examples/microstrip_PML.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | mm = 0.001
4 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric
5 | import numpy as np
6 | import argparse
7 |
8 | parser = argparse.ArgumentParser()
9 | #parser.add_argument('--command', type=str, help="text above graph", default="S Parameters")
10 | parser.add_argument('--view', action='store_true', help="view CSXCAD")
11 | parser.add_argument('--solve', action='store_true', help="run solver")
12 | parser.add_argument('--Er', type=float, help="dielectric", default=4.3)
13 | parser.add_argument('--Fc', type=float, help="center frequency", default=12e9)
14 | parser.add_argument('--Fmax', type=float, help="stop frequency", default=60e9)
15 | args = parser.parse_args()
16 |
17 | em = OpenEMS('microstrip', EndCriteria = 1e-5, fmin = 0e6, fmax = args.Fmax, fsteps = 1601, boundaries = ['PEC', 'PML_12', 'PEC', 'PEC', 'PEC', 'PEC'])
18 | copper = Metal(em, 'copper')
19 | sub = Dielectric(em, 'substrate', eps_r=args.Er, tand=0.00, fc=args.Fc)
20 |
21 | foil_thickness = 0.036*mm
22 | box_length = 10*mm
23 | box_width = 2.5*mm
24 | box_top = 1.5*mm
25 |
26 | port_length = 0.065*mm
27 | substrate_top = 100e-6
28 | ms_width = 0.156*mm
29 | box_width = 1*mm
30 | box_top = 0.5*mm
31 |
32 | foil_top = substrate_top + foil_thickness
33 |
34 | em.resolution = 25e-6
35 |
36 | em.mesh.AddLine('z', foil_top + box_top)
37 |
38 | # substrate
39 | start = np.array([-0.5*box_length, 0.5*box_width, 0])
40 | stop = np.array([0.5*box_length, -0.5*box_width, substrate_top])
41 | sub.AddBox(start, stop, priority=2)
42 |
43 | # line
44 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
45 | stop = np.array([0.5*box_length, -0.5*ms_width, foil_top])
46 | copper.AddBox(start, stop, padname = '1', priority=9)
47 |
48 | # port (ms)
49 | start = [-0.5*box_length, ms_width/2.0, substrate_top]
50 | stop = [-0.5*box_length + port_length, ms_width/-2.0, foil_top]
51 | Port(em, start, stop, direction='x', z=50)
52 |
53 | command = ''
54 | if args.view:
55 | command += 'view '
56 | if args.solve:
57 | command += 'solve '
58 | s = em.run_openems(command)
59 |
60 | if s is not None:
61 | s_dc = s[0][0]
62 | z_dc = 50.0 * (1+s_dc)/(1-s_dc)
63 | print(f"Z DC = {z_dc} ohms")
64 | print(s[0][0])
65 |
--------------------------------------------------------------------------------
/examples/miter.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, epsilon_0, mu_0, mil, inch
4 | mm = 0.001
5 | import openems
6 | import openems.miter
7 | import numpy as np
8 |
9 | em = openems.OpenEMS('miter_6.6')
10 | em.end_criteria = 1e-6
11 | em.fmin = 0
12 | em.fmax = 40e9
13 | em.fsteps = 1601
14 | fc = 40e9
15 | pec = openems.Metal(em, 'pec')
16 | sub = openems.Dielectric(em, 'ro4350b', eps_r=3.66, tand=0.0035, fc=fc)
17 |
18 | foil_thickness = 0.033*mm
19 | substrate_thickness = 6.6*mil
20 | ms_air_above = 1.0*mm
21 | z = [0.0, substrate_thickness, substrate_thickness + foil_thickness]
22 | em.mesh.AddLine('z', z[2] + ms_air_above)
23 |
24 | em.resolution = 50e-6
25 |
26 | openems.miter.generate(
27 | em,
28 | pec,
29 | sub,
30 | miter = 0.35 * mm,
31 | z = z,
32 | port_length = 0.1 * mm,
33 | ms_width = 0.3 *mm,
34 | box_size = 1.2*mm)
35 |
36 | em.write_kicad(em.name, mirror = '')
37 | em.write_kicad(em.name+'_m', mirror = 'y')
38 | command = 'view solve'
39 | if len(sys.argv) > 1:
40 | command = sys.argv[1]
41 | print(command)
42 | em.run_openems(command)
43 |
--------------------------------------------------------------------------------
/examples/openems:
--------------------------------------------------------------------------------
1 | ../
--------------------------------------------------------------------------------
/examples/semi_rigid_047_oshpark_flex.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # UT-047 to OSHPark flex
3 | import sys
4 | from scipy.constants import pi, c, mil
5 | mm = 0.001
6 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric, Polygon, arc
7 | import numpy as np
8 |
9 | em = OpenEMS(
10 | 'semi_rigid_047_oshpark_flex',
11 | EndCriteria = 1e-6,
12 | fmin = 0e6,
13 | fmax = 60e9,
14 | fsteps = 1001,
15 | boundaries = ['PEC', 'PML_16', 'PEC', 'PEC', 'PEC', 'PEC'],
16 | )
17 | copper = Metal(em, 'copper')
18 | copper_shield = Metal(em, 'copper_shield')
19 | copper_ms = Metal(em, 'copper_ms')
20 | sub = Dielectric(em, 'ro4350b', eps_r=3.2, tand=0.0035, fc=em.fmax)
21 | teflon = Dielectric(em, 'teflon', eps_r=2.1, tand=0.0002, fc=em.fmax)
22 |
23 | foil_thickness = 0.05*mm
24 | substrate_thickness = 4*mil
25 | box_length = 4*mm
26 | box_width = 2.2*mm
27 | ms_width = 0.19*mm
28 | board_gap = 0
29 |
30 | # coax
31 | pin_radius = 0.5*11.3*mil
32 | dielectric_radius = 0.5*37*mil
33 |
34 | box_height = dielectric_radius
35 |
36 | # dimensions Z
37 | foil_top = -(pin_radius)
38 | substrate_top = foil_top - foil_thickness
39 | substrate_bottom = substrate_top - substrate_thickness
40 |
41 | em.resolution = [50e-6, 25e-6, 25e-6]
42 |
43 | # substrate
44 | start = np.array([-0.5*box_length, 0.5*box_width, substrate_bottom])
45 | stop = np.array([0.0, -0.5*box_width, substrate_top])
46 | Box(sub, 1, start, stop);
47 |
48 | # line
49 | port_length = 0.065*mm
50 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
51 | stop = np.array([0, -0.5*ms_width, foil_top])
52 | Box(copper_ms, 1, start, stop, padname = None)
53 |
54 | # port (ms)
55 | stop[0] = -0.5*box_length
56 | Port(em, start, stop, direction='x', z=50)
57 |
58 | # pad
59 | ypad = 0.3e-3
60 | xpad = -0.62e-3
61 | xpad2 = -0.8e-3
62 | xpad1 = -0.1e-3
63 | points = np.array([
64 | [0,0], [xpad1, ypad], [xpad, ypad], [xpad2, 0], [xpad, -ypad], [xpad1, -ypad]])
65 | Polygon(copper, points, [substrate_top, foil_top], is_custom_pad=True, pad_name='1', x=0.5*xpad, y=0)
66 |
67 | # solder
68 | start = np.array([-0.5*mm, pin_radius, 0])
69 | stop = np.array([0, -pin_radius, foil_top])
70 | Box(Metal(em, 'solder'), 1, start, stop, padname = None)
71 |
72 | z1 = substrate_bottom - foil_thickness
73 | z2 = -(dielectric_radius + 1e-4)
74 |
75 | gp = Metal(em, 'groundplane')
76 | y1 = ypad-20e-6
77 | x1 = xpad2-00e-6
78 | points = np.array([
79 | [x1,y1], [0, y1], [0, 0.5*box_width], [-0.5*box_length, 0.5*box_width]])
80 | Polygon(
81 | copper,
82 | np.concatenate((points, points[::-1]*[1,-1])),
83 | [z1, substrate_bottom], is_custom_pad=True, pcb_layer='B.Cu', pad_name='2', x=x1-0.3e-3, y=0)
84 |
85 | angle = np.arccos(-z1/dielectric_radius)
86 |
87 | # shield
88 | start = np.array([0.5*box_length, 0.5*box_width, 1*mm])
89 | stop = np.array([board_gap, -0.5*box_width, -1*mm])
90 | Box(copper_shield, 1, start, stop, padname = None)
91 |
92 | # dielectric
93 | start = np.array([0.5*box_length, 0, 0])
94 | stop = np.array([board_gap, 0, 0])
95 | Cylinder(teflon, 2, start, stop, dielectric_radius)
96 |
97 | # pin
98 | start = np.array([0.5*box_length, 0, 0])
99 | stop = np.array([-0.5*mm, 0, 0])
100 | Cylinder(copper, 3, start, stop, pin_radius)
101 |
102 | em.write_kicad(em.name)
103 | command = 'view solve'
104 | if len(sys.argv) > 1:
105 | command = sys.argv[1]
106 | print(command)
107 | em.run_openems(command)
108 |
--------------------------------------------------------------------------------
/examples/semi_rigid_086_oshpark_flex.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # RG405 to OSHPark flex
3 | import sys
4 | from scipy.constants import pi, c, mil, inch
5 | mm = 0.001
6 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric, Polygon, arc
7 | import numpy as np
8 |
9 | em = OpenEMS(
10 | 'semi_rigid_086_oshpark_flex',
11 | EndCriteria = 1e-6,
12 | fmin = 0e6,
13 | fmax = 60e9,
14 | fsteps = 1001,
15 | boundaries = ['PEC', 'PML_16', 'PEC', 'PEC', 'PEC', 'PEC'],
16 | )
17 | copper = Metal(em, 'copper')
18 | copper_shield = Metal(em, 'copper_shield')
19 | copper_ms = Metal(em, 'copper_ms')
20 | sub = Dielectric(em, 'ro4350b', eps_r=3.2, tand=0.0035, fc=em.fmax)
21 | teflon = Dielectric(em, 'teflon', eps_r=2.1, tand=0.0002, fc=em.fmax)
22 |
23 | foil_thickness = 0.05*mm
24 | substrate_thickness = 4*mil
25 | box_length = 4*mm
26 | box_width = 2.2*mm
27 | ms_width = 0.19*mm
28 | board_gap = 0
29 |
30 | # coax
31 | pin_radius = 254e-6
32 | dielectric_radius = 0.5*67*mil
33 |
34 | box_height = dielectric_radius
35 |
36 | # dimensions Z
37 | foil_top = -(pin_radius)
38 | substrate_top = foil_top - foil_thickness
39 | substrate_bottom = substrate_top - substrate_thickness
40 |
41 | em.resolution = [50e-6, 25e-6, 25e-6]
42 |
43 | # substrate
44 | start = np.array([-0.5*box_length, 0.5*box_width, substrate_bottom])
45 | stop = np.array([0.0, -0.5*box_width, substrate_top])
46 | Box(sub, 1, start, stop);
47 |
48 | # line
49 | port_length = 0.065*mm
50 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
51 | stop = np.array([0, -0.5*ms_width, foil_top])
52 | Box(copper_ms, 1, start, stop, padname = None)
53 |
54 | # port (ms)
55 | stop[0] = -0.5*box_length
56 | Port(em, start, stop, direction='x', z=50)
57 |
58 | # pad
59 | ypad = 0.4e-3
60 | xpad = -0.62e-3
61 | xpad2 = -0.8e-3
62 | xpad1 = -0.05e-3
63 | points = np.array([
64 | [0,0], [xpad1, ypad], [xpad, ypad], [xpad2, 0], [xpad, -ypad], [xpad1, -ypad]])
65 | Polygon(copper, points, [substrate_top, foil_top], is_custom_pad=True, pad_name='1', x=0.5*xpad, y=0)
66 |
67 | # solder
68 | start = np.array([-0.5*mm, pin_radius, 0])
69 | stop = np.array([0, -pin_radius, foil_top])
70 | Box(Metal(em, 'solder'), 1, start, stop, padname = None)
71 |
72 | z1 = substrate_bottom - foil_thickness
73 | z2 = -(dielectric_radius + 1e-4)
74 |
75 | gp = Metal(em, 'groundplane')
76 | y1 = ypad+30e-6
77 | x1 = xpad2-00e-6
78 | points = np.array([
79 | [x1,y1], [0, y1], [0, 0.5*box_width], [-0.5*box_length, 0.5*box_width]])
80 | Polygon(
81 | copper,
82 | np.concatenate((points, points[::-1]*[1,-1])),
83 | [z1, substrate_bottom], is_custom_pad=True, pcb_layer='B.Cu', pad_name='2', x=x1-0.3e-3, y=0)
84 |
85 | angle = np.arccos(-z1/dielectric_radius)
86 |
87 | # shield
88 | start = np.array([0.5*box_length, 0.5*box_width, 1.5*mm])
89 | stop = np.array([board_gap, -0.5*box_width, -2*mm])
90 | Box(copper_shield, 1, start, stop, padname = None)
91 |
92 | # dielectric
93 | start = np.array([0.5*box_length, 0, 0])
94 | stop = np.array([board_gap, 0, 0])
95 | Cylinder(teflon, 2, start, stop, dielectric_radius)
96 |
97 | # pin
98 | start = np.array([0.5*box_length, 0, 0])
99 | stop = np.array([-0.5*mm, 0, 0])
100 | Cylinder(copper, 3, start, stop, pin_radius)
101 |
102 | em.write_kicad(em.name)
103 | command = 'view solve'
104 | if len(sys.argv) > 1:
105 | command = sys.argv[1]
106 | print(command)
107 | em.run_openems(command)
108 |
--------------------------------------------------------------------------------
/examples/sma_el.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Common SMA edge launch to OSHPark 4 layer
3 | import sys
4 | from scipy.constants import mil
5 | import openems
6 | import numpy as np
7 |
8 | em = openems.OpenEMS('sma_el', EndCriteria = 1e-6, fmin = 0, fmax = 20e9, fsteps = 1001)
9 | em.resolution = 8e-5
10 | copper = openems.Metal(em, 'copper')
11 | copper_shield = openems.Metal(em, 'copper_shield')
12 | sub = openems.Dielectric(em, 'ro4350b', eps_r=3.66, tand=0.0035, fc=em.fmax)
13 | teflon = openems.Dielectric(em, 'teflon', eps_r=2.1, tand=0.0002, fc=em.fmax)
14 |
15 | foil_thickness = 35e-6
16 | substrate_thickness = 62*mil-7*mil
17 | port_length = 300e-6
18 | box_length = 10e-3
19 | box_width = 6e-3
20 | box_height = 6e-3
21 | ms_width = 1.0e-3
22 | board_gap = 0.0
23 |
24 | # coax
25 | coax_scale = 2.0
26 | pin_diameter = 0.5e-3*coax_scale
27 | dielectric_diameter = 1.67e-3*coax_scale
28 | coax_port_length = 0.2e-3*coax_scale
29 |
30 | # dimensions Z
31 | foil_top = -0.5*pin_diameter
32 | substrate_top = foil_top - foil_thickness
33 | substrate_bottom = substrate_top - substrate_thickness
34 |
35 | # substrate
36 | start = np.array([-0.5*box_length, 0.5*box_width, substrate_bottom])
37 | stop = np.array([0.0, -0.5*box_width, substrate_top])
38 | openems.Box(sub, 1, start, stop);
39 |
40 | # line
41 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
42 | stop = np.array([0, -0.5*ms_width, foil_top])
43 | openems.Box(copper, 1, start, stop, padname = '1')
44 |
45 | # solder
46 | start = np.array([-0.5e-3, 0.5*pin_diameter, foil_top+0.5*pin_diameter])
47 | stop = np.array([0, -0.5*pin_diameter, foil_top])
48 | openems.Box(copper, 1, start, stop, padname = None)
49 |
50 | # ground via
51 | start = np.array([-0.5*box_length, -0.5*ms_width-0.2e-3, foil_top])
52 | stop = np.array([0.0, -0.5*box_width, substrate_top])
53 | for m in [[1,1,1], [1,-1,1]]:
54 | openems.Box(copper, 10, start*m, stop*m, padname = '2')
55 |
56 | # ground via
57 | start = np.array([-0.5*box_length, -0.5*ms_width-0.3e-3, substrate_top])
58 | stop = np.array([0.0, -0.5*box_width, substrate_bottom])
59 | for m in [[1,1,1], [1,-1,1]]:
60 | openems.Box(copper, 10, m*start, m*stop, padname = None)
61 |
62 | # ground
63 | start = np.array([-0.5*box_length, 0.5*box_width, substrate_bottom])
64 | stop = np.array([board_gap, -0.5*box_width, -0.5*box_height])
65 | openems.Box(copper, 10, start, stop, padname = None)
66 |
67 | # port (ms)
68 | start = [-0.5*box_length, 0.5*ms_width, substrate_top]
69 | stop = [-0.5*box_length + port_length, -0.5*ms_width, foil_top]
70 | openems.Port(em, start, stop, direction='x', z=50)
71 |
72 | # shield
73 | start = np.array([0.5*box_length, 0.5*box_width, 0.5*box_height])
74 | stop = np.array([board_gap, -0.5*box_width, -0.5*box_height])
75 | openems.Box(copper_shield, 1, start, stop, padname = None)
76 |
77 | # dielectric
78 | start = np.array([0.5*box_length, 0, 0])
79 | stop = np.array([board_gap, 0, 0])
80 | openems.Cylinder(teflon, 2, start, stop, 0.5*dielectric_diameter)
81 |
82 | # pin
83 | start = np.array([0.5*box_length - coax_port_length, 0, 0])
84 | stop = np.array([-0.5e-3, 0, 0])
85 | openems.Cylinder(copper, 3, start, stop, 0.5*pin_diameter)
86 |
87 | # port (coax)
88 | start = [0.5*box_length, -0.25*coax_port_length, -0.25*coax_port_length]
89 | stop = [0.5*box_length - coax_port_length, 0.25*coax_port_length, 0.25*coax_port_length]
90 | openems.Port(em, start, stop, direction='x', z=50)
91 |
92 | em.write_kicad(em.name)
93 | command = 'view solve'
94 | if len(sys.argv) > 1:
95 | command = sys.argv[1]
96 | em.run_openems(command)
97 |
--------------------------------------------------------------------------------
/examples/sma_th.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import mil
4 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric
5 | import numpy as np
6 |
7 | em = OpenEMS('sma_th', EndCriteria = 1e-6, fmin = 0, fmax = 20e9, fsteps = 1601)
8 | em.resolution = 8e-5
9 | copper = Metal(em, 'copper')
10 | copper_shield = Metal(em, 'copper_shield')
11 | ro4350b = Dielectric(em, 'ro4350b', eps_r=3.66, tand=0.0035, fc=em.fmax)
12 | teflon = Dielectric(em, 'teflon', eps_r=2.1, tand=0.0002, fc=em.fmax)
13 |
14 | foil_thickness = 0.035e-3
15 | substrate_thickness = 62*mil
16 | port_length = 1.0e-3
17 | box_length = 6e-3
18 | box_width = 6e-3
19 | box_height = 7e-3
20 | ms_width = 2.5e-3
21 |
22 | # coax
23 | pin_diameter = 1.27e-3
24 | dielectric_diameter = 4.2e-3
25 | coax_port_length = 0.4e-3
26 |
27 | # dimensions Z
28 | foil_top = -0.5*pin_diameter
29 | substrate_top = foil_top - foil_thickness
30 | substrate_bottom = substrate_top - substrate_thickness
31 |
32 | em.mesh.AddLine('z', 0.5*box_height)
33 |
34 | # substrate
35 | Box(ro4350b, 1,
36 | [-0.5*box_length, 0.5*box_width, substrate_bottom],
37 | [0.5*box_length, -0.5*box_width, substrate_top]
38 | )
39 |
40 | # line
41 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
42 | stop = np.array([0, -0.5*ms_width, foil_top])
43 | Box(copper, 1, start, stop, padname = '1')
44 |
45 | # via top
46 | Cylinder(copper, 2, [0, 0, substrate_top], [0, 0, foil_top], 0.5*ms_width)
47 |
48 | # ground top
49 | start = np.array([-0.5*box_length, -0.5*ms_width-0.5e-3, foil_top])
50 | stop = np.array([0.5*box_length, -0.5*box_width, substrate_top])
51 | for m in ['', 'y']:
52 | Box(copper, 10, start, stop, padname = '2', mirror=m)
53 |
54 | # ground via
55 | start = np.array([-0.5*box_length, -0.5*ms_width-1.0e-3, substrate_top])
56 | stop = np.array([0.5*box_length, -0.5*box_width, substrate_bottom])
57 | for m in ['', 'y']:
58 | Box(copper, 10, start, stop, padname = None, mirror=m)
59 |
60 | # port (ms)
61 | start = [-0.5*box_length, ms_width/2.0, substrate_top]
62 | stop = [-0.5*box_length + port_length, ms_width/-2.0, foil_top]
63 | Port(em, start, stop, direction='x', z=50)
64 |
65 | # shield
66 | start = np.array([0.5*box_length, 0.5*box_width, substrate_bottom])
67 | stop = np.array([-0.5*box_length, -0.5*box_width, -0.5*box_height])
68 | Box(copper_shield, 1, start, stop, padname = None)
69 |
70 | # dielectric
71 | start = np.array([0, 0, substrate_bottom])
72 | stop = np.array([0, 0, -0.5*box_height])
73 | Cylinder(teflon, 2, start, stop, 0.5*dielectric_diameter)
74 |
75 | # pin
76 | start = np.array([0, 0, substrate_top + 0.5e-3])
77 | stop = np.array([0, 0, -0.5*box_height + coax_port_length])
78 | Cylinder(copper, 3, start, stop, 0.5*pin_diameter)
79 |
80 | # port (coax)
81 | start = [0.5*coax_port_length, 0.5*coax_port_length, -0.5*box_height + coax_port_length]
82 | stop = [-0.5*coax_port_length, -0.5*coax_port_length, -0.5*box_height]
83 | Port(em, start, stop, direction='z', z=50)
84 |
85 | em.write_kicad(em.name)
86 | command = 'view solve'
87 | if len(sys.argv) > 1:
88 | command = sys.argv[1]
89 | print(command)
90 | em.run_openems(command)
91 |
--------------------------------------------------------------------------------
/examples/sss_idbpf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, mil
4 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric, Polygon, arc
5 | import numpy as np
6 |
7 | em = OpenEMS(
8 | 'sss_idbpf',
9 | EndCriteria = 1e-6,
10 | fmin = 0e6,
11 | fmax = 40e9,
12 | fsteps = 1001,
13 | boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'],
14 | )
15 | copper = Metal(em, 'copper')
16 | sub = Dielectric(em, 'polyimide', eps_r=3.2, tand=0.0035, fc=em.fmax)
17 |
18 | foil_thickness = 35e-6
19 | substrate_thickness = 4*mil
20 | port_length = 0.065e-3
21 | box_length = 14e-3
22 | ms_width = 0.6e-3
23 | port_length = 0.065e-3
24 |
25 | lengths = 3.0e-3*np.ones(4)
26 | gaps = [0.2e-3, 0.68e-3, 0.85e-3, 0.9e-3]
27 | rw = 1e-3
28 |
29 | # dimensions Z
30 | zair = 1e-3
31 | z0 = 0
32 | z1 = z0 + 1e-3 # bottom of bottom foil
33 | z2 = z1 + foil_thickness # top of bottom foil
34 | z3 = z2 + substrate_thickness # bottom of top foil
35 | z4 = z3 + foil_thickness # top of top foil
36 | z5 = z4 + zair
37 |
38 | em.resolution = [100e-6, 100e-6, 100e-6]
39 |
40 | em.mesh.AddLine('z', z0)
41 | em.mesh.AddLine('z', z5)
42 | em.mesh.AddLine('z', (2*z2+z3)*0.33)
43 | em.mesh.AddLine('z', (2*z3+z2)*0.33)
44 |
45 | endgap = 0.3e-3
46 |
47 | y = -0.5*(np.max(lengths)+endgap)
48 |
49 | # resonators
50 | x = -0.5*gaps[-1]
51 | ym = -1
52 |
53 | for i in range(len(lengths))[::-1]:
54 | ym *= -1
55 | x += gaps[i]
56 | l = lengths[i]
57 | x1 = x + rw
58 | yr = y+endgap if i==0 else y
59 | for m in [-1,1]:
60 | start = np.array([x*m, m*ym*yr, z3])
61 | stop = np.array([x1*m, m*ym*(y+l), z4])
62 | Box(copper, 1, start, stop, padname='poly')
63 | x = x1
64 |
65 | ymin = y
66 | ymax = -y
67 | ledge = 0.5e-3
68 | yshift = 0e-3
69 |
70 | # port (ms), port line
71 | for m in [-1,1]:
72 | start = np.array([m*(0.5*box_length-port_length), ym*(y+endgap)*m, z3])
73 | stop = np.array([m*x, ym*(y+endgap+ms_width)*m, z4])
74 | Box(copper, 1, start, stop, padname = None)
75 | stop[0] = m*0.5*box_length
76 | Port(em, start, stop, direction='x', z=50)
77 | # end grounds
78 | start = np.array([m*0.5*box_length, ymin, z1])
79 | stop = np.array([m*(x+0.3e-3), ymax, z2])
80 | Box(copper, 1, start, stop, padname = None)
81 |
82 | # top and bottom grounds
83 | for y in [[ymin, ymin-ledge+yshift], [ymax, ymax+ledge+yshift]]:
84 | for z in [[z3, z4], [z1, z2]]:
85 | start = np.array([m*0.5*box_length, y[0], z[0]])
86 | stop = np.array([m*-0.5*box_length, y[1], z[1]])
87 | Box(copper, 1, start, stop, padname = None)
88 |
89 | # substrate
90 | start = np.array([-0.5*box_length, ymin, z2])
91 | stop = np.array([0.5*box_length, ymax, z3])
92 | Box(sub, 1, start, stop)
93 |
94 | em.write_kicad(em.name)
95 | command = 'view solve'
96 | if len(sys.argv) > 1:
97 | command = sys.argv[1]
98 | print(command)
99 | em.run_openems(command)
100 |
--------------------------------------------------------------------------------
/examples/stripline/line.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, epsilon_0, mu_0, mil
4 | mm = 0.001
5 | import openems
6 | import numpy as np
7 |
8 | em = openems.OpenEMS('line', EndCriteria = 1e-6)
9 | em.fmin = 1e6
10 | em.fmax = 40e9
11 | em.fsteps = 1601
12 | fc = 40e9
13 | pec = openems.Metal(em, 'pec')
14 | sub = openems.Dielectric(em, 'fr4', eps_r=3.9, tand=0.0035, fc=fc)
15 | foil_thickness = 0.6*mil
16 | substrate_thickness = 0.2*mm
17 | ms_air_above = 0.36*mm
18 | port_length = 0.1*mm
19 | box_length = 10*mm
20 | box_width = 1*mm
21 | ms_width = 0.2*mm
22 | # dimensions Z
23 | substrate_bottom = 0.0
24 | substrate_top = substrate_bottom + substrate_thickness
25 | foil_top = substrate_top + foil_thickness
26 |
27 | from math import sqrt
28 | em.resolution = c/(em.fmax*sqrt(3.0))/100.0
29 |
30 | # substrate
31 | start = np.array([0.5*box_length, 0.5*box_width, substrate_bottom])
32 | stop = openems.mirror(start, 'xy') + np.array([0, 0, substrate_top+ms_air_above])
33 | sub.AddBox(start, stop, 1);
34 |
35 | # line
36 | start = np.array([0.5*box_length-port_length, 0.5*ms_width, substrate_top])
37 | stop = np.array([0, -0.5*ms_width, foil_top])
38 | for m in [[1,1,1], [-1,1,1]]:
39 | pec.AddBox(start*m, stop*m, 2)
40 |
41 | # ports
42 | start = np.array([-0.5*box_length, ms_width/2.0, substrate_top])
43 | stop = np.array([-0.5*box_length + port_length, ms_width/-2.0, foil_top])
44 | for m in [[1,1,1], [-1,-1,1]]:
45 | openems.Port(em, start*m, stop*m, direction='x', z=50)
46 |
47 | em.write_kicad(em.name)
48 | command = 'view solve'
49 | if len(sys.argv) > 1:
50 | command = sys.argv[1]
51 | print(command)
52 | em.run_openems(command)
53 |
--------------------------------------------------------------------------------
/examples/stripline_ecbpf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, mil
4 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric, Polygon, arc
5 | import numpy as np
6 |
7 | band = 5
8 |
9 | g = np.ones(10) * 0.28e-3
10 | g[0] = 0.18e-3
11 |
12 | cw = 4e-3
13 | mesh = [53e-6, 53e-6, 53e-6]
14 |
15 | # 7 updated
16 | w = np.array([7.25,15.75,19.25,19.25])*mil
17 | s = np.array([7.25,11.5,15.75,16.25])*mil
18 |
19 | boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC']
20 |
21 | if band == 1:
22 | fc = 16.95e9*.985
23 | fmax = 25e9
24 | g = np.ones(10) * 0.28e-3
25 | g[0] = 0.15e-3
26 | mesh = [34e-6, 32e-6, 27e-6]
27 | # 12.09183645866654
28 |
29 | elif band == 2:
30 | fc = 21.35e9*0.9875#/(21.62/21.35)
31 | fmax = 30e9
32 | mesh = [34e-6, 32e-6, 27e-6]
33 | #14.964134888145727
34 |
35 | elif band == 3:
36 | fc = 26.9e9*0.9955
37 | fmax = 35e9
38 | mesh = [34e-6, 32e-6, 27e-6]
39 | #18.483322945104838
40 |
41 | elif band == 4:
42 | fc = 33.9e9*0.9955
43 | fmax = 50e9
44 | mesh = [52e-6, 52e-6, 35e-6]
45 | boundaries = ['PEC', 'PEC', 'MUR', 'MUR', 'PEC', 'PEC']
46 |
47 | elif band == 5:
48 | fc = 42.7e9
49 | fmax = 60e9
50 | mesh = [52e-6, 52e-6, 35e-6]
51 | boundaries = ['PEC', 'PEC', 'MUR', 'MUR', 'PEC', 'PEC']
52 |
53 | elif band == 6:
54 | fc = 53.8e9
55 | fmax = 70e9
56 | mesh = [52e-6, 52e-6, 35e-6]
57 | #g *= 0
58 | boundaries = ['PEC', 'PEC', 'MUR', 'MUR', 'PEC', 'PEC']
59 |
60 | else:
61 | raise Exception("undefined band")
62 |
63 | em = OpenEMS(
64 | 'sss_ecbpf{}'.format(band),
65 | EndCriteria = 1e-6,
66 | fmin = 0e6,
67 | fmax = fmax,
68 | fsteps = 1001,
69 | boundaries = boundaries,
70 | )
71 |
72 | copper = Metal(em, 'copper')
73 | sub = Dielectric(em, 'polyimide', eps_r=3.2, tand=0.0035, fc=fc)
74 | zport = 100
75 | foil_thickness = 0.05e-3
76 | substrate_thickness = 4*mil
77 | port_length = 0.42e-3
78 |
79 | sl_width = 0.597e-3
80 |
81 | qw = 0.25 * c / (fc * np.sqrt(1.35))
82 | print("quarter wave length:", qw)
83 |
84 | etch = 0e-6
85 | g += 0.5*etch
86 | w -= etch
87 | s += etch
88 |
89 | # dimensions Z
90 | zair = 0.75e-3
91 | z0 = 0
92 | z1 = z0 + zair # bottom of bottom foil
93 | z2 = z1 + foil_thickness # top of bottom foil
94 | z3 = z2 + substrate_thickness # bottom of top foil
95 | z4 = z3 + foil_thickness # top of top foil
96 | z5 = z4 + zair
97 |
98 | em.resolution = mesh
99 |
100 | em.mesh.AddLine('z', z0)
101 | em.mesh.AddLine('z', z5)
102 | em.mesh.AddLine('z', z4 + 50e-6)
103 | #em.mesh.AddLine('z', z4 - 25e-6)
104 | em.mesh.AddLine('z', (z3+z2)*0.5)
105 | #em.mesh.AddLine('z', (3*z3+z2)*0.25)
106 |
107 | x = 0
108 | y = 0.5*w[-1]
109 |
110 | # resonators
111 | for i in range(len(w))[::-1]:
112 | gp = g[0]
113 | for m in [-1,1]:
114 | start = np.array([(x-gp)*m, (y-w[i])*m, z3])
115 | stop = np.array([(x+qw-g[i])*m, y*m, z4])
116 | Box(copper, 1, start, stop, padname='poly')
117 | y += s[i]
118 | for m in [-1,1]:
119 | start = np.array([(x+g[i])*m, y*m, z3])
120 | stop = np.array([(x+qw)*m, (y+w[i])*m, z4])
121 | Box(copper, 1, start, stop, padname='poly')
122 | y += w[i]
123 | x += qw
124 |
125 | # port (ms), port line
126 | y -= w[0]
127 | hl = x + .75e-3
128 | pname = 1
129 | for m in [-1,1]:
130 | start = np.array([m*(hl-port_length), m*y, z3])
131 | stop = np.array([m*x, m*(y+sl_width), z4])
132 | Box(copper, 1, start, stop, padname = str(pname))
133 | pname += 1
134 | stop[0] = m*hl
135 | Port(em, start, stop, direction='x', z=zport)
136 |
137 | y += sl_width
138 | theta = np.arctan(y/hl)
139 | print("theta =", theta*180/np.pi)
140 | yoff = 0.5*cw/np.cos(theta)
141 | print("yoff =", yoff)
142 | ymax = y + yoff
143 |
144 | points = np.array([[-hl, -ymax], [hl, -ymax], [hl, ymax-2*yoff]])
145 | for m in ['xy', '']:
146 | if boundaries[2] == 'PEC':
147 | Polygon(copper, points, [z0, z5], x=0, y=0, mirror=m, priority=9)
148 |
149 | theta = np.arctan((2*ymax-2*yoff)/(2*hl))
150 | print("theta =", theta*180/np.pi)
151 |
152 | # substrate
153 | start = np.array([-hl, -ymax, z2])
154 | stop = np.array([hl, ymax, z3])
155 | Box(sub, 1, start, stop)
156 |
157 | em.write_kicad(em.name)
158 | command = 'view solve'
159 | if len(sys.argv) > 1:
160 | command = sys.argv[1]
161 | print(command)
162 | em.run_openems(command, z=zport)
163 |
--------------------------------------------------------------------------------
/examples/stripline_ss100.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, mil
4 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric, Polygon, arc
5 | import numpy as np
6 | use_PML_coax = True
7 |
8 | em = OpenEMS(
9 | 'sss_bpf',
10 | EndCriteria = 1e-6,
11 | fmin = 0e6,
12 | fmax = 50e9,
13 | fsteps = 1001,
14 | #boundaries = ['PEC', 'PEC', 'PML_8', 'PML_8', 'PEC', 'PEC'],
15 | )
16 | fc = 16.95e9
17 | copper = Metal(em, 'copper')
18 | sub = Dielectric(em, 'ro4350b', eps_r=3.2)
19 | zport = 100
20 |
21 | foil_thickness = 0.05e-3
22 | substrate_thickness = 4*mil
23 | port_length = 0.42e-3
24 | box_width = 3e-3
25 | box_length = 20e-3
26 | ms_width = 0.56e-3
27 |
28 | # dimensions Z
29 | zair = 0.7e-3
30 | z0 = 0
31 | z1 = z0 + zair # bottom of bottom foil
32 | z2 = z1 + foil_thickness # top of bottom foil
33 | z3 = z2 + substrate_thickness # bottom of top foil
34 | z4 = z3 + foil_thickness # top of top foil
35 | z5 = z4 + zair
36 |
37 | em.resolution = [50e-6, 50e-6, 50e-6]
38 |
39 | em.mesh.AddLine('z', z0)
40 | em.mesh.AddLine('z', z5)
41 | em.mesh.AddLine('z', z4 + 50e-6)
42 | #em.mesh.AddLine('z', z4 - 25e-6)
43 | em.mesh.AddLine('z', (z3+z2)*0.5)
44 | #em.mesh.AddLine('z', (3*z3+z2)*0.25)
45 |
46 | # substrate
47 | start = np.array([-0.5*box_length, 0.5*box_width, z2])
48 | stop = np.array([0.5*box_length, -0.5*box_width, z3])
49 | Box(sub, 1, start, stop)
50 |
51 | for m in [-1,1]:
52 | start = np.array([m*(0.5*box_length-port_length), 0.5*ms_width, z3])
53 | stop = np.array([0, -0.5*ms_width, z4])
54 | Box(copper, 1, start, stop, padname = None)
55 | stop[0] = m*0.5*box_length
56 | Port(em, start, stop, direction='x', z=zport)
57 |
58 | em.write_kicad(em.name)
59 | command = 'view solve'
60 | if len(sys.argv) > 1:
61 | command = sys.argv[1]
62 | print(command)
63 | em.run_openems(command, z=zport)
64 |
--------------------------------------------------------------------------------
/examples/transition.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, mil
4 | from openems import OpenEMS, Box, Cylinder, Port, Metal, Dielectric, Polygon, arc
5 | import numpy as np
6 | use_PML_coax = True
7 |
8 | em = OpenEMS(
9 | 'suspended_substrate_stripline',
10 | EndCriteria = 1e-6,
11 | fmin = 0e6,
12 | fmax = 60e9,
13 | fsteps = 1001,
14 | boundaries = ['PEC', 'PML_16' if use_PML_coax else 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'],
15 | )
16 | copper = Metal(em, 'copper')
17 | copper_shield = Metal(em, 'copper_shield')
18 | copper_ms = Metal(em, 'copper_ms')
19 | sub = Dielectric(em, 'ro4350b', eps_r=3.2, tand=0.0035, fc=em.fmax)
20 | teflon = Dielectric(em, 'teflon', eps_r=2.1, tand=0.0002, fc=em.fmax)
21 |
22 | foil_thickness = 0.035e-3
23 | substrate_thickness = 4*mil
24 | port_length = 0.15e-3
25 | box_width = 2e-3
26 | box_length = 5e-3
27 | ms_width = 0.21e-3
28 | 3
29 | # coax
30 | pin_radius = 254e-6
31 | dielectric_radius = 0.5*67*mil
32 | coax_port_length = 0.2e-3
33 |
34 | # dimensions Z
35 | foil_top = 0
36 | substrate_top = foil_top - foil_thickness
37 | substrate_bottom = substrate_top - substrate_thickness
38 |
39 | em.resolution = [50e-6, 25e-6, 25e-6]
40 |
41 | em.mesh.AddLine('z', substrate_bottom - 1e-3)
42 | em.mesh.AddLine('z', substrate_top + 1e-3)
43 |
44 | # substrate
45 | start = np.array([-0.5*box_length, 0.5*box_width, substrate_bottom])
46 | stop = np.array([0.0, -0.5*box_width, substrate_top])
47 | Box(sub, 1, start, stop);
48 |
49 | # line
50 | port_length = 0.065e-3
51 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, substrate_top])
52 | stop = np.array([0, -0.5*ms_width, foil_top])
53 | Box(copper_ms, 1, start, stop, padname = None)
54 |
55 | # port (ms)
56 | stop[0] = -0.5*box_length
57 | Port(em, start, stop, direction='x', z=50)
58 |
59 | # pad
60 | ypad = 0.6e-3
61 | xpad = -0.62e-3
62 | xpad2 = -0.8e-3
63 |
64 | points = np.array([
65 | [0, ypad], [xpad, ypad], [xpad2, 0], [xpad, -ypad], [0, -ypad]])
66 | Polygon(copper, points, [substrate_top, foil_top], is_custom_pad=True, pad_name='1', x=0.5*xpad, y=0)
67 |
68 | z1 = substrate_bottom - foil_thickness
69 |
70 | gp = Metal(em, 'groundplane')
71 | y1 = ypad+200e-6
72 | x1 = xpad2-00e-6
73 | points = np.array([
74 | [x1,y1], [0, y1], [0, 0.5*box_width], [-0.5*box_length, 0.5*box_width]])
75 | Polygon(
76 | copper,
77 | np.concatenate((points, points[::-1]*[1,-1])),
78 | [z1, substrate_bottom], is_custom_pad=True, pcb_layer='B.Cu', pad_name='2', x=x1-0.3e-3, y=0)
79 |
80 | em.write_kicad(em.name)
81 | command = 'view solve'
82 | if len(sys.argv) > 1:
83 | command = sys.argv[1]
84 | print(command)
85 | em.run_openems(command)
86 |
--------------------------------------------------------------------------------
/examples/vert_connector_ms_oshpark.kicad_mod:
--------------------------------------------------------------------------------
1 | (module coax_compression (layer F.Cu) (tedit 5C589EFB)
2 | (descr DocString)
3 | (tags Keywords)
4 | (solder_mask_margin 0.0762)
5 | (clearance 0.152)
6 | (zone_connect 2)
7 | (attr smd)
8 | (fp_text reference J? (at 0 -5.85) (layer F.SilkS)
9 | (effects (font (size 1 1) (thickness 0.127)))
10 | )
11 | (fp_text value ? (at 0 1.27) (layer Cmts.User) hide
12 | (effects (font (size 1.016 1.016) (thickness 0.127)))
13 | )
14 | (fp_circle (center 0 -3.6) (end 2.4 -3.6) (layer Cmts.User) (width 0.12))
15 | (fp_circle (center 0 3.6) (end 2.4 3.6) (layer Cmts.User) (width 0.12))
16 | (fp_poly (pts (xy -2.9 -4.2) (xy -2.9 4.2) (xy -1.45 5) (xy 1.45 5)
17 | (xy 2.9 4.2) (xy 2.9 -4.2) (xy 1.45 -5) (xy -1.45 -5)) (layer F.Mask) (width 0.2))
18 | (fp_text user value (at 0 1.27) (layer F.SilkS) hide
19 | (effects (font (size 0.7 0.7) (thickness 0.127)))
20 | )
21 | (fp_text user U1 (at 0 -1.27) (layer F.SilkS)
22 | (effects (font (size 0.7 0.7) (thickness 0.127)))
23 | )
24 | (pad 2 thru_hole circle (at 0 -3.58) (size 2.5 2.5) (drill 1.6) (layers *.Cu *.Mask)
25 | (zone_connect 2))
26 | (pad 2 thru_hole circle (at 0 3.58) (size 2.5 2.5) (drill 1.6) (layers *.Cu *.Mask)
27 | (zone_connect 2))
28 | (pad 2 thru_hole circle (at -0.669131 -0.743145) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
29 | (solder_mask_margin -0.0325) (zone_connect 2))
30 | (pad 2 thru_hole circle (at 0.71934 0.694658) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
31 | (solder_mask_margin -0.0325) (zone_connect 2))
32 | (pad 2 thru_hole circle (at 0.034899 -0.999391) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
33 | (solder_mask_margin -0.0325) (zone_connect 2))
34 | (pad 2 thru_hole circle (at 0.034899 0.999391) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
35 | (solder_mask_margin -0.0325) (zone_connect 2))
36 | (pad 2 thru_hole circle (at 1 0) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
37 | (solder_mask_margin -0.0325) (zone_connect 2))
38 | (pad 2 thru_hole circle (at -0.669131 0.743145) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
39 | (solder_mask_margin -0.0325) (zone_connect 2))
40 | (pad 2 thru_hole circle (at 0.71934 -0.694658) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
41 | (solder_mask_margin -0.0325) (zone_connect 2))
42 | (pad 1 thru_hole circle (at 0 0) (size 0.5 0.5) (drill 0.25) (layers *.Cu *.Mask)
43 | (solder_mask_margin -0.0325) (clearance 0.275) (zone_connect 2))
44 | (pad 1 smd circle (at 0 0) (size 0.5 0.5) (layers F.Cu F.Paste F.Mask)
45 | (solder_mask_margin -0.0325) (clearance 0.5) (zone_connect 2))
46 | (model "/home/dlharmon/vna/library/3d/HRM(G)-300-468B-1.stp"
47 | (offset (xyz 0 0 4.5))
48 | (scale (xyz 1 1 1))
49 | (rotate (xyz 90 0 90))
50 | )
51 | )
52 |
--------------------------------------------------------------------------------
/examples/vert_connector_ms_oshpark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | mm = 0.001
4 | import openems
5 | import openems.geometries
6 | import numpy as np
7 |
8 | em = openems.OpenEMS('vert_connector_ms_oshpark', EndCriteria = 1e-5, fmin = 0e6, fmax = 50e9,
9 | boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PML_12', 'PEC'])
10 | em.fsteps = 1601
11 | copper = openems.Metal(em, 'copper')
12 | pcopper = openems.Metal(em, 'pcopper')
13 | sub1 = openems.Dielectric(em, 'substrate', eps_r=3.2)
14 | sub2 = openems.Dielectric(em, 'substrate', eps_r=4.0)
15 |
16 | sub1t = 0.19*mm
17 | sub2t = 1.0*mm
18 |
19 | ifoil = 0.0125*mm
20 | ofoil = 0.035*mm
21 | port_length = 0.1*mm
22 | box_length = 2*mm
23 | box_width = 2*mm
24 | ms_width = 0.42*mm
25 | airspace = 1*mm
26 | via_pad = 0.5*mm
27 | via_clearance = 0.275*mm
28 |
29 | bt = sub1t + ofoil
30 | bb = -1*(ofoil+sub2t+sub1t)
31 |
32 | em.resolution = 25e-6
33 |
34 | em.mesh.AddLine('z', sub1t+airspace)
35 | zmin = bb - 1*mm
36 | em.mesh.AddLine('z', zmin)
37 |
38 | planar = openems.geometries.planar_full_box(x=[-0.5*box_length, 0.5*box_length],
39 | y=[-0.5*box_width, 0.5*box_width])
40 |
41 | clearance_r = via_pad*0.5 + via_clearance
42 | planar.add(sub1, [0, sub1t], priority=1) # sub1 top
43 | planar.add_center_hole(pcopper, [0, ifoil], clearance_r, priority=2) # inner 1 foil
44 | planar.add(sub2, [0, -sub2t], priority=1) # sub2
45 | planar.add(sub1, [-sub2t, -(sub2t+sub1t)], priority=1) # sub1 bottom
46 | planar.add_center_hole(pcopper, [-sub2t, -(sub2t+ifoil)], clearance_r, priority=2) # inner2 foil
47 | planar.add_center_hole(pcopper, [bb, bb+ofoil], 0.75*mm, priority=1) # bottom foil
48 |
49 | # ms line
50 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, sub1t])
51 | stop = np.array([0, -0.5*ms_width, bt])
52 | copper.AddBox(start, stop, priority=9)
53 |
54 | # ms port
55 | start = [-0.5*box_length, ms_width/2.0, sub1t]
56 | stop = [-0.5*box_length + port_length, ms_width/-2.0, bt]
57 | openems.Port(em, start, stop, direction='x', z=50)
58 |
59 | via_z = [[bt,bb],[bt, bt-ofoil], [0, ifoil], [-sub2t, -sub2t-ifoil], [bb+ofoil, bb]]
60 |
61 | # ground vias
62 | for n in range(-3,4):
63 | r = 1 * mm
64 | c = np.exp(1j*2*np.pi*n*22.0/180.0) * r
65 | openems.Via(copper, priority=9, x=np.real(c), y=np.imag(c), z=via_z,
66 | drillradius = 0.25*mm*0.5,
67 | wall_thickness = 25e-6,
68 | padradius = via_pad * 0.5,
69 | padname='2')
70 |
71 | # signal via
72 | openems.Via(copper, priority=9, x=0, y=0,
73 | z=[[bt,bb],[bt, bt-ofoil], [0, ifoil], [-sub2t, -sub2t-ifoil], [bb+ofoil, bb]],
74 | drillradius = 0.25*mm*0.5,
75 | wall_thickness = 25e-6,
76 | padradius = via_pad * 0.5,
77 | padname='1')
78 |
79 | # coax shield
80 | planar.add_center_hole(copper, [bb, zmin], r=1.5*mm/2.0, priority=1)
81 |
82 | pin_diameter = 0.695*mm
83 | coax_port_length = 0.2*mm
84 |
85 | # pin
86 | start = np.array([0, 0, bb])
87 | stop = np.array([0, 0, zmin])
88 | copper.AddCylinder(start, stop, 0.5*pin_diameter, priority=9)
89 |
90 | # coax goes into Z- PML
91 |
92 | command = 'view solve'
93 | if len(sys.argv) > 1:
94 | command = sys.argv[1]
95 | em.write_kicad(em.name)
96 | em.run_openems(command)
97 |
--------------------------------------------------------------------------------
/examples/via_1_3_oshpark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from openems import OpenEMS, Box, Cylinder, Via, Port, Metal, Dielectric, geometries
4 | import numpy as np
5 | em = OpenEMS('via_1_3_oshpark', EndCriteria = 1e-4, fmin = 0, fmax = 40e9,
6 | boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'],
7 | fsteps=801)
8 | copper = Metal(em, 'copper')
9 | pcopper = Metal(em, 'pcopper')
10 | sub1 = Dielectric(em, 'substrate', eps_r=3.2)
11 | sub2 = Dielectric(em, 'substrate', eps_r=4)
12 |
13 | sub1t = 0.166e-3
14 | sub2t = 47*25.4e-6
15 | ifoil = 0.0125e-3
16 | ofoil = 0.035e-3
17 | port_length = 0.1e-3
18 | box_length = 5e-3
19 | box_width = 2e-3
20 | sl_width = 0.24e-3
21 | ms_width = 0.35e-3
22 | airspace = 1e-3
23 |
24 | bt = sub1t + ofoil
25 | bb = -1*(ofoil+sub2t+sub1t)
26 |
27 | em.resolution = 50e-6
28 |
29 | planar = geometries.planar_full_box(
30 | x=[-0.5*box_length, 0.5*box_length],
31 | y=[-0.5*box_width, 0.5*box_width])
32 |
33 | clearance_r = 0.86e-3 * 0.5
34 |
35 | em.mesh.AddLine('z', sub1t+airspace)
36 | em.mesh.AddLine('z', -1.0*(2*sub1t+sub2t+airspace))
37 |
38 | planar.add(sub1, [0, sub1t]) # sub1 top
39 | planar.add_center_hole(pcopper, [0, ifoil], clearance_r, priority=2) # inner 1 foil
40 | planar.add(sub2, [0, -sub2t]) # core
41 | planar.add(sub1, [-sub2t, -(sub2t+sub1t)]) # sub1 bot
42 | planar.add_center_hole(pcopper, [bb, -(sub2t+sub1t)], clearance_r, priority=2) # bottom foil
43 |
44 | # line (sl)
45 | start = np.array([0, 0.5*sl_width, -sub2t])
46 | stop = np.array([0.5*box_length-port_length, -0.5*sl_width, -sub2t-ifoil])
47 | Box(copper, 9, start, stop)
48 |
49 | # line (ms)
50 | Box(copper, 9, [-0.5*box_length+port_length, 0.5*ms_width, sub1t], [0, -0.5*ms_width, bt])
51 |
52 | # port (ms)
53 | start = [-0.5*box_length, ms_width/2.0, sub1t]
54 | stop = [-0.5*box_length + port_length, ms_width/-2.0, bt]
55 | Port(em, start, stop, direction='x', z=50)
56 |
57 | # port (sl)
58 | start = [0.5*box_length, sl_width/2.0, -sub2t]
59 | stop = [0.5*box_length - port_length, sl_width/-2.0, -sub2t-ifoil]
60 | Port(em, start, stop, direction='x', z=50)
61 |
62 | Via(copper, priority=9, x=0, y=0,
63 | z=[[bb, bt], [bt, bt-ofoil], [0, ifoil], [-sub2t, -sub2t-ifoil], [bb+ofoil, bb]],
64 | drillradius = 0.5*0.25e-3,
65 | wall_thickness = 25e-6,
66 | padradius = 0.46e-3*0.5,
67 | padname = '1')
68 |
69 | for x in range(-3,4):
70 | x *= 0.5e-3
71 | for y in [-0.75e-3, 0.75e-3]:
72 | Cylinder(copper, 9, [x, y, bb], [x, y, bt], 0.3e-3*0.5)
73 | Cylinder(copper, 9, [x, y, bt], [x, y, bt-ofoil], 0.46e-3*0.5)
74 |
75 | command = 'view solve'
76 | if len(sys.argv) > 1:
77 | command = sys.argv[1]
78 | em.run_openems(command)
79 |
--------------------------------------------------------------------------------
/examples/via_1_4_oshpark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from scipy.constants import pi, c, mil
4 | mm = 0.001
5 | from openems import OpenEMS, Box, Cylinder, Via, Port, Metal, Dielectric, geometries
6 | import numpy as np
7 |
8 | em = OpenEMS('via_1_4_oshpark', EndCriteria = 1e-4, fmin = 0e6, fmax = 40e9,
9 | boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'])
10 | em.fsteps = 1601
11 | copper = Metal(em, 'copper')
12 | pcopper = Metal(em, 'pcopper')
13 | sub1 = Dielectric(em, 'substrate', eps_r=3.2)
14 | sub2 = Dielectric(em, 'substrate', eps_r=4)
15 | air = Dielectric(em, 'substrate', eps_r=1)
16 |
17 | sub1t = 0.166*mm
18 | sub2t = 47*mil
19 |
20 | ifoil = 0.0125*mm
21 | ofoil = 0.035*mm
22 | port_length = 0.1*mm
23 | box_length = 5*mm
24 | box_width = 2*mm
25 | ms_width = 0.35*mm
26 | airspace = 1*mm
27 |
28 | bt = sub1t + ofoil
29 | bb = -1*(ofoil+sub2t+sub1t)
30 |
31 | em.resolution = 50e-6
32 |
33 | planar = geometries.planar_full_box(x=[-0.5*box_length, 0.5*box_length],
34 | y=[-0.5*box_width, 0.5*box_width])
35 |
36 | em.mesh.AddLine('z', sub1t+airspace)
37 | em.mesh.AddLine('z', -1.0*(2*sub1t+sub2t+airspace))
38 |
39 | clearance_r = 0.86e-3 * 0.5
40 |
41 | planar.add(sub1, [0, sub1t]) # sub1 top
42 | planar.add_center_hole(pcopper, [0, ifoil], clearance_r, priority=2) # inner 1 foil
43 | planar.add(sub2, [0, -sub2t]) # core
44 | planar.add_center_hole(pcopper, [-sub2t, -(sub2t+ifoil)], clearance_r, priority=2) # inner 2 foil
45 | planar.add(sub1, [-sub2t, -(sub2t+sub1t)]) # sub1 bot
46 |
47 | # line
48 | start = np.array([0, 0.5*ms_width, bb])
49 | stop = np.array([0.5*box_length-port_length, -0.5*ms_width, bb+ofoil])
50 | Box(copper, 9, start, stop)
51 |
52 | # line
53 | start = np.array([-0.5*box_length+port_length, 0.5*ms_width, sub1t])
54 | stop = np.array([0, -0.5*ms_width, bt])
55 | Box(copper, 9, start, stop)
56 |
57 | # ports
58 | start = [-0.5*box_length, ms_width/2.0, sub1t]
59 | stop = [-0.5*box_length + port_length, ms_width/-2.0, bt]
60 | Port(em, start, stop, direction='x', z=50)
61 | start = [0.5*box_length, ms_width/2.0, bb]
62 | stop = [0.5*box_length - port_length, ms_width/-2.0, bb+ofoil]
63 | Port(em, start, stop, direction='x', z=50)
64 |
65 | Via(copper, priority=9, x=0, y=0,
66 | z=[[bb, bt], [bt, bt-ofoil], [0, ifoil], [-sub2t, -sub2t-ifoil], [bb+ofoil, bb]],
67 | drillradius = 0.5*0.25e-3,
68 | wall_thickness = 25e-6,
69 | padradius = 0.46e-3*0.5,
70 | padname = '1')
71 |
72 | for x in range(-2,3):
73 | for y in [-0.75*mm, 0.75*mm]:
74 | Cylinder(copper, 9, [x*mm, y, bb], [x*mm, y, bt], 0.3*mm*0.5)
75 | Cylinder(copper, 9, [x*mm, y, bt], [x*mm, y, bt-ofoil], 0.46*mm*0.5)
76 | Cylinder(copper, 9, [x*mm, y, bb], [x*mm, y, bb+ofoil], 0.46*mm*0.5)
77 |
78 | command = 'view solve'
79 | if len(sys.argv) > 1:
80 | command = sys.argv[1]
81 | em.run_openems(command)
82 |
--------------------------------------------------------------------------------
/examples/wilkinson_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import openems
4 | from openems import wilkinson
5 |
6 | em = openems.OpenEMS('wilkinson_1', EndCriteria = 1e-4, fmin = 0e6, fmax = 6e9, fsteps=400)
7 |
8 | fc = 1e9
9 | sub = openems.Dielectric(em, 'fr408', eps_r=3.3, tand=0.012, fc=fc)
10 |
11 | foil_thickness = 35e-6
12 | substrate_thickness = 180e-6
13 | ms_air_above = 700e-6
14 | z = [0.0, substrate_thickness, substrate_thickness + foil_thickness]
15 | em.mesh.AddLine('z', z[2] + ms_air_above)
16 |
17 | em.resolution = 50e-6
18 |
19 | wilkinson.generate(
20 | em,
21 | y1 = 5e-3*1.43,
22 | y2 = 500e-6,
23 | r = 200e-6,
24 | rv = [None, None, None, 100],
25 | w = [150e-6]*4,
26 | substrate = sub,
27 | z = z,
28 | port_length = 100e-6,
29 | ms_width = 360e-6)
30 |
31 | em.write_kicad(em.name, mirror = '')
32 | command = 'view solve'
33 | if len(sys.argv) > 1:
34 | command = sys.argv[1]
35 | em.run_openems(command)
36 |
--------------------------------------------------------------------------------
/examples/wilkinson_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import openems
4 | from openems import wilkinson
5 |
6 | em = openems.OpenEMS('wilkinson_2', EndCriteria = 1e-4, fmin = 0e6, fmax = 6e9, fsteps=400)
7 |
8 | fc = 1e9
9 | sub = openems.Dielectric(em, 'fr408', eps_r=3.3, tand=0.012, fc=fc)
10 |
11 | foil_thickness = 35e-6
12 | substrate_thickness = 180e-6
13 | ms_air_above = 700e-6
14 | z = [0.0, substrate_thickness, substrate_thickness + foil_thickness]
15 | em.mesh.AddLine('z', z[2] + ms_air_above)
16 |
17 | em.resolution = 50e-6
18 |
19 | wilkinson.generate(
20 | em,
21 | y1 = 4.8e-3,
22 | y2 = 500e-6,
23 | r = 200e-6,
24 | rv = [None, None, 100],
25 | w = [150e-6]*4,
26 | substrate = sub,
27 | z = z,
28 | port_length = 100e-6,
29 | ms_width = 360e-6)
30 |
31 | em.write_kicad(em.name, mirror = '')
32 | command = 'view solve'
33 | if len(sys.argv) > 1:
34 | command = sys.argv[1]
35 | em.run_openems(command)
36 |
--------------------------------------------------------------------------------
/examples/wilkinson_3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import openems
4 | from openems import wilkinson
5 |
6 | em = openems.OpenEMS('wilkinson_3', EndCriteria = 1e-4, fmin = 0e6, fmax = 6e9, fsteps=400)
7 |
8 | fc = 1e9
9 | sub = openems.Dielectric(em, 'fr408', eps_r=3.3, tand=0.012, fc=fc)
10 |
11 | foil_thickness = 35e-6
12 | substrate_thickness = 180e-6
13 | ms_air_above = 700e-6
14 | z = [0.0, substrate_thickness, substrate_thickness + foil_thickness]
15 | em.mesh.AddLine('z', z[2] + ms_air_above)
16 |
17 | em.resolution = 50e-6
18 |
19 | wilkinson.generate(
20 | em,
21 | y1 = 5e-3,
22 | y2 = 500e-6,
23 | r = 200e-6,
24 | rv = [None, 100],
25 | w = [150e-6]*4,
26 | substrate = sub,
27 | z = z,
28 | port_length = 100e-6,
29 | ms_width = 360e-6)
30 |
31 | em.write_kicad(em.name, mirror = '')
32 | command = 'view solve'
33 | if len(sys.argv) > 1:
34 | command = sys.argv[1]
35 | em.run_openems(command)
36 |
--------------------------------------------------------------------------------
/geometries.py:
--------------------------------------------------------------------------------
1 | from openems import OpenEMS, Box, Cylinder, Via, Port, Metal, Dielectric, geometries, arc, Polygon
2 | import numpy as np
3 |
4 | def smp_connector(em, x, y, z, zmax, coax_port_length = 0.2e-3, pin_diameter=0.85e-3):
5 | copper_shield = Metal(em, 'smp_shield')
6 | copper = Metal(em, 'smp_pin')
7 | lcp = Dielectric(em, 'lcp', eps_r=3.2)
8 |
9 | # shield
10 | outside = np.array([[-2.5,1.0], [-2.5,2.5], [2.5,2.5], [2.5,-2.5], [-2.5,-2.5], [-2.5,-1.0]])*1e-3
11 | angle = np.arcsin(1.0/2.25)
12 | inside = arc(0,0, 4.5e-3 / 2.0, -np.pi + angle, np.pi - angle)
13 | Polygon(copper_shield,
14 | priority=9,
15 | pcb_layer=None,
16 | points = [x,y] + np.concatenate((inside, outside)),
17 | elevation = [z, z + 0.9e-3],
18 | normal_direction = 'z')
19 |
20 | outside = np.array([[0,2.5], [2.5,2.5], [2.5,-2.5], [0,-2.5]])*1e-3
21 | inside = arc(0,0, 1.95e-3 / 2.0, -np.pi*0.5, np.pi*0.5)
22 | for m in ['', 'x']:
23 | Polygon(copper_shield, 9,
24 | pcb_layer=None,
25 | points = [x,y] + np.concatenate((inside, outside)),
26 | elevation = [z + 0.9e-3, zmax],
27 | normal_direction = 'z',
28 | mirror=m)
29 | # pin
30 | start = np.array([x, y, zmax - coax_port_length])
31 | stop = np.array([x, y, z + 0.9e-3])
32 | Cylinder(copper, 9, start, stop, 0.5*pin_diameter)
33 |
34 | # smaller part of pin
35 | Cylinder(copper, 9, start, [x, y, z], 0.5*0.8e-3)
36 |
37 | # insulator
38 | Cylinder(lcp, 1, [x, y, z], [x, y, z + 0.9e-3], 2.25e-3)
39 |
40 | if coax_port_length != 0:
41 | # port (coax)
42 | start = [x + 0.5*coax_port_length, y + 0.5*coax_port_length, zmax - coax_port_length]
43 | stop = [x - 0.5*coax_port_length, y - 0.5*coax_port_length, zmax]
44 | Port(em, start, stop, direction='z', z=50)
45 | em.mesh.AddLine('z', start[2])
46 |
47 | class planar_full_box:
48 | def __init__(self, x, y):
49 | self.x = x
50 | self.y = y
51 | def add(self, sub, z, priority=1):
52 | start = np.array([self.x[0], self.y[0], z[0]])
53 | stop = np.array([self.x[1], self.y[1], z[1]])
54 | sub.AddBox(start, stop, priority=priority)
55 |
56 | # add a plane with a hole in the middle
57 | def add_center_hole(self, sub, z, r, priority=1, pcb_layer = None):
58 | outside = np.array([[0,self.y[1]],
59 | [self.x[1],self.y[1]],
60 | [self.x[1],self.y[0]],
61 | [0,self.y[0]]])
62 | inside = arc(0,0, r, -np.pi*0.5, np.pi*0.5)
63 | for m in ['', 'x']:
64 | Polygon(sub,
65 | priority=priority,
66 | pcb_layer=pcb_layer,
67 | points = np.concatenate((inside, outside)),
68 | elevation = z,
69 | normal_direction = 'z',
70 | mirror = m
71 | )
72 |
--------------------------------------------------------------------------------
/idbpf.py:
--------------------------------------------------------------------------------
1 | mm = 0.001
2 | from openems import OpenEMS, Box, Cylinder, Via, Port, Metal, Dielectric, geometries, mirror
3 | import numpy as np
4 |
5 | def idbpf(
6 | em, # openems instance
7 | sub, # substrate, define with Dielectric()
8 | z = [], # z position, z[0] = sub bottom, z[1] = sub top, z[2] = foil top
9 | lidz = 0, # upper substrate thickness
10 | rl = [], # length of resonator fingers
11 | rw = [], # width of resonator fingers
12 | space = [], # space between resonator fingers
13 | endmetal = True, # add metal to filter ends
14 | portlength = 0.2*mm, # length of the openems port
15 | feedwidth = 0.85*mm, # width of the trace leaving the filter and port
16 | end_gap = 0.3*mm, # space between the end of a resonator and ground
17 | via_radius = 0.15*mm, # radius of the via drills
18 | via_padradius = 0.3*mm, # radius of the via pads
19 | pcb_layer = 'F.Cu', # Kicad layer
20 | mask = None, # mask, define with Dielectric()
21 | mask_thickness = 0, # set to non-zero to enable solder mask over filter
22 | ):
23 | edge_space = 0.5*mm
24 | pec = Metal(em, 'pec_filter')
25 | ring_ix = 0.5 * (np.max(rl) + end_gap)
26 | ring_ox = ring_ix + 2.0 * via_padradius
27 | via_z = [[z[0], z[1]+lidz], [z[1], z[2]]]
28 | # fingers
29 | y = -0.5*space[-1:][0]
30 | mirror = False
31 | mirrorstring = ['', 'x']
32 | for i in range(len(space))[::-1]: # reverse order
33 | x1 = 0.5 * (ring_ox + ring_ix)
34 | x2 = ring_ix - rl[i]
35 | y += space[i]
36 | start = [x1, y, z[1]];
37 | y += rw[i]
38 | stop = [x2, y, z[2]];
39 | box = Box(pec, 9, start, stop, padname = 'poly', pcb_layer=pcb_layer)
40 | box.mirror(mirrorstring[mirror])
41 | mirror = not mirror
42 | box2 = box.duplicate()
43 | box2.mirror('xy')
44 | if i == 0:
45 | continue
46 | v = Via(
47 | pec, priority=2,
48 | x=ring_ix+via_padradius,
49 | y=y-0.5*rw[i], z=via_z,
50 | drillradius=via_radius,
51 | padradius=via_padradius, padname='2')
52 | if not mirror:
53 | v.mirror('x')
54 | v.duplicate().mirror('xy')
55 | mirror = not mirror
56 | # ports
57 | y1 = y + feedwidth - rw[0] # outer edge of feed
58 | y2 = y - rw[0] # inner edge
59 | px = ring_ox
60 | start = [px, y2, z[1]]
61 | stop = [px - portlength, y1, z[2]]
62 | p = Port(em, start, stop, direction='x', z=50)
63 | p.mirror(mirrorstring[mirror])
64 | p2 = p.duplicate().mirror('xy')
65 | # feed lines
66 | start = [-ring_ix + rl[0], y1, z[1]]
67 | stop = [px - portlength, y2, z[2]]
68 | box = Box(pec, 9, start, stop, padname = '1', pcb_layer=pcb_layer)
69 | box.mirror(mirrorstring[mirror])
70 | box2 = box.duplicate()
71 | box2.mirror('xy')
72 | box2.padname = '3'
73 | # substrate
74 | start = np.array([ring_ox, y1 + edge_space, z[0]])
75 | stop = mirror(start, 'xy')
76 | stop[2] = z[1]+lidz
77 | sub = Box(sub, 1, start, stop)
78 | # mask
79 | if mask_thickness > 0.0:
80 | start = np.array([ring_ox, y2, z[1]])
81 | stop = mirror(start, 'xy')
82 | stop[2] += mask_thickness
83 | Box(mask, 1, start, stop)
84 | # grounded end metal
85 | if endmetal:
86 | for m in ['', 'xy']:
87 | Box(pec, 9,
88 | start = [ring_ix, -y2+space[0], z[1]],
89 | stop = [ring_ix + 2.0*via_padradius, y1+edge_space, z[2]],
90 | padname = '2', pcb_layer=pcb_layer,
91 | mirror=m)
92 |
--------------------------------------------------------------------------------
/idbpf_tapped.py:
--------------------------------------------------------------------------------
1 | mm = 0.001
2 | import openems
3 | import numpy as np
4 |
5 | def idbpf(em, # openems instance
6 | sub, # substrate, define with openems.Dielectric()
7 | lidz = 0.0,
8 | z = [], # z position, z[0] = sub bottom, z[1] = sub top, z[2] = foil top
9 | space = [], # space between resonator fingers
10 | rl = [], # length of resonator fingers
11 | rw = [], # width of resonator fingers
12 | portlength = 0.2*mm, # length of the openems port
13 | feedwidth = 0.85*mm, # width of the trace leaving the filter and port
14 | tapoffset = 1.0*mm, # offset of tap from end of resonator
15 | end_gap = 0.3*mm, # space between the end of a resonator and ground
16 | via_radius = 0.15*mm, # radius of the via drills
17 | via_padradius = 0.3*mm, # radius of the via pads
18 | pcb_layer = 'F.Cu', # Kicad layer
19 | mask_thickness = 0, # set to non-zero to enable solder mask over filter
20 | mask = None, # mask, define with openems.Dielectric()
21 | endmetal = True, # add metal to filter ends
22 | ):
23 | pec = openems.Metal(em, 'pec_filter')
24 | ring_y_width = via_padradius * 2.0
25 | ring_ix = 0.5 * (np.max(rl) + end_gap)
26 | ring_ox = ring_ix + 2.0 * via_padradius
27 | via_z = [[z[0], z[1]+lidz], [z[1], z[2]]]
28 | # fingers
29 | y = -0.5*space[-1:][0]
30 | mirror = np.array([1,1,1])
31 | if len(space) % 2 == 0:
32 | mirror[0] *= -1
33 | for i in range(len(space))[::-1]: # reverse order
34 | x1 = 0.5 * (ring_ox + ring_ix)
35 | x2 = ring_ix - rl[i]
36 | y += space[i]
37 | start = [x1, y, z[1]];
38 | y += rw[i]
39 | stop = [x2, y, z[2]];
40 | for m in [np.array([1,1,1]),np.array([-1,-1,1])]:
41 | m *= mirror
42 | openems.Box(pec, 9, start*m, stop*m, padname = 'poly', pcb_layer=pcb_layer)
43 | openems.Via(pec, priority=2,
44 | x=m[0]*(ring_ix+via_padradius),
45 | y=m[1]*(y-0.5*rw[i]),
46 | z=via_z,
47 | drillradius=via_radius,
48 | padradius=via_padradius, padname='2')
49 | mirror[0] *= -1
50 |
51 | if endmetal:
52 | v.duplicate().mirror('y')
53 | v.duplicate().mirror('x')
54 |
55 | mirror *= [-1,1,1]
56 | # ports
57 | y1 = y
58 | y -= 0.5*rw[0] # center of line
59 | y2 = y + 0.5*feedwidth + ring_y_width # y outside
60 | px = ring_ix - tapoffset
61 | py = y2 - portlength
62 | for m in [-1,1]:
63 | start = [m*(px + 0.5*feedwidth), m*y2, z[1]]
64 | stop = [m*(px - 0.5*feedwidth), m*py, z[2]]
65 | openems.Port(em, start, stop, direction='y', z=50)
66 | # feed lines
67 | start = [m*(px + 0.5*feedwidth), m*py, z[1]]
68 | stop = [m*(px - 0.5*feedwidth), m*(y-0.5*rw[0]), z[2]]
69 | openems.Box(pec, 9, start, stop, padname = '1' if m==1 else '3',
70 | pcb_layer=pcb_layer)
71 | # substrate
72 | start = np.array([ring_ox, y2, z[0]])
73 | stop = openems.mirror(start, 'xy')
74 | stop[2] = z[1]+lidz
75 | sub = openems.Box(sub, 1, start, stop)
76 | # mask
77 | if mask_thickness > 0.0:
78 | start = np.array([ring_ox, y2, z[1]])
79 | stop = openems.mirror(start, 'xy')
80 | stop[2] += mask_thickness
81 | openems.Box(mask, 1, start, stop)
82 | # grounded end metal
83 | if endmetal:
84 | for xm in [-1,1]:
85 | em1 = openems.Box(pec, 9, start = [xm*ring_ix, y2, z[1]],
86 | stop = [xm*(ring_ix + 2.0*via_padradius), -y2, z[2]],
87 | padname = '2', pcb_layer=pcb_layer)
88 |
--------------------------------------------------------------------------------
/kicadfpwriter.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2005-2019 Darrell Harmon, David Carr, Peter Baxendale, Stephen Ecob, Larry Doolittle
2 | # GPLv3+
3 |
4 | class Generator():
5 | def __init__(self, part):
6 | self.mirror = ""
7 | self.fp = "(module {} (layer F.Cu)\n".format(part)
8 | self.fp += " (at 0 0)\n"
9 | self.fp += " (attr smd)"
10 | self.fp += " (fp_text reference U1 (at 0 -1.27) (layer F.SilkS) hide (effects (font (size 0.7 0.7) (thickness 0.127))))\n"
11 | self.fp += " (fp_text value value (at 0 1.27) (layer F.SilkS) hide (effects (font (size 0.7 0.7) (thickness 0.127))))\n"
12 |
13 | # mm, degrees
14 | def add_pad(self,
15 | name,
16 | x,
17 | y,
18 | xsize=None,
19 | ysize=None,
20 | diameter=None,
21 | drill = 0,
22 | layer='F.Cu',
23 | mirror = "",
24 | shape="rect"):
25 | if "x" in self.mirror:
26 | x *= -1.0
27 | if "y" in self.mirror:
28 | y *= -1.0
29 | if "cir" in shape:
30 | shape = "circle"
31 | xsize = diameter
32 | ysize = diameter
33 | if "round" in shape:
34 | shape = "oval"
35 | atstring = "(at {:.6f} {:.6f})".format(x, y)
36 | padtype = "smd"
37 | layers = " (layers {})".format(layer)
38 | drillstring = ""
39 | if drill > 0:
40 | drillstring = " (drill {:.6f})".format(drill)
41 | padtype = "thru_hole"
42 | layers = " (layers *.Cu)"
43 | self.fp += " (pad {} {} {} {} (size {:.6f} {:.6f}){}".format(
44 | name, padtype, shape, atstring, xsize, ysize, drillstring)
45 | self.fp += layers
46 | self.fp += ")\n"
47 |
48 | def add_polygon(self, points, layer="F.Cu", width = 0.0):
49 | polystring = "(fp_poly (pts\n"
50 | for p in points:
51 | if "x" in self.mirror:
52 | p[0] *= -1.0
53 | if "y" in self.mirror:
54 | p[1] *= -1.0
55 | polystring += "\t(xy {:.6f} {:.6f})\n".format(p[0], p[1])
56 | polystring += ") (layer {}) (width {:.6f}) )\n".format(layer, width)
57 | self.fp += polystring
58 |
59 | def add_custom_pad(self, name, x, y, polygons, layer="F.Cu"):
60 | self.fp += "(pad {} connect custom (at {} {}) (size 0.1 0.1) (layers {})\n".format(
61 | name, x, y, layer)
62 | self.fp += "(options (clearance outline) (anchor circle))\n"
63 | self.fp += "(primitives\n"
64 | for polygon in polygons:
65 | polygon -= [x,y]
66 | self.fp += "(gr_poly (pts\n"
67 | for p in polygon:
68 | if "x" in self.mirror:
69 | p[0] *= -1.0
70 | if "y" in self.mirror:
71 | p[1] *= -1.0
72 | self.fp += "\t(xy {:.6f} {:.6f})\n".format(p[0], p[1])
73 | self.fp += ") (width 0))\n"
74 | self.fp += "))\n"
75 |
76 | def finish(self):
77 | self.fp += ")\n"
78 | return self.fp
79 |
--------------------------------------------------------------------------------
/miter.py:
--------------------------------------------------------------------------------
1 | from scipy.constants import pi, c, epsilon_0, mu_0, mil, inch
2 | mm = 0.001
3 | import openems
4 | import numpy as np
5 |
6 | def generate(em, metal, substrate, miter, z, port_length, ms_width, box_size, priority = 9):
7 | d1 = 0.5 * box_size
8 | d2 = d1 - port_length
9 | d3 = d2 - 0.2*mm
10 | d4 = -0.5*ms_width + miter
11 |
12 | # substrate
13 | start = np.array([ 0.5*box_size, 0.5*box_size, z[0]])
14 | stop = np.array([-0.5*box_size, -0.5*box_size, z[1]])
15 | openems.Box(substrate, 1, start, stop);
16 |
17 | # port pads
18 | start = np.array([ 0.5*ms_width, d2, z[1]])
19 | stop = np.array([-0.5*ms_width, d3, z[2]])
20 | openems.Box(metal, priority, start, stop, padname = '1')
21 | start = np.array([d2, 0.5*ms_width, z[1]])
22 | stop = np.array([d3, -0.5*ms_width, z[2]])
23 | openems.Box(metal, priority, start, stop, padname = '2')
24 |
25 | # line
26 | openems.Polygon(metal,
27 | priority = priority,
28 | points = np.array([[-0.5*ms_width, d2],
29 | [ 0.5*ms_width, d2],
30 | [ 0.5*ms_width, 0.5*ms_width],
31 | [ d2, 0.5*ms_width],
32 | [ d2, -0.5*ms_width],
33 | [ d4, -0.5*ms_width],
34 | [ -0.5*ms_width, d4]]),
35 | elevation = z[1:],
36 | normal_direction = 'z',
37 | pcb_layer = 'F.Cu',
38 | pcb_width = 0.001)
39 |
40 | # ports
41 | start = np.array([ 0.5*ms_width, d1, z[1]])
42 | stop = np.array([-0.5*ms_width, d2, z[2]])
43 | openems.Port(em, start, stop, direction='y', z=50)
44 | start = np.array([d1, 0.5*ms_width, z[1]])
45 | stop = np.array([d2, -0.5*ms_width, z[2]])
46 | openems.Port(em, start, stop, direction='x', z=50)
47 |
--------------------------------------------------------------------------------
/polygon.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import openems
3 |
4 | class Polygon(openems.Object):
5 | def __init__(self,
6 | material,
7 | points,
8 | elevation,
9 | priority=1,
10 | normal_direction = 'z',
11 | pcb_layer = 'F.Cu',
12 | mirror = '',
13 | **kwargs
14 | ):
15 | """
16 | Add a polygon, generally assumed to be in xy plane, but can be changed to yz or xz
17 | name: any unique name
18 | material: the name of a previously defined material
19 | priority: in the case of overlapping materials, the one with higher priority will be used
20 | points: pairs of points (xy, yz or xz) [[x1, y1], [x2, y2], ...]
21 | elevation: start and stop points in the normal direction
22 | normal_direction: optional, default = z, direction normal to the polygon - 'x', 'y' or 'z'
23 | """
24 | self.points = np.array(points)
25 | if mirror != '':
26 | assert(normal_direction == 'z')
27 | if 'x' in mirror:
28 | self.points[:,0] *= -1.0
29 | if 'y' in mirror:
30 | self.points[:,1] *= -1.0
31 | self.priority = priority
32 | self.material = material
33 | self.elevation = elevation
34 | self.pcb_layer = pcb_layer
35 | self.normal_direction = normal_direction
36 | self.em = material.em
37 | name = self.em.get_name(None)
38 | self.em.objects[name] = self
39 | self.kwargs = kwargs
40 |
41 | def generate_kicad(self, g):
42 | if self.material.__class__.__name__ == 'Dielectric':
43 | return
44 | if self.pcb_layer == None:
45 | return
46 | if 'is_custom_pad' in self.kwargs.keys():
47 | name = self.kwargs["pad_name"]
48 | x = self.kwargs["x"]*1000.0
49 | y = self.kwargs["y"]*1000.0
50 | g.add_custom_pad(name, x, y, [1000.0*self.points], layer=self.pcb_layer)
51 | else:
52 | g.add_polygon(points = 1000.0 * self.points, layer = self.pcb_layer, width=0)
53 |
54 | def generate_octave(self):
55 | height = self.elevation[1] - self.elevation[0]
56 | self.material.material.AddLinPoly(np.swapaxes(self.points, 0, 1),
57 | self.normal_direction,
58 | self.elevation[0],
59 | height,
60 | priority=self.priority)
61 |
--------------------------------------------------------------------------------
/ratrace_folded.py:
--------------------------------------------------------------------------------
1 | mm = 0.001
2 | import openems
3 | import numpy as np
4 | pi = np.pi
5 | cat = np.concatenate
6 | arc = openems.arc
7 |
8 | def ratrace(
9 | metal, substrate,
10 | z, # [bottom of substrate, top of substrate, top of metal]
11 | port_length,
12 | ms_width, priority = 9,
13 | lq=8.9*mm, w=0.175*mm, r1=0.7*mm, r2 = 0.7*mm, kicad=False, loopr=1.0*mm):
14 | em = metal.em
15 | ms_width = ms_width
16 | endspace = 0.5 * mm
17 |
18 | feedx = -lq + -r2 + pi*r2/2.0
19 | arcx = feedx - lq + (r1+r2)*pi/2
20 | endspace = ms_width + port_length if kicad else endspace
21 |
22 | def addy_sym(y):
23 | em.mesh.AddLine('y',y)
24 | em.mesh.AddLine('y',-y)
25 |
26 | addy_sym(r1 - 0.5*w)
27 | addy_sym(r1 + 0.5*w)
28 | addy_sym(r1+r2 - 0.5*w)
29 | addy_sym(r1+r2 + 0.5*w)
30 |
31 | # substrate
32 | start = np.array([endspace, r1+r2+endspace, z[0]])
33 | stop = np.array([arcx - r1 - r2 - endspace, -1.0*start[1], z[1]])
34 | substrate.AddBox(start, stop, 1)
35 |
36 | # outer loop
37 | c1 = arcx
38 | lo = cat((
39 | arc(-1.0*r2, r1, r2+0.5*w, 0*pi, 0.5*pi),
40 | arc(c1, 0, r1+r2+0.5*w, 0.5*pi, 1.5*pi),
41 | arc(-1.0*r2, -r1, r2+0.5*w, 1.5*pi, 2*pi),
42 | ))
43 | li = cat((
44 | arc(-1.0*r2, r1, r2-0.5*w, 0*pi, 0.5*pi),
45 | arc(c1, 0, r1+r2-0.5*w, 0.5*pi, 1.5*pi),
46 | arc(-1.0*r2, -r1, r2-0.5*w, 1.5*pi, 2*pi),
47 | ))
48 | l = cat((lo, li[::-1]))
49 | metal.AddPolygon(
50 | priority = priority,
51 | points = l,
52 | elevation = z[1:],
53 | normal_direction = 'z',
54 | pcb_layer = 'F.Cu',
55 | pcb_width = 0.001)
56 |
57 | # inner loop
58 | lo = arc(np.pi*r1/2.0 - lq, 0, r1+0.5*w, 0.5*pi, 1.5*pi)
59 | li = arc(np.pi*r1/2.0 - lq, 0, r1-0.5*w, 0.5*pi, 1.5*pi)
60 | lo = cat((lo, [[0.5*w, -(r1+0.5*w)], [0.5*w, -(r1-0.5*w)]]))
61 | li = cat(([[0.5*w, r1+0.5*w], [0.5*w, r1-0.5*w]], li))
62 | l = cat((lo, li[::-1]))
63 | metal.AddPolygon(
64 | priority = priority,
65 | points = l,
66 | elevation = z[1:],
67 | normal_direction = 'z',
68 | pcb_layer = 'F.Cu',
69 | pcb_width = 0.001)
70 |
71 | # delta port
72 | start = [feedx + 0.5*ms_width, -1.0*(r1+r2)-endspace, z[1]]
73 | stop = [start[0] - ms_width, start[1] + port_length, z[2]]
74 | openems.Port(em, start, stop, direction='y', z=50)
75 |
76 | # delta line
77 | start[1] = -1.0*(r1+r2)
78 | metal.AddBox(start, stop, priority, padname = '1')
79 |
80 | # 0/180 ports
81 | start = [endspace, r1-0.5*ms_width, z[1]]
82 | stop = [endspace - port_length, r1+0.5*ms_width, z[2]]
83 | for m in ['', 'y']:
84 | openems.Port(em, start, stop, direction='x', z=50, mirror = m)
85 |
86 | # 0/180 lines
87 | start[0] = 0
88 | metal.AddBox(start, stop, priority, padname = '3')
89 | metal.AddBox(start, stop, priority, padname = '4', mirror='y')
90 |
91 | # sum port
92 | portx1 = feedx - 0.5*mm
93 | portx2 = portx1 - port_length
94 | start = [portx1, -0.5*ms_width, z[1]]
95 | stop = [portx2, 0.5*ms_width, z[2]]
96 | openems.Port(em, start, stop, direction='x', z=50)
97 |
98 | # sum line
99 | start[0] = feedx
100 | stop[0] = portx1
101 | metal.AddBox(start, stop, priority, padname = '2')
102 |
103 | # sum ground
104 | start[0] = portx2
105 | stop[0] = portx2 - 0.5*mm
106 | start[2] = 0
107 | if not kicad:
108 | metal.AddBox(start, stop, priority)
109 |
110 | # ground loops
111 | if kicad:
112 | r = loopr
113 | y1 = r1+r
114 | x = -lq + pi*r
115 | w = ms_width
116 | l = cat((
117 | arc(0, y1, r+0.5*w, -0.5*pi, 0.5*pi),
118 | [[x, y1+r+0.5*w], [x, y1+r-0.5*w]],
119 | arc(0, y1, r-0.5*w, 0.5*pi, -0.5*pi),
120 | ))
121 | for m in ['', 'y']:
122 | metal.AddPolygon(
123 | priority = priority,
124 | points = l,
125 | elevation = z[1:],
126 | normal_direction = 'z',
127 | pcb_layer = 'F.Cu',
128 | pcb_width = 0.001,
129 | mirror=m
130 | )
131 | openems.Via(metal, priority=2, x=x, y=y1+r, z=[[0, z[2]], [z[1], z[2]]],
132 | drillradius=0.1*mm,
133 | padradius=0.225*mm, padname='5',
134 | mirror=m)
135 |
--------------------------------------------------------------------------------
/wilkinson.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | mm = 0.001
3 | import openems
4 | import numpy as np
5 | import math
6 |
7 | pi = math.pi
8 | cat = np.concatenate
9 | arc = openems.arc
10 |
11 | def generate(em,
12 | substrate,
13 | z, # [bottom of substrate, top of substrate, top of metal]
14 | y1 = 5.5*mm,
15 | y2 = 0.9*mm,
16 | r = 0.5 * mm,
17 | rv = [200, 200, 200, 200],
18 | w = [0.127*mm, 0.152*mm, 0.19*mm, 0.23*mm],
19 | port_length = 0.2*mm,
20 | ms_width = 0.36*mm,
21 | endspace = 0.5 * mm,
22 | resistor_size='0402'
23 | ):
24 | alumina = openems.Dielectric(em, 'alumina', eps_r=9.8)
25 | metal = openems.Metal(em, 'pec_wilkinson')
26 | n = len(rv)
27 |
28 | # substrate
29 | start = np.array([(n*4 - 1)*r+endspace, y1+r+endspace, z[0]])
30 | stop = np.array([-1.0*endspace, -1.0*start[1], z[1]])
31 | substrate.AddBox(start, stop, 1)
32 |
33 | # common port line (pad 1)
34 | start = np.array([-1.0*endspace + port_length, 0.5*ms_width, z[1]])
35 | stop = np.array([0, -0.5*ms_width, z[2]])
36 | metal.AddBox(start, stop, priority=9, padname = '1')
37 |
38 | lo = arc(r, -y1, r+0.5*w[0], pi, 2*pi, 16)
39 | li = arc(r, -y1, r-0.5*w[0], pi, 2*pi, 16)
40 | lo = cat((lo, arc(3*r, -y2, r-0.5*w[0], pi, 0.55*pi, 16)))
41 | li = cat((li, arc(3*r, -y2, r+0.5*w[0], pi, 0.55*pi, 16)))
42 | for i in range(n-1):
43 | x = (4*i+3)*r
44 | w2 = w[1+i]
45 | em.mesh.AddLine('x', x + r - 0.5*w2)
46 | em.mesh.AddLine('x', x + r + 0.5*w2)
47 | em.mesh.AddLine('x', x + 3*r - 0.5*w2)
48 | em.mesh.AddLine('x', x + 3*r + 0.5*w2)
49 | lo = cat((lo, arc(x, -y2, r-0.5*w2, 0.45*pi, 0, 16)))
50 | li = cat((li, arc(x, -y2, r+0.5*w2, 0.45*pi, 0, 16)))
51 | lo = cat((lo, arc(2*r + x, -y1, r+0.5*w2, pi, 2*pi, 16)))
52 | li = cat((li, arc(2*r + x, -y1, r-0.5*w2, pi, 2*pi, 16)))
53 | lo = cat((lo, arc(4*r + x, -y2, r-0.5*w2, pi, 0.55*pi, 16)))
54 | li = cat((li, arc(4*r + x, -y2, r+0.5*w2, pi, 0.55*pi, 16)))
55 | l = cat((lo, li[::-1]))
56 | l = cat((l[::-1]*[1,-1], l))
57 | openems.Polygon(metal,
58 | priority = 9,
59 | points = l,
60 | elevation = z[1:],
61 | normal_direction = 'z',
62 | pcb_layer = 'F.Cu',
63 | pcb_width = 0.001)
64 |
65 | # x at 2 port side
66 | x0 = (n*4 - 1) * r + endspace
67 | x1 = x0 - port_length
68 |
69 | # output lines
70 | for (ym,padname) in [(1,2),(-1,3)]:
71 | start = np.array([x1, ym*0.2*mm, z[1]])
72 | stop = np.array([(n*4 - 1)*r-0.1*mm, ym*(0.2*mm+ms_width), z[2]])
73 | lp2 = metal.AddBox(start, stop, priority=9, padname = padname)
74 |
75 | # main line port
76 | start = [-1.0*endspace, -0.5*ms_width, z[1]]
77 | stop = [-1.0*endspace + port_length, 0.5*ms_width, z[2]]
78 | openems.Port(em, start, stop, direction='x', z=50)
79 |
80 | # coupled line ports
81 | for ym in [-1,1]:
82 | start = [x0 - port_length, ym*0.2*mm, z[1]]
83 | stop = [x0, ym*(0.2*mm+ms_width), z[2]]
84 | openems.Port(em, start, stop, direction='x', z=50)
85 |
86 | for i in range(n):
87 | if not rv[i]:
88 | continue
89 | em.add_resistor('r{}'.format(i),
90 | origin=np.array([4*r*i + 3*r,0,z[2]]),
91 | direction='y',
92 | value=rv[i], invert=False, priority=9, dielectric=alumina,
93 | metal=metal,
94 | element_down=False,
95 | size = resistor_size)
96 |
--------------------------------------------------------------------------------