pax_global_header 0000666 0000000 0000000 00000000064 13140600222 0014500 g ustar 00root root 0000000 0000000 52 comment=39cfcf2c224ebf66fa2170f636c4589e51a3e072
PySpice-v0.4.1/ 0000775 0000000 0000000 00000000000 13140600222 0013244 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/.bzrignore 0000664 0000000 0000000 00000000345 13140600222 0015250 0 ustar 00root root 0000000 0000000 *.stderr
*.stdout
.git/
.git-backup/
MANIFEST
__pycache__
#
.ipynb_checkpoints/
circuit-macros-tmp/
dependencies/
doc/extern/
doc/sphinx/build/
doc/sphinx/source/bower/bower_components/
notebook-test/
notebook-test/
notes/
test/
PySpice-v0.4.1/.gitignore 0000664 0000000 0000000 00000002453 13140600222 0015240 0 ustar 00root root 0000000 0000000 *~
__pycache__
*.stderr
*.stdout
*/tex/auto
#
####################################################################################################
#
MANIFEST
#
####################################################################################################
#
build/
dist/
doc/sphinx/build/
doc/sphinx/source/api/
doc/sphinx/source/examples/
#
####################################################################################################
#
open-doc.sh
rsync-vps.sh
tools/upload-to-pypi
tools/upload-www
#
####################################################################################################
#
.ipynb_checkpoints/
bower/bower_components/
du.log
example-test/
#
####################################################################################################
#
doc/sphinx/source/themes/sphinx_rtd_theme
doc/sphinx/source/themes/sphinx_rtd_theme-dev
doc/sphinx/source/themes/sphinx_rtd_theme-venv
ngspice-shared/sharedspice.h
unit-test/test.py
#
circuit-macros-tmp/
doc/extern/
examples/diode/tex/auto/
examples/spice-parser/kicad-pyspice-example/kicad-pyspice-example.bak
gh-pages-error.txt
issues/warning-example/
ngspice-shared/log
notebook-test/
notes/
ressources/
spice-examples/mosfet-characterization.cir
spice-examples/ring-modulator.cir
test/
theme-screenshots/
tmp/
unit-test-errors.txt
PySpice-v0.4.1/.travis.yml 0000664 0000000 0000000 00000000766 13140600222 0015366 0 ustar 00root root 0000000 0000000 language: python
python:
- "3.4"
env:
before_script:
install:
- pip install -q -r requirements.txt
- pip install .
script:
# fixme:
- python unit-test/Spice/test_BasicElement.py
# - python unit-test/Spice/test_NetList.py # fail
python unit-test/Unit/test_Units.py
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.4.1/CHANGES.txt 0000664 0000000 0000000 00000000045 13140600222 0015054 0 ustar 00root root 0000000 0000000 v1.0, 08/08/2014 -- Initial release.
PySpice-v0.4.1/GPL-V3.0.txt 0000664 0000000 0000000 00000104513 13140600222 0015057 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
PySpice-v0.4.1/LICENSE.txt 0000664 0000000 0000000 00000104513 13140600222 0015073 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
PySpice-v0.4.1/MANIFEST.in 0000664 0000000 0000000 00000001747 13140600222 0015013 0 ustar 00root root 0000000 0000000 include .bzrignore
include .gitignore
include .pyflymakerc
include CHANGES.txt
include GPL-V3.0.txt
include LICENSE.txt
include MANIFEST.in
include README.html
include README.rst
include README.txt
include TODO.rst
include make-release.sh
include pylintrc.ini
include setenv.sh start.sh
include setup_data.py
include requirements.txt requirements-dev.txt
include .travis.yml
include bin/cir2py
include bower/bower.json
recursive-include PySpice *.py *.yml *.h *py.in
recursive-include doc *
recursive-include doc/sphinx/source/_static *
recursive-include doc/sphinx/source/_templates *
recursive-include examples *
recursive-include gh-pages *
recursive-include spec *.spec *.desktop
recursive-include tools *
recursive-include unit-test *.py
recursive-include spice-examples *
recursive-include issues *.py *.lib
global-exclude *~
global-exclude *__pycache__*
global-exclude *.pyc
global-exclude *.pdf
prune build
prune doc/sphinx/build
prune doc/sphinx/source/api
prune doc/sphinx/source/examples
PySpice-v0.4.1/PySpice/ 0000775 0000000 0000000 00000000000 13140600222 0014620 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Config/ 0000775 0000000 0000000 00000000000 13140600222 0016025 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Config/ConfigInstall.py 0000664 0000000 0000000 00000001645 13140600222 0021141 0 ustar 00root root 0000000 0000000 ####################################################################################################
import os
####################################################################################################
import PySpice.Tools.Path as PathTools # Fixme: why ?
####################################################################################################
_this_file = PathTools.to_absolute_path(__file__)
class Path:
pyspice_module_directory = PathTools.parent_directory_of(_this_file, step=2)
config_directory = os.path.dirname(_this_file)
####################################################################################################
class Logging:
default_config_file = 'logging.yml'
directories = (Path.config_directory,)
##############################################
@staticmethod
def find(config_file):
return PathTools.find(config_file, Logging.directories)
PySpice-v0.4.1/PySpice/Config/__init__.py 0000664 0000000 0000000 00000001627 13140600222 0020144 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/PySpice/Config/logging.yml 0000664 0000000 0000000 00000002647 13140600222 0020207 0 ustar 00root root 0000000 0000000 ####################################################################################################
version: 1
####################################################################################################
formatters:
simple:
format: '%(asctime)s - %(name)s - %(module)s.%(levelname)s - %(message)s'
ansi:
# RESET_SEQ = "\033[0m"
# COLOR_SEQ = "\033[1;%dm"
# BOLD_SEQ = "\033[1m"
format: '[1;32m%(asctime)s[0m - [1;34m%(name)s.%(funcName)s[0m - [1;31m%(levelname)s[0m - %(message)s'
####################################################################################################
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: ansi
stream: ext://sys.stdout
####################################################################################################
root:
level: DEBUG
#level: INFO
#level: WARNING
handlers: [console]
####################################################################################################
# loggers:
# ##################################################
# #
# # Modules
# #
# PySpice:
# level: DEBUG
# #level: INFO
# #level: WARNING
# handlers: [console]
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.4.1/PySpice/Logging/ 0000775 0000000 0000000 00000000000 13140600222 0016206 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Logging/Logging.py 0000664 0000000 0000000 00000004043 13140600222 0020147 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import yaml
import logging
import logging.config
import os
####################################################################################################
import PySpice.Config.ConfigInstall as ConfigInstall
####################################################################################################
def setup_logging(application_name='PySpice',
config_file=ConfigInstall.Logging.default_config_file):
logging_config_file_name = ConfigInstall.Logging.find(config_file)
logging_config = yaml.load(open(logging_config_file_name, 'r'))
# Fixme: \033 is not interpreted in YAML
formatter_config = logging_config['formatters']['ansi']['format']
logging_config['formatters']['ansi']['format'] = formatter_config.replace('', '\033')
logging.config.dictConfig(logging_config)
logger = logging.getLogger(application_name)
if 'PySpiceLogLevel' in os.environ:
numeric_level = getattr(logging, os.environ['PySpiceLogLevel'], None)
logger.setLevel(numeric_level)
return logger
PySpice-v0.4.1/PySpice/Logging/__init__.py 0000664 0000000 0000000 00000001627 13140600222 0020325 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/PySpice/Math/ 0000775 0000000 0000000 00000000000 13140600222 0015511 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Math/Calculus.py 0000664 0000000 0000000 00000014730 13140600222 0017643 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This module provides algorithms to compute the derivative of a function sampled on an uniform
grid.
"""
####################################################################################################
import fractions
import numpy as np
####################################################################################################
from PySpice.Math import odd
####################################################################################################
def compute_exact_finite_difference_coefficients(derivative_order, grid, x0=0):
"""This function compute the finite difference coefficients for the given derivative order and
grid. The parameter *x* specifies where is computed the derivative on the grid. The grid is
given as a list of integer offsets.
The algorithm is derived from the article: Generation of Finite Difference Formulas on Arbitrary Space
Grids, Bengt Fornberg, Mathematics of computation, volume 51, number 184, october 1988
"""
N = len(grid)
# d[m,n,v]
d = [[[0
for v in range(N)]
for n in range(N)]
for m in range(derivative_order +1)]
d[0][0][0] = fractions.Fraction(1,1)
c1 = 1
for n in range(1, N):
c2 = 1
for v in range(n):
c3 = grid[n] - grid[v]
c2 *= c3
if n <= derivative_order:
d[n][n-1][v] = 0
for m in range(min(n, derivative_order) +1):
d[m][n][v] = ( (grid[n] - x0)*d[m][n-1][v] - m*d[m-1][n-1][v] ) / c3
for m in range(min(n, derivative_order) +1):
d[m][n][n] = fractions.Fraction(c1,c2)*( m*d[m-1][n-1][n-1] - (grid[n-1] - x0)*d[m][n-1][n-1] )
c1 = c2
return d[-1][-1]
####################################################################################################
def compute_finite_difference_coefficients(derivative_order, grid):
return [float(x) for x in compute_exact_finite_difference_coefficients(derivative_order, grid)]
####################################################################################################
_coefficient_cache = dict(centred={}, forward={}, backward={})
def get_finite_difference_coefficients(derivative_order, accuracy_order, grid_type):
if derivative_order < 1:
raise ValueError("Wrong derivative order")
if odd(accuracy_order) or accuracy_order < 2:
raise ValueError("Wrong accuracy order")
if grid_type == 'centred':
window_size = accuracy_order / 2
grid = list(range(-window_size, window_size +1))
elif grid_type == 'forward':
grid = list(range(derivative_order + accuracy_order))
elif grid_type == 'backward':
grid = list(range(-(derivative_order + accuracy_order) +1, 1))
grid = list(reversed(grid)) # Fixme: why ?
else:
raise ValueError("Wrong grid type")
key = '{}-{}'.format(derivative_order, accuracy_order)
coefficients = _coefficient_cache[grid_type].get(key, None)
if coefficients is None:
coefficients = compute_finite_difference_coefficients(derivative_order, grid)
_coefficient_cache[grid_type][key] = coefficients
return grid, coefficients
####################################################################################################
def simple_derivative(x, values):
""" Compute the derivative as a simple slope. """
return x[:-1], np.diff(values)/np.diff(x)
####################################################################################################
def derivative(x, values, derivative_order=1, accuracy_order=4):
"""Compute the derivative at the given derivative order and accuracy order. The precision of the
Taylor expansion is :math:`\mathcal{O}(dx^{accuracy})`.
"""
dx = np.diff(x)
# if not np.all(dx == dx[0]):
# raise ValueError("Sampling is not uniform")
dx = dx[0]
values_size, = values.shape
derivative = np.zeros(values_size, dtype=values.dtype)
grid, coefficients = get_finite_difference_coefficients(derivative_order, accuracy_order, 'centred')
window_size = grid[-1]
# print grid, coefficients
vector_size = values_size - 2*window_size
if not vector_size:
raise ValueError("The size of the value's array is not sufficient for the given accuracy order")
lower_index = window_size
upper_index = values_size - window_size
derivative_view = derivative[window_size:-window_size]
for offset, coefficient in zip(grid, coefficients):
if coefficient:
# print offset, lower_index + offset, upper_index + offset
derivative_view += values[lower_index + offset:upper_index + offset] * coefficient
grid, coefficients = get_finite_difference_coefficients(derivative_order, accuracy_order, 'forward')
# print grid, coefficients
grid_size = len(grid)
upper_index = window_size
derivative_view = derivative[:window_size]
for offset, coefficient in zip(grid, coefficients):
# print offset, offset, window_size+offset
derivative_view += values[offset:upper_index + offset] * coefficient
grid, coefficients = get_finite_difference_coefficients(derivative_order, accuracy_order, 'backward')
# print grid, coefficients
grid_size = len(grid)
lower_index = values_size - window_size
upper_index = values_size
derivative_view = derivative[-window_size:]
for offset, coefficient in zip(grid, coefficients):
# print offset, lower_index + offset, upper_index + offset
derivative_view += values[lower_index + offset:upper_index + offset] * coefficient
return derivative / dx**derivative_order
PySpice-v0.4.1/PySpice/Math/__init__.py 0000664 0000000 0000000 00000002605 13140600222 0017625 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import math
####################################################################################################
def odd(x):
return x & 1
def even(x):
return not(odd(x))
####################################################################################################
def rms_to_amplitude(x):
return x * math.sqrt(2)
def amplitude_to_rms(x):
return x / math.sqrt(2)
PySpice-v0.4.1/PySpice/Physics/ 0000775 0000000 0000000 00000000000 13140600222 0016242 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Physics/MaterialProperties.py 0000664 0000000 0000000 00000003041 13140600222 0022425 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
class Copper:
atomic_number = 29
atomic_mass = 63.546 * 1e-3 # kg
density = 8.96 * 1e3 # kg·m−3
thermal_conductivity = 401 # W·m−1·K−1
electrical_resistivity = 16.78 * 1e-9 # Ω·m @20 °C
electron_mobility = - 4.6 * 1e3 # m2·V−1·s−1
##############################################
def electrical_resistance_for_conductor(self, degree):
""" Used to compute conductor resistance. """
rho0 = 16e-3 # Ω·m·mm−2
return rho0 * (1 + .00393 * degree)
PySpice-v0.4.1/PySpice/Physics/PhysicalConstants.py 0000664 0000000 0000000 00000005156 13140600222 0022274 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
#
# Physical Constants from Particle Data Group 2013
# http://pdg.lbl.gov/2013/reviews/rpp2013-rev-phys-constants.pdf
#
####################################################################################################
####################################################################################################
pi = 3.141592653589793238 # π = 3.141 592 653 589 793 238
####################################################################################################
speed_of_light_in_vacuum = c = 299792458 # 299 792 458 m s−1
electron_charge_magnitude = e = q = 1.602176565e-19 # 1.602 176 565(35)×10−19 C = 4.803 204 50(11)×10−10 esu
permeability_of_free_space = mu0 = 4*pi*1e-7 # 4π × 10−7 N A−2 = 12.566 370 614 ... ×10−7 N A−2
permittivity_of_free_space = epsilon0 = 1./(mu0*c**2) # 8.854 187 817 ... ×10−12 F m −1
avogadro_constant = Na = 6.02214129e23 # 6.022 141 29(27)×1023 mol−1
boltzmann_constant = k = 1.3806488e-23 # 1.380 6488(13)×10−23 J K−1 = 8.617 3324(78)×10−5 eV K−1
# 1 eV = 1.602 176 565(35) × 10−19 J
# 1 eV/c2 = 1.782 661 845(39) × 10−36 kg
####################################################################################################
# 0 ◦C ≡ 273.15 K
def degree_to_kelvin(x):
return x + 273.15
def kelvin_to_degree(x):
return x - 273.15
def temperature(degree=None, kelvin=None):
if degree is not None:
return degree_to_kelvin(degree)
else:
return kelvin
# kT at 300 K = [38.681 731(35)]−1 eV
def kT(degree=None, kelvin=None):
return k*temperature(degree=degree, kelvin=kelvin)
PySpice-v0.4.1/PySpice/Physics/Resistor.py 0000664 0000000 0000000 00000002531 13140600222 0020427 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
#
# Notes: Drude model, Nernst-Einstein law
#
####################################################################################################
####################################################################################################
def conductor_resistance(resistivity, length, section):
return resistivity * length / section
PySpice-v0.4.1/PySpice/Physics/SemiConductor.py 0000664 0000000 0000000 00000004337 13140600222 0021401 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
""" This module provides semiconductor models.
"""
####################################################################################################
import numpy as np
####################################################################################################
import PySpice.Physics.PhysicalConstants as Cst
####################################################################################################
class ShockleyDiode:
""" This class provides an implementation of the Shockley Diode Model.
"""
##############################################
def __init__(self,
Is=10e-12, # 10 pA
n=1,
degree=25, kelvin=None):
self.Is = Is # reverse bias saturation current
self.n = n # ideality factor or emission coefficient
self.T = Cst.temperature(degree=degree, kelvin=kelvin)
##############################################
@property
def Vt(self):
""" Thermal Voltage """
return Cst.kT(kelvin=self.T) / Cst.q
##############################################
def I(self, Vd):
return self.Is*(np.exp(Vd/(self.n*self.Vt)) - 1)
##############################################
def rd(self, Vd):
""" Dynamic resistance defined by dVd/dI. """
return self.n*self.Vt/self.I(Vd)
PySpice-v0.4.1/PySpice/Physics/__init__.py 0000664 0000000 0000000 00000001627 13140600222 0020361 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/PySpice/Plot/ 0000775 0000000 0000000 00000000000 13140600222 0015536 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Plot/BodeDiagram.py 0000664 0000000 0000000 00000004402 13140600222 0020246 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import math
from matplotlib import pyplot
####################################################################################################
# frequency in Hz, gain in dB, phase in radians between -pi and pi.
def bode_diagram_gain(axe, frequency, gain, **kwargs):
axe.semilogx(frequency, gain, basex=10, **kwargs)
axe.grid(True)
axe.grid(True, which='minor')
axe.set_xlabel("Frequency [Hz]")
axe.set_ylabel("Gain [dB]")
####################################################################################################
def bode_diagram_phase(axe, frequency, phase, **kwargs):
axe.semilogx(frequency, phase, basex=10, **kwargs)
axe.set_ylim(-math.pi, math.pi)
axe.grid(True)
axe.grid(True, which='minor')
axe.set_xlabel("Frequency [Hz]")
axe.set_ylabel("Phase [rads]")
# axe.set_yticks # Fixme:
pyplot.yticks((-math.pi, -math.pi/2,0, math.pi/2, math.pi),
(r"$-\pi$", r"$-\frac{\pi}{2}$", "0", r"$\frac{\pi}{2}$", r"$\pi$"))
####################################################################################################
def bode_diagram(axes, frequency, gain, phase, **kwargs):
bode_diagram_gain(axes[0], frequency, gain, **kwargs)
bode_diagram_phase(axes[1], frequency, phase, **kwargs)
PySpice-v0.4.1/PySpice/Plot/__init__.py 0000664 0000000 0000000 00000001627 13140600222 0017655 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/PySpice/Probe/ 0000775 0000000 0000000 00000000000 13140600222 0015667 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Probe/Plot.py 0000664 0000000 0000000 00000003221 13140600222 0017155 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
"""This module implements plotting helper."""
####################################################################################################
import matplotlib.pyplot as plt
####################################################################################################
def plot(waveform, *args, **kwargs):
"""Plot a waveform using the current Axes instance or the one specified by the *axis* key
argument. Additional parameters are passed to the Matplotlib plot function.
"""
axis = kwargs.get('axis', plt.gca())
if 'axis' in kwargs:
del kwargs['axis']
axis.plot(waveform.abscissa, waveform, *args, **kwargs)
PySpice-v0.4.1/PySpice/Probe/WaveForm.py 0000664 0000000 0000000 00000015037 13140600222 0017775 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
#
# op: value
# transient: time
# dc: v-sweep
# ac: frequency
# sens: element parameters
#
# Probes
#
# Nodes:
# Voltage versus gnd
# real or complex
# static or (temporale/transient, v-sweep, frequency)
#
# static waveform: (node->, unit, value)
# waveform: (node->, unit, values, abscissa->)
#
# analysis.1.v !
# analysis.in.v !
# analysis['1'].v
# analysis.n1.v
#
# Source:
# Current
#
# analysis.Vinput.i
#
# Vinput -> lower case
#
# axe.plot(x, y, *args, **kwargs)
#
###################################################################################################
####################################################################################################
#
# Analysis versus Simulation
#
####################################################################################################
####################################################################################################
import numpy as np
####################################################################################################
# Fixme: self._
class WaveForm(np.ndarray):
"""This class implements a waveform on top of Numpy Array.
"""
##############################################
def __new__(cls, name, unit, data, title=None, abscissa=None):
obj = np.asarray(data).view(cls)
obj.name = str(name)
obj.unit = unit
obj.title = title # str(title)
obj.abscissa = abscissa
return obj
##############################################
def __array_finalize__(self, obj):
if obj is None:
return
self.name = getattr(obj, 'name', None)
self.unit = getattr(obj, 'unit', None)
self.title = getattr(obj, 'title', None)
self.abscissa = getattr(obj, 'abscissa', None)
##############################################
def __repr__(self):
return 'variable {0.name} [{0.unit}]'.format(self)
##############################################
def __str__(self):
if self.title is not None:
return self.title
else:
return '{0.name} [{0.unit}]'.format(self)
##############################################
def str_data(self):
return super().__repr__()
####################################################################################################
class Analysis:
##############################################
def __init__(self, simulation, nodes=(), branches=(), elements=()):
# Fixme: branches are elements in fact, and elements is not yet supported ...
self._simulation = simulation
# Fixme: to func?
self.nodes = {waveform.name:waveform for waveform in nodes}
self.branches = {waveform.name:waveform for waveform in branches}
self.elements = {waveform.name:waveform for waveform in elements}
##############################################
@property
def simulation(self):
return self._simulation
##############################################
def __getitem__(self, name):
if name in self.nodes:
return self.nodes[name]
elif name in self.branches:
return self.branches[name]
elif name in self.elements:
return self.elements[name]
else:
raise IndexError(name)
##############################################
def __getattr__(self, name):
try:
return self.__getitem__(name)
except IndexError:
raise AttributeError(name)
####################################################################################################
class OperatingPoint(Analysis):
pass
####################################################################################################
class SensitivityAnalysis(Analysis):
##############################################
def __init__(self, simulation, elements):
super().__init__(simulation=simulation, elements=elements)
####################################################################################################
class DcAnalysis(Analysis):
"""
When the DC analysis is performed with multiple sources, sweep is the last source.
The loop scheme is::
for v1 in vsource1:
for v2 in vsource2:
...
"""
##############################################
def __init__(self, simulation, sweep, nodes, branches):
super().__init__(simulation=simulation, nodes=nodes, branches=branches)
self._sweep = sweep
##############################################
@property
def sweep(self):
return self._sweep
####################################################################################################
class AcAnalysis(Analysis):
##############################################
def __init__(self, simulation, frequency, nodes, branches):
super().__init__(simulation=simulation, nodes=nodes, branches=branches)
self._frequency = frequency
##############################################
@property
def frequency(self):
return self._frequency
####################################################################################################
class TransientAnalysis(Analysis):
##############################################
def __init__(self, simulation, time, nodes, branches):
super().__init__(simulation=simulation, nodes=nodes, branches=branches)
self._time = time
##############################################
@property
def time(self):
return self._time
PySpice-v0.4.1/PySpice/Probe/__init__.py 0000664 0000000 0000000 00000001627 13140600222 0020006 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/PySpice/Spice/ 0000775 0000000 0000000 00000000000 13140600222 0015663 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Spice/BasicElement.py 0000664 0000000 0000000 00000110113 13140600222 0020565 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This module implements SPICE circuit elements.
.. warning:: Some elements are partially implemented.
.. warning:: The API documentation generated by Sphinx is perfectible for this module. The source
code can be more informative.
.. note:: It would be nice to have an use-full and working documentation in the interactive environment.
The element's parameters are internally implemented using class attributes and descriptors.
There are two types of parameters, positionals which are required and where the order mater, and
optional parameters which are passed as keyword arguments.
Parameters are registered with more expressive and longer names than their Spice counterparts. For
optional parameters, we can use both Spice and longer name for convenience.
See Ngspice documentation for details.
+--------------+------------------------------------------------------+
| First letter + Element description |
+--------------+------------------------------------------------------+
| A + XSPICE code model |
+--------------+------------------------------------------------------+
| B + Behavioral (arbitrary) source |
+--------------+------------------------------------------------------+
| C + Capacitor |
+--------------+------------------------------------------------------+
| D + Diode |
+--------------+------------------------------------------------------+
| E + Voltage-controlled voltage source (VCVS) |
+--------------+------------------------------------------------------+
| F + Current-controlled current source (CCCs) |
+--------------+------------------------------------------------------+
| G + Voltage-controlled current source (VCCS) |
+--------------+------------------------------------------------------+
| H + Current-controlled voltage source (CCVS) |
+--------------+------------------------------------------------------+
| I + Current source |
+--------------+------------------------------------------------------+
| J + Junction field effect transistor (JFET) |
+--------------+------------------------------------------------------+
| K + Coupled (Mutual) Inductors |
+--------------+------------------------------------------------------+
| L + Inductor |
+--------------+------------------------------------------------------+
| M + Metal oxide field effect transistor (MOSFET) |
+--------------+------------------------------------------------------+
| N + Numerical device for GSS |
+--------------+------------------------------------------------------+
| O + Lossy transmission line |
+--------------+------------------------------------------------------+
| P + Coupled multiconductor line (CPL) |
+--------------+------------------------------------------------------+
| Q + Bipolar junction transistor (BJT) |
+--------------+------------------------------------------------------+
| R + Resistor |
+--------------+------------------------------------------------------+
| S + Switch (voltage-controlled) |
+--------------+------------------------------------------------------+
| T + Lossless transmission line |
+--------------+------------------------------------------------------+
| U + Uniformly distributed RC line |
+--------------+------------------------------------------------------+
| V + Voltage source |
+--------------+------------------------------------------------------+
| W + Switch (current-controlled) |
+--------------+------------------------------------------------------+
| X + Subcircuit |
+--------------+------------------------------------------------------+
| Y + Single lossy transmission line (TXL) |
+--------------+------------------------------------------------------+
| Z + Metal semiconductor field effect transistor (MESFET) |
+--------------+------------------------------------------------------+
"""
# Fixme: add transmission lines
####################################################################################################
from ..Tools.StringTools import join_list, str_spice
from ..Unit import U_m, U_s, U_A, U_V, U_Degree, U_Ω, U_F, U_H, U_Hz
from .Netlist import (Pin,
AnyPinElement,
TwoPinElement, ThreePinElement, FourPinElement,
NPinElement,
TwoPortElement)
from .ElementParameter import (
# KeyValueParameter,
BoolKeyParameter,
ElementNamePositionalParameter,
ExpressionKeyParameter,
ExpressionPositionalParameter,
FlagParameter,
FloatKeyParameter,
FloatPairKeyParameter,
FloatTripletKeyParameter,
FloatPositionalParameter,
InitialStatePositionalParameter,
IntKeyParameter,
ModelPositionalParameter,
)
####################################################################################################
class SubCircuitElement(NPinElement):
"""This class implements a sub-circuit.
Spice syntax::
XYYYYYY node1 node2 ... subcircuit_name parameter1=value1 ...
Attributes:
:attr:`subcircuit_name`
.note : As opposite to Spice, the circuit's name is specified before the nodes so as to act as
`*args`.
"""
alias = 'X'
prefix = 'X'
subcircuit_name = ElementNamePositionalParameter(position=0, key_parameter=False)
##############################################
def __init__(self, name, subcircuit_name, *nodes, **parameters):
pins = [Pin(self, None, node) for node in nodes]
super().__init__(name, pins, subcircuit_name)
self.parameters = parameters
# Fixme: investigate
# for key, value in parameters.items():
# parameter = KeyValueParameter(key)
# parameter.__set__(self, value)
# self.optional_parameters[key] = parameter
# setattr(self, key, parameter)
##############################################
def format_spice_parameters(self):
""" Return the formatted list of parameters. """
spice_parameters = super().format_spice_parameters()
if self.parameters:
spice_parameters += ' ' + join_list(['{}={}'.format(key, str_spice(value))
for key, value in self.parameters.items()])
return spice_parameters
####################################################################################################
class Resistor(TwoPinElement):
"""This class implements a resistor.
Spice syntax::
RXXXXXXX n+ n- value
Keyword Parameters:
:attr:`ac`
:attr:`multiplier`
alias `m`
:attr:`scale`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
:attr:`noisy`
Attributes:
:attr:`resistance`
:attr:`ac`
:attr:`multiplier`
:attr:`scale`
:attr:`temperature`
:attr:`device_temperature`
:attr:`noisy`
"""
alias = 'R'
prefix = 'R'
resistance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_Ω)
ac = FloatKeyParameter('ac', unit=U_Ω)
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
noisy = BoolKeyParameter('noisy')
####################################################################################################
class SemiconductorResistor(TwoPinElement):
"""This class implements a Semiconductor resistor.
Spice syntax::
RXXXXXXX n+ n- m=
Keyword Parameters:
:attr:`model`
:attr:`length`
alias `l`
:attr:`width`
alias `w`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
:attr:`multiplier`
alias `m`
:attr:`ac`
:attr:`scale`
:attr:`noisy`
Attributes:
:attr:`resistance`
:attr:`model`
:attr:`length`
:attr:`width`
:attr:`temperature`
:attr:`device_temperature`
:attr:`multiplier`
:attr:`ac`
:attr:`scale`
:attr:`noisy`
"""
alias = 'SemiconductorResistor'
prefix = 'R'
resistance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_Ω)
model = ModelPositionalParameter(position=1, key_parameter=True)
length = FloatKeyParameter('l', unit=U_m)
width = FloatKeyParameter('w', unit=U_m)
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
multiplier = IntKeyParameter('m')
ac = FloatKeyParameter('ac', unit=U_Ω)
scale = FloatKeyParameter('scale')
noisy = BoolKeyParameter('noisy')
####################################################################################################
class BehavioralResistor(TwoPinElement):
# Behavioral / Behavioural
"""This class implements a behavioral resistor.
Spice syntax::
RXXXXXXX n+ n- 'expression'
Rxxxxxxx n+ n- R='expression'
Keyword Parameters:
:attr:`tc1`
:attr:`tc2`
Attributes:
:attr:`resistance_expression`
:attr:`tc1`
:attr:`tc2`
"""
alias = 'BehavioralResistor'
prefix = 'R'
resistance_expression = ExpressionPositionalParameter(position=0, key_parameter=False)
tc1 = FloatKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
####################################################################################################
class Capacitor(TwoPinElement):
"""This class implements a capacitor.
Spice syntax::
CXXXXXXX n+ n-
Keyword Parameters:
:attr:`model`
:attr:`multiplier`
alias `m`
:attr:`scale`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
:attr:`initial_condition`
alias `ic`
Attributes:
:attr:`capacitance`
:attr:`model`
:attr:`multiplier`
:attr:`scale`
:attr:`temperature`
:attr:`device_temperature`
:attr:`initial_condition`
"""
alias = 'C'
prefix = 'C'
capacitance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_F)
model = ModelPositionalParameter(position=1, key_parameter=True)
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
initial_condition = FloatKeyParameter('ic')
####################################################################################################
class SemiconductorCapacitor(TwoPinElement):
"""This class implements a semiconductor capacitor.
Spice syntax::
CXXXXXXX n+ n-
Keyword Parameters:
:attr:`model`
:attr:`length`
alias `l`
:attr:`width`
alias `w`
:attr:`multiplier`
alias `m`
:attr:`scale`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
:attr:`initial_condition`
alias `ic`
Attributes:
:attr:`capacitance`
:attr:`model`
:attr:`length`
:attr:`width`
:attr:`multiplier`
:attr:`scale`
:attr:`temperature`
:attr:`device_temperature`
:attr:`initial_condition`
"""
alias = 'SemiconductorCapacitor'
prefix = 'C'
capacitance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_F)
model = ModelPositionalParameter(position=1, key_parameter=True)
length = FloatKeyParameter('l', unit=U_m)
width = FloatKeyParameter('w', unit=U_m)
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
initial_condition = FloatKeyParameter('ic')
####################################################################################################
class BehavioralCapacitor(TwoPinElement):
"""This class implements a behavioral capacitor.
Spice syntax::
CXXXXXXX n+ n- 'expression'
CXXXXXXX n+ n- C='expression'
Keyword Parameters:
:attr:`tc1`
:attr:`tc2`
Attributes:
:attr:`capacitance_expression`
:attr:`tc1`
:attr:`tc2`
"""
alias = 'BehavioralCapacitor'
prefix = 'C'
capacitance_expression = ExpressionPositionalParameter(position=0, key_parameter=False)
tc1 = FloatKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
####################################################################################################
class Inductor(TwoPinElement):
"""This class implements an inductor.
Spice syntax::
LYYYYYYY n+ n-
Keyword Parameters:
:attr:`nt`
:attr:`multiplier`
alias `m`
:attr:`scale`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
:attr:`initial_condition`
alias `ic`
Attributes:
:attr:`inductance`
:attr:`model`
:attr:`nt`
:attr:`multiplier`
:attr:`scale`
:attr:`temperature`
:attr:`device_temperature`
:attr:`initial_condition`
"""
alias = 'L'
prefix = 'L'
inductance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_H)
model = ModelPositionalParameter(position=1, key_parameter=True)
nt = FloatKeyParameter('nt')
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
initial_condition = FloatKeyParameter('ic')
####################################################################################################
class BehavioralInductor(TwoPinElement):
"""This class implements a behavioral inductor.
Spice syntax::
LXXXXXXX n+ n- 'expression'
LXXXXXXX n+ n- L='expression'
Keyword Parameters:
:attr:`tc1`
:attr:`tc2`
Attributes:
:attr:`inductance_expression`
:attr:`tc1`
:attr:`tc2`
"""
alias = 'BehavioralInductor'
prefix = 'L'
inductance_expression = ExpressionPositionalParameter(position=0, key_parameter=False)
tc1 = FloatKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
####################################################################################################
class CoupledInductor(AnyPinElement):
"""This class implementss a coupled (mutual) inductors.
Spice syntax::
KXXXXXXX LYYYYYYY LZZZZZZZ value
Keyword Parameters:
Attributes:
:attr:`inductor1`
:attr:`inductor2`
:attr:`coupling_factor`
"""
alias = 'CoupledInductor'
prefix = 'K'
inductor1 = ElementNamePositionalParameter(position=0, key_parameter=False)
inductor2 = ElementNamePositionalParameter(position=1, key_parameter=False)
coupling_factor = FloatPositionalParameter(position=2, key_parameter=False)
##############################################
def __init__(self, name, inductor_name1, inductor_name2, coupling_factor):
# Fixme: any pins here
super().__init__(name, (),
inductor_name1, inductor_name2, coupling_factor)
self._inductor_names = (inductor_name1, inductor_name2)
####################################################################################################
class VoltageControlledSwitch(TwoPortElement):
"""This class implements a voltage controlled switch.
Spice syntax::
SXXXXXXX n+ n- nc+ nc- model
Keyword Parameters:
:attr:`model`
:attr:`initial_state`
Attributes:
:attr:`model`
:attr:`initial_state`
"""
alias = 'VCS'
prefix = 'S'
model = ModelPositionalParameter(position=0, key_parameter=True)
initial_state = InitialStatePositionalParameter(position=1, key_parameter=True)
####################################################################################################
class CurrentControlledSwitch(TwoPinElement):
"""This class implements a current controlled switch.
Spice syntax::
WYYYYYYY n+ n- vname model
Keyword Parameters:
:attr:`source`
:attr:`model`
:attr:`initial_state`
Attributes:
:attr:`source`
:attr:`model`
:attr:`initial_state`
"""
alias = 'CCS'
prefix = 'W'
source = ElementNamePositionalParameter(position=0, key_parameter=True)
model = ModelPositionalParameter(position=1, key_parameter=True)
initial_state = InitialStatePositionalParameter(position=2, key_parameter=True)
####################################################################################################
class VoltageSource(TwoPinElement):
"""This class implements an independent sources for voltage.
Spice syntax::
VXXXXXXX n+ n- < dc/tran value> >> >> >>
Keyword Parameters:
Attributes:
:attr:`dc_value`
"""
alias = 'V'
prefix = 'V'
# Fixme: ngspice manual doesn't describe well the syntax
dc_value = FloatPositionalParameter(position=0, key_parameter=False, unit=U_V)
####################################################################################################
class CurrentSource(TwoPinElement):
"""This class implements an independent sources for current.
Spice syntax::
IYYYYYYY n+ n- < dc/tran value> >> >> >>
Keyword Parameters:
Attributes:
:attr:`dc_value`
"""
alias = 'I'
prefix = 'I'
# Fixme: ngspice manual doesn't describe well the syntax
dc_value = FloatPositionalParameter(position=0, key_parameter=False, unit=U_A)
####################################################################################################
class VoltageControlledVoltageSource(TwoPortElement):
"""This class implements a linear voltage-controlled voltage sources (VCVS).
Spice syntax::
EXXXXXXX n+ n- nc+ nc- value
Keyword Parameters:
Attributes:
:attr:`voltage_gain`
"""
alias = 'VCVS'
prefix = 'E'
voltage_gain = ExpressionPositionalParameter(position=0, key_parameter=False)
####################################################################################################
class CurrentControlledCurrentSource(TwoPinElement):
"""This class implements a linear current-controlled current sources (CCCS).
Spice syntax::
FXXXXXXX n+ n- vname value
Keyword Parameters:
Attributes:
:attr:`source`
:attr:`current_gain`
"""
alias = 'CCCS'
prefix = 'F'
source = ElementNamePositionalParameter(position=0, key_parameter=False)
current_gain = ExpressionPositionalParameter(position=1, key_parameter=False)
####################################################################################################
class VoltageControlledCurrentSource(TwoPortElement):
"""This class implements a linear voltage-controlled current sources (VCCS).
.. warning:: partially implemented
Spice syntax::
Gxxx n+ n- nc+ nc- value
Gxxx n+ n- value={expr}
Gxxx n1 n2 TABLE {expression}=(x0,y0) (x1,y1) (x2,y2)
Gxxx n+ n- ( POLY (nd) ) nc1+ nc1- ( nc2+ nc2- ... ) p0 ( p1 ... )
Laplace
Keyword Parameters:
Attributes:
:attr:`transconductance`
"""
alias = 'VCCS'
prefix = 'G'
transconductance = ExpressionPositionalParameter(position=0, key_parameter=False)
####################################################################################################
class CurrentControlledVoltageSource(TwoPinElement):
"""This class implements a linear current-controlled voltage sources (ccvs).
Spice syntax::
HXXXXXXX n+ n- vname value
Keyword Parameters:
Attributes:
:attr:`source`
:attr:`transresistance`
"""
alias = 'CCVS'
prefix = 'H'
source = ElementNamePositionalParameter(position=0, key_parameter=False)
transresistance = ExpressionPositionalParameter(position=1, key_parameter=False)
####################################################################################################
class BehavioralSource(TwoPinElement):
"""This class implements a behavioral source.
Spice syntax::
BXXXXXXX n+ n-
Keyword Parameters:
:attr:`current_expression`
alias `i`
:attr:`voltage_expression`
alias `v`
:attr:`tc1`
:attr:`tc2`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
Attributes:
:attr:`current_expression`
:attr:`voltage_expression`
:attr:`tc1`
:attr:`tc2`
:attr:`temperature`
:attr:`device_temperature`
"""
alias = 'BehavioralSource'
prefix = 'B'
current_expression = ExpressionKeyParameter('i')
voltage_expression = ExpressionKeyParameter('v')
tc1 = FloatKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
####################################################################################################
class NonLinearVoltageSource(TwoPinElement):
"""This class implements a non linear voltage source.
.. warning:: partially implemented
Spice syntax::
Exxx n+ n- vol='expr'
Exxx n+ n- value={expr}
Exxx n1 n2 TABLE {expression}=(x0,y0) (x1,y1) (x2,y2)
Exxx n+ n- ( POLY (nd) ) nc1+ nc1- ( nc2+ nc2- ... ) p0 ( p1 ... )
Laplace
Keyword Parameters:
Attributes:
"""
alias = 'NonLinearVoltageSource'
prefix = 'E'
##############################################
def __init__(self, name,
node_plus, node_minus,
expression=None,
table=None):
super().__init__(name, node_plus, node_minus)
self.expression = expression
self.table = table
##############################################
def __str__(self):
spice_element = self.format_node_names()
# Fixme: expression
if self.table is not None:
# TABLE {expression} = (x0, y0) (x1, y1) ...
table = ['({}, {})'.format(str_spice(x), str_spice(y)) for x, y in self.table]
spice_element += ' TABLE {%s} = %s' % (self.expression, join_list(table))
return spice_element
####################################################################################################
class Diode(TwoPinElement):
"""This class implements a junction diode.
Spice syntax::
DXXXXXXX n+ n- mname
Keyword Parameters:
:attr:`model`
:attr:`area`
:attr:`multiplier`
alias `m`
:attr:`pj`
:attr:`off`
:attr:`ic`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
Attributes:
:attr:`model`
:attr:`area`
:attr:`multiplier`
:attr:`pj`
:attr:`off`
:attr:`ic`
:attr:`temperature`
:attr:`device_temperature`
"""
alias = 'D'
prefix = 'D'
model = ModelPositionalParameter(position=0, key_parameter=True)
area = FloatKeyParameter('area')
multiplier = IntKeyParameter('m')
pj = FloatKeyParameter('pj')
off = FlagParameter('off')
ic = FloatPairKeyParameter('ic')
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
####################################################################################################
class BipolarJunctionTransistor(NPinElement):
"""This class implements a bipolar junction transistor.
Spice syntax::
QXXXXXXX nc nb ne mname
Keyword Parameters:
:attr:`model`
:attr:`area`
:attr:`areac`
:attr:`areab`
:attr:`multiplier`
alias `m`
:attr:`off`
:attr:`ic`
:attr:`temperature`
alias `temp`
:attr:`device_temperature`
alias `dtemp`
Attributes:
:attr:`model`
:attr:`area`
:attr:`areac`
:attr:`areab`
:attr:`multiplier`
:attr:`off`
:attr:`ic`
:attr:`temperature`
:attr:`device_temperature`
"""
# Fixme: off doesn't fit in kwargs !
alias = 'BJT'
prefix = 'Q'
model = ModelPositionalParameter(position=0, key_parameter=True)
area = FloatKeyParameter('area')
areac = FloatKeyParameter('areac')
areab = FloatKeyParameter('areab')
multiplier = IntKeyParameter('m')
off = FlagParameter('off')
ic = FloatPairKeyParameter('ic')
temperature = FloatKeyParameter('temp', unit=U_Degree)
device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
##############################################
def __init__(self, name,
collector_node, base_node, emitter_node,
substrate_node=None, # default is ground
**kwargs):
pins = [Pin(self, 'collector', collector_node),
Pin(self, 'base', base_node),
Pin(self, 'emitter', emitter_node),]
if substrate_node is not None:
self._substrate_pin = Pin(self, 'substrate', substrate_node)
pins.append(self._substrate_pin)
super().__init__(name, pins, **kwargs)
##############################################
@property
def collector(self):
return self.pins[0]
##############################################
@property
def base(self):
return self.pins[1]
##############################################
@property
def emitter(self):
return self.pins[2]
##############################################
@property
def substrate(self):
try:
return self.pins[3]
except IndexError:
return None
####################################################################################################
class JunctionFieldEffectTransistor(ThreePinElement):
"""This class implements a bipolar junction transistor.
Spice syntax::
JXXXXXXX nd ng ns mname
Keyword Parameters:
:attr:`model`
:attr:`area`
:attr:`off`
:attr:`ic`
:attr:`temperature`
alias `temp`
Attributes:
:attr:`model`
:attr:`area`
:attr:`off`
:attr:`ic`
:attr:`temperature`
"""
# Fixme: off doesn't fit in kwargs !
alias = 'JFET'
prefix = 'J'
model = ModelPositionalParameter(position=0, key_parameter=True)
area = FloatKeyParameter('area')
multiplier = IntKeyParameter('m')
off = FlagParameter('off')
ic = FloatPairKeyParameter('ic')
temperature = FloatKeyParameter('temp', unit=U_Degree)
##############################################
def __init__(self, name,
drain_node, gate_node, source_node,
**kwargs):
pins = [Pin(self, 'drain', drain_node),
Pin(self, 'gate', gate_node),
Pin(self, 'source', source_node),]
super().__init__(name, pins, **kwargs)
##############################################
@property
def drain(self):
return self.pins[0]
##############################################
@property
def gate(self):
return self.pins[1]
##############################################
@property
def source(self):
return self.pins[2]
####################################################################################################
class Mesfet(ThreePinElement):
"""This class implements a Metal Semiconductor Field Effect Transistor.
Spice syntax::
ZXXXXXXX nd ng ns mname
Keyword Parameters:
:attr:`model`
:attr:`area`
:attr:`off`
:attr:`ic`
Attributes:
:attr:`model`
:attr:`area`
:attr:`off`
:attr:`ic`
"""
# Fixme: off doesn't fit in kwargs !
alias = 'MESFET'
prefix = 'Z'
model = ModelPositionalParameter(position=0, key_parameter=True)
area = FloatKeyParameter('area')
multiplier = IntKeyParameter('m')
off = FlagParameter('off')
ic = FloatPairKeyParameter('ic')
##############################################
def __init__(self, name,
drain_node, gate_node, source_node,
**kwargs):
pins = [Pin(self, 'drain', drain_node),
Pin(self, 'gate', gate_node),
Pin(self, 'source', source_node),]
super().__init__(name, pins, **kwargs)
##############################################
@property
def drain(self):
return self.pins[0]
##############################################
@property
def gate(self):
return self.pins[1]
##############################################
@property
def source(self):
return self.pins[2]
####################################################################################################
class Mosfet(FourPinElement):
"""This class implements a Metal Oxide Field Effect Transistor.
Spice syntax::
MXXXXXXX nd ng ns nb mname
+
+
Keyword Parameters:
:attr:`model`
:attr:`multiplier`
alias `m`
:attr:`length`
alias `l`
:attr:`width`
alias `w`
:attr:`drain_area`
alias `ad`
:attr:`source_area`
alias `as`
:attr:`drain_perimeter`
alias `pd`
:attr:`source_perimeter`
alias `ps`
:attr:`drain_number_square`
alias `nrd`
:attr:`source_number_square`
alias `nrs`
:attr:`off`
:attr:`ic`
:attr:`temperature`
alias `temp`
Attributes:
:attr:`model`
:attr:`multiplier`
:attr:`length`
:attr:`width`
:attr:`drain_area`
:attr:`source_area`
:attr:`drain_perimeter`
:attr:`source_perimeter`
:attr:`drain_number_square`
:attr:`source_number_square`
:attr:`off`
:attr:`ic`
:attr:`temperature`
"""
# Fixme: off doesn't fit in kwargs !
alias = 'MOSFET'
prefix = 'M'
model = ModelPositionalParameter(position=0, key_parameter=True)
multiplier = IntKeyParameter('m')
length = FloatKeyParameter('l', unit=U_m)
width = FloatKeyParameter('w', unit=U_m)
drain_area = FloatKeyParameter('ad')
source_area = FloatKeyParameter('as')
drain_perimeter = FloatKeyParameter('pd')
source_perimeter = FloatKeyParameter('ps')
drain_number_square = FloatKeyParameter('nrd')
source_number_square = FloatKeyParameter('nrs')
off = FlagParameter('off')
ic = FloatTripletKeyParameter('ic')
temperature = FloatKeyParameter('temp', unit=U_Degree)
##############################################
def __init__(self, name,
drain_node, gate_node, source_node, substrate_node,
**kwargs):
pins = [Pin(self, 'drain', drain_node),
Pin(self, 'gate', gate_node),
Pin(self, 'source', source_node),
Pin(self, 'substrate', substrate_node), # bulk
]
super().__init__(name, pins, **kwargs)
##############################################
@property
def drain(self):
return self.pins[0]
##############################################
@property
def gate(self):
return self.pins[1]
##############################################
@property
def source(self):
return self.pins[2]
##############################################
@property
def substrate(self):
return self.pins[3]
####################################################################################################
class TransmissionLine(TwoPortElement):
"""This class implements a lossless transmission line.
Spice syntax::
TXXXXXXX N1 N2 N3 N4 Z0=VALUE
>
where TD or F, NL must be specified.
Keyword Parameters:
:attr:`impedance`
alias:`Z0`
:attr:`time_delay`
alias:`TD`
:attr:`frequency`
alias:`F`
:attr:`normalized_length`
alias:`NL`
Attributes:
:attr:`impedance`
:attr:`time_delay`
:attr:`frequency`
:attr:`normalized_length`
Note: Either time_delay or frequency must be given.
"""
alias = 'TransmissionLine'
prefix = 'T'
impedance = FloatKeyParameter('Z0', default=50, unit=U_Ω)
time_delay = FloatKeyParameter('TD', unit=U_s)
frequency = FloatKeyParameter('F', unit=U_Hz)
normalized_length = FloatKeyParameter('NL')
##############################################
def __init__(self, name,
input_node_plus, input_node_minus,
output_node_plus, output_node_minus,
*args, **kwargs):
# check: ^ xor, & bitwise and
if not (('time_delay' in kwargs) ^
(('frequency' in kwargs) & ('normalized_length' in kwargs))):
raise NameError('Either TD or F, NL must be specified')
super().__init__(name,
output_node_plus, output_node_minus,
input_node_plus, input_node_minus, # Fixme: inverted inputs
*args, **kwargs)
PySpice-v0.4.1/PySpice/Spice/ElementParameter.py 0000664 0000000 0000000 00000025073 13140600222 0021476 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This modules implements the machinery to define element's parameters as descriptor."""
####################################################################################################
from ..Unit import Unit
from ..Tools.StringTools import str_spice
####################################################################################################
class ParameterDescriptor:
"""This base class implements a descriptor for element parameters.
Public Attributes:
:attr:`attribute_name`
Name of the attribute in the element's class
:attr:`default_value`
The default value
"""
##############################################
def __init__(self, default=None):
self._default_value = default
self._attribute_name = None
##############################################
@property
def default_value(self):
return self._default_value
@property
def attribute_name(self):
return self._attribute_name
@attribute_name.setter
def attribute_name(self, name):
self._attribute_name = name
##############################################
def __get__(self, instance, owner=None):
try:
return getattr(instance, '_' + self._attribute_name)
except AttributeError:
return self.default_value
##############################################
def __set__(self, instance, value):
setattr(instance, '_' + self._attribute_name, value)
##############################################
def __repr__(self):
return self.__class__.__name__
##############################################
def validate(self, value):
"""Validate the parameter's value."""
return value
##############################################
def nonzero(self, instance):
return self.__get__(instance) is not None
##############################################
def to_str(self, instance):
"""Convert the parameter's value to SPICE syntax."""
raise NotImplementedError
##############################################
def __lt__(self, other):
return self._attribute_name < other.attribute_name
####################################################################################################
class PositionalElementParameter(ParameterDescriptor):
"""This class implements a descriptor for positional element parameters.
Public Attributes:
:attr:`key_parameter`
Flag to specify if the parameter is passed as key parameter in Python
:attr:`position`
Position of the parameter in the element definition
"""
##############################################
def __init__(self, position, default=None, key_parameter=False):
super().__init__(default)
self._position = position
self._key_parameter = key_parameter
##############################################
@property
def position(self):
return self._position
@property
def key_parameter(self):
return self._key_parameter
##############################################
def to_str(self, instance):
return str_spice(self.__get__(instance))
##############################################
def __lt__(self, other):
return self._position < other.position
####################################################################################################
class ElementNamePositionalParameter(PositionalElementParameter):
"""This class implements an element name positional parameter."""
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class ExpressionPositionalParameter(PositionalElementParameter):
"""This class implements an expression positional parameter. """
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class FloatPositionalParameter(PositionalElementParameter):
"""This class implements a float positional parameter."""
##############################################
def __init__(self, position, unit=None, **kwargs):
super().__init__(position, **kwargs)
self._unit = unit
##############################################
def validate(self, value):
if isinstance(value, Unit):
return value
else:
return Unit(value)
####################################################################################################
class InitialStatePositionalParameter(PositionalElementParameter):
"""This class implements an initial state (on, off) positional parameter."""
##############################################
def validate(self, value):
return bool(value) # Fixme: check KeyParameter
##############################################
def to__str_(self, instance):
if self.__get__(instance):
return 'on'
else:
return 'off'
####################################################################################################
class ModelPositionalParameter(PositionalElementParameter):
"""This class implements a model positional parameter. """
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class FlagParameter(ParameterDescriptor):
"""This class implements a flag parameter.
Public Attributes:
:attr:`spice_name`
Name of the parameter
"""
##############################################
def __init__(self, spice_name, default=False):
super().__init__(default)
self.spice_name = spice_name
##############################################
def nonzero(self, instance):
return bool(self.__get__(instance))
##############################################
def to_str(self, instance):
if self.nonzero(instance):
return 'off'
else:
return ''
####################################################################################################
class KeyValueParameter(ParameterDescriptor):
"""This class implements a key value pair parameter.
Public Attributes:
:attr:`spice_name`
Name of the parameter
"""
##############################################
def __init__(self, spice_name, default=None):
super().__init__(default)
self.spice_name = spice_name
##############################################
def str_value(self, instance):
return str_spice(self.__get__(instance))
##############################################
def to_str(self, instance):
if bool(self):
return '{}={}'.format(self.spice_name, self.str_value(instance))
else:
return ''
####################################################################################################
class BoolKeyParameter(KeyValueParameter):
"""This class implements a boolean key parameter."""
##############################################
def nonzero(self, instance):
return bool(self.__get__(instance))
##############################################
def str_value(self, instance):
if self.nonzero(instance):
return '1'
else:
return '0'
####################################################################################################
class ExpressionKeyParameter(KeyValueParameter):
"""This class implements an expression key parameter."""
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class FloatKeyParameter(KeyValueParameter):
"""This class implements a float key parameter."""
##############################################
def __init__(self, spice_name, unit=None, **kwargs):
super().__init__(spice_name, **kwargs)
self._unit = unit
##############################################
def validate(self, value):
return float(value)
####################################################################################################
class FloatPairKeyParameter(KeyValueParameter):
"""This class implements a float pair key parameter. """
##############################################
def validate(self, pair):
if len(pair) == 2:
return (float(pair[0]), float(pair[1]))
else:
raise ValueError()
##############################################
def str_value(self, instance):
return ','.join([str(value) for value in self.__get__(instance)])
####################################################################################################
class FloatTripletKeyParameter(FloatPairKeyParameter):
"""This class implements a triplet key parameter."""
##############################################
def validate(self, uplet):
if len(uplet) == 3:
return (float(uplet[0]), float(uplet[1]), float(uplet[2]))
else:
raise ValueError()
####################################################################################################
class IntKeyParameter(KeyValueParameter):
"""This class implements an integer key parameter."""
##############################################
def validate(self, value):
return int(value)
PySpice-v0.4.1/PySpice/Spice/HighLevelElement.py 0000664 0000000 0000000 00000046634 13140600222 0021433 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
""" This module implements high level elements built on Spice elements. """
# Fixme: these waveforms can be current sources as well
####################################################################################################
from ..Math import rms_to_amplitude, amplitude_to_rms
from ..Tools.StringTools import join_list, join_dict, str_spice
from ..Unit import as_s, as_V, as_A, as_Hz
from .BasicElement import VoltageSource
####################################################################################################
class Sinusoidal(VoltageSource):
r""" This class implements a sinusoidal waveform.
+------+----------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+----------------+---------------+-------+
| Vo + offset + + V, A |
+------+----------------+---------------+-------+
| Va + amplitude + + V, A |
+------+----------------+---------------+-------+
| f + frequency + 1 / TStop + Hz |
+------+----------------+---------------+-------+
| Td + delay + 0.0 + sec |
+------+----------------+---------------+-------+
| Df + damping factor + 0.01 + 1/sec |
+------+----------------+---------------+-------+
The shape of the waveform is described by the following formula:
.. math::
V(t) = \begin{cases}
V_o & \text{if}\ 0 \leq t < T_d, \\
V_o + V_a e^{-D_f(t-T_d)} \sin\left(2\pi f (t-T_d)\right) & \text{if}\ T_d \leq t < T_{stop}.
\end{cases}
Spice syntax::
SIN ( Voffset Vamplitude Freq Tdelay DampingFactor )
Public Attributes:
:attr:`amplitude`
:attr:`damping_factor`
:attr:`dc_offset`
:attr:`delay`
:attr:`frequency`
:attr:`offset`
"""
##############################################
def __init__(self, name, node_plus, node_minus,
dc_offset=0,
offset=0, amplitude=1, frequency=50,
delay=0, damping_factor=0):
super().__init__(name, node_plus, node_minus)
self.dc_offset = as_V(dc_offset)
self.offset = as_V(offset)
self.amplitude = as_V(amplitude)
self.frequency = as_Hz(frequency) # Fixme: protect by setter?
self.delay = as_s(delay)
self.damping_factor = as_Hz(damping_factor)
##############################################
@property
def rms_voltage(self):
return amplitude_to_rms(self.amplitude)
##############################################
@property
def period(self):
return self.frequency.period
##############################################
def format_spice_parameters(self):
sin_part = join_list((self.offset, self.amplitude, self.frequency, self.delay, self.damping_factor))
return join_list((
'DC {}'.format(str_spice(self.dc_offset)),
'AC SIN({})'.format(sin_part),
))
####################################################################################################
class AcLine(Sinusoidal):
##############################################
def __init__(self, name, node_plus, node_minus, rms_voltage=230, frequency=50):
super().__init__(name, node_plus, node_minus,
amplitude=rms_to_amplitude(rms_voltage),
frequency=frequency)
####################################################################################################
class Pulse(VoltageSource):
"""This class implements a pulse waveform.
Nomenclature:
+--------+---------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+--------+---------------+---------------+-------+
| V1 + initial value + + V, A |
+--------+---------------+---------------+-------+
| V2 + pulsed value + + V, A |
+--------+---------------+---------------+-------+
| Td + delay time + 0.0 + sec |
+--------+---------------+---------------+-------+
| Tr + rise time + Tstep + sec |
+--------+---------------+---------------+-------+
| Tf + fall time + Tstep + sec |
+--------+---------------+---------------+-------+
| Pw + pulse width + Tstop + sec |
+--------+---------------+---------------+-------+
| Period + period + Tstop + sec |
+--------+---------------+---------------+-------+
Spice Syntax::
PULSE ( V1 V2 Td Tr Tf Pw Period )
A single pulse so specified is described by the following table:
+-------------+-------+
| Time | Value |
+-------------+-------+
| 0 | V1 |
+-------------+-------+
| Td | V1 |
+-------------+-------+
| Td+Tr | V2 |
+-------------+-------+
| Td+Tr+Pw | V2 |
+-------------+-------+
| Td+Tr+Pw+Tf | V1 |
+-------------+-------+
| Tstop | V1 |
+-------------+-------+
Note: default value in Spice for rise and fall time is the simulation transient step, pulse
width and period is the simulation stop time.
Public Attributes:
:attr:`delay_time`
:attr:`fall_time`
:attr:`initial_value`
:attr:`period`
:attr:`pulse_width`
:attr:`pulsed_value`
:attr:`rise_time`
"""
##############################################
def __init__(self, name, node_plus, node_minus,
initial_value, pulsed_value,
pulse_width, period,
delay_time=0, rise_time=0, fall_time=0):
# Fixme: default
# rise_time, fall_time = Tstep
# pulse_width, period = Tstop
super().__init__(name, node_plus, node_minus)
self.initial_value = as_V(initial_value)
self.pulsed_value = as_V(pulsed_value)
self.delay_time = as_s(delay_time)
self.rise_time = as_s(rise_time)
self.fall_time = as_s(fall_time)
self.pulse_width = as_s(pulse_width)
self.period = as_s(period) # Fixme: protect by setter?
# # Fixme: to func?
# # Check parameters
# found_none = False
# for parameter in ('rise_time', 'fall_time', 'pulse_width', 'period'):
# parameter_value = getattr(self, parameter)
# if found_none:
# if parameter_value is not None:
# raise ValueError("Parameter {} is set but some previous parameters was not set".format(parameter))
# else:
# found_none = parameter_value is None
##############################################
@property
def frequency(self):
return self.period.frequency
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('PULSE(' +
join_list((self.initial_value, self.pulsed_value, self.delay_time,
self.rise_time, self.fall_time, self.pulse_width, self.period)) +
')')
####################################################################################################
class Exponential(VoltageSource):
r"""This class implements a Exponential waveform.
Nomenclature:
+------+--------------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+--------------------+---------------+-------+
| V1 + Initial value + + V, A |
+------+--------------------+---------------+-------+
| V2 + pulsed value + + V, A |
+------+--------------------+---------------+-------+
| Td1 + rise delay time + 0.0 + sec |
+------+--------------------+---------------+-------+
| tau1 + rise time constant + Tstep + sec |
+------+--------------------+---------------+-------+
| Td2 + fall delay time + Td1+Tstep + sec |
+------+--------------------+---------------+-------+
| tau2 + fall time constant + Tstep + sec |
+------+--------------------+---------------+-------+
Spice Syntax::
EXP ( V1 V2 TD1 TAU1 TD2 TAU2 )
The shape of the waveform is described by the following formula:
Let V21 = V2 - V1 and V12 = V1 - V2.
.. math::
V(t) = \begin{cases}
V_1 & \text{if}\ 0 \leq t < T_{d1}, \\
V_1 + V_{21} ( 1 − e^{-\frac{t-T_{d1}}{\tau_1}} )
& \text{if}\ T_{d1} \leq t < T_{d2}, \\
V_1 + V_{21} ( 1 − e^{-\frac{t-T_{d1}}{\tau_1}} ) + V_{12} ( 1 − e^{-\frac{t-T_{d2}}{\tau_2}} )
& \text{if}\ T_{d2} \leq t < T_{stop}
\end{cases}
"""
##############################################
def __init__(self, name, node_plus, node_minus,
initial_value, pulsed_value,
rise_delay_time=.0, rise_time_constant=None,
fall_delay_time=None, fall_time_constant=None,
):
# Fixme: default
super().__init__(name, node_plus, node_minus)
self.initial_value = as_V(initial_value)
self.pulsed_value = as_V(pulsed_value)
self.rise_delay_time = as_s(rise_delay_time)
self.rise_time_constant = as_s(rise_time_constant)
self.fall_delay_time = as_s(fall_delay_time)
self.fall_time_constant = as_s(fall_time_constant)
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('EXP(' +
join_list((self.initial_value, self.pulsed_value,
self.rise_delay_time, self.rise_time_constant,
self.fall_delay_time, self.fall_time_constant,
)) +
')')
####################################################################################################
class PieceWiseLinear(VoltageSource):
r"""This class implements a Piece-Wise Linear waveform.
Spice Syntax::
PWL( T1 V1 )
Each pair of values (Ti , Vi) specifies that the value of the source is Vi (in Volts or Amps) at
time = Ti . The value of the source at intermediate values of time is determined by using linear
interpolation on the input values. The parameter r determines a repeat time point. If r is not
given, the whole sequence of values (Ti , Vi ) is issued once, then the output stays at its
final value. If r = 0, the whole sequence from time = 0 to time = Tn is repeated forever. If r =
10ns, the sequence between 10ns and 50ns is repeated forever. the r value has to be one of the
time points T1 to Tn of the PWL sequence. If td is given, the whole PWL sequence is delayed by a
delay time time = td. The current source still needs to be patched, td and r are not yet
available.
"""
##############################################
def __init__(self, name, node_plus, node_minus,
values,
repeate_time=0, delay_time=.0,
):
# Fixme: default
super().__init__(name, node_plus, node_minus)
self.values = [as_V(x) for x in values]
self.repeate_time = as_s(repeate_time)
self.delay_time = as_s(delay_time)
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('PWL(' +
join_list(self.values) +
join_dict({'r':self.repeate_time, 'td':self.delay_time}) + # OrderedDict(
')')
####################################################################################################
class SingleFrequencyFM(VoltageSource):
r"""This class implements a Single-Frequency FM waveform.
Spice Syntax::
SFFM (VO VA FC MDI FS )
+------+-------------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+-------------------+---------------+-------+
| Vo + offset + + V, A |
+------+-------------------+---------------+-------+
| Va + amplitude + + V, A |
+------+-------------------+---------------+-------+
| Fc + carrier frequency + 1 / Tstop + Hz |
+------+-------------------+---------------+-------+
| Mdi + modulation index + + |
+------+-------------------+---------------+-------+
| Fs + signal frequency + 1 / Tstop + Hz |
+------+-------------------+---------------+-------+
The shape of the waveform is described by the following equation:
.. math::
V(t) = V_o + V_a \sin (2\pi F_c\, t + M_{di} \sin (2\pi F_s\,t))
"""
##############################################
def __init__(self, name, node_plus, node_minus,
offset, amplitude, carrier_frequency, modulation_index, signal_frequency):
super().__init__(name, node_plus, node_minus)
self.offset = as_V(offset)
self.amplitude = as_V(amplitude)
self.carrier_frequency = as_Hz(carrier_frequency)
self.modulation_index = modulation_index
self.signal_frequency = as_Hz(signal_frequency)
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('SFFM(' +
join_list((self.offset, self.amplitude, self.carrier_frequency,
self.modulation_index, self.signal_frequency)) +
')')
####################################################################################################
class AmplitudeModulated(VoltageSource):
r"""This class implements a Amplitude Modulated source.
+------+----------------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+----------------------+---------------+-------+
| Vo + offset + + V, A |
+------+----------------------+---------------+-------+
| Va + amplitude + + V, A |
+------+----------------------+---------------+-------+
| Mf + modulating frequency + + Hz |
+------+----------------------+---------------+-------+
| Fc + carrier frequency + 1 / Tstop + Hz |
+------+----------------------+---------------+-------+
| Td + signal delay + + s |
+------+----------------------+---------------+-------+
Spice Syntax::
AM(VA VO MF FC TD)
The shape of the waveform is described by the following equation:
.. math::
V(t) = V_a (V_o + \sin (2\pi M_f\,t)) \sin (2\pi F_c\,t)
"""
##############################################
def __init__(self, name, node_plus, node_minus,
offset, amplitude, modulating_frequency, carrier_frequency, signal_delay):
# Fixme: default
super().__init__(name, node_plus, node_minus)
self.offset = as_V(offset)
self.amplitude = as_V(amplitude)
self.carrier_frequency = as_Hz(carrier_frequency)
self.modulating_frequency = as_Hz(modulating_frequency)
self.signal_delay = as_s(signal_delay)
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('AM(' +
join_list((self.offset, self.amplitude, self.carrier_frequency,
self.modulating_frequency, self.signal_delay)) +
')')
####################################################################################################
class RandomVoltage(VoltageSource):
r"""This class implements a Random Voltage source.
The TRRANDOM option yields statistically distributed voltage values, derived from the ngspice
random number generator. These values may be used in the transient simulation directly within a
circuit, e.g. for generating a specific noise voltage, but especially they may be used in the
control of behavioral sources (B, E, G sources, voltage controllable A sources, capacitors,
inductors, or resistors) to simulate the circuit dependence on statistically varying device
parameters. A Monte-Carlo simulation may thus be handled in a single simulation run.
Spice Syntax::
TRRANDOM( TYPE TS
> >)
TYPE determines the random variates generated: 1 is uniformly distributed, 2 Gaussian, 3
exponential, 4 Poisson. TS is the duration of an individual voltage value. TD is a time delay
with 0 V output before the random voltage values start up. PARAM1 and PARAM2 depend on the type
selected.
+-------------+---------------+---------+-------------+---------+
| Type + Parameter 1 + Default + Parameter 2 + Default |
+-------------+---------------+---------+-------------+---------+
| uniform + range + 1 + offset + 0 |
+-------------+---------------+---------+-------------+---------+
| gaussian + standard dev. + 1 + mean + 0 |
+-------------+---------------+---------+-------------+---------+
| exponential + mean + 1 + offset + 0 |
+-------------+---------------+---------+-------------+---------+
| poisson + lambda + 1 + offset + 0 |
+-------------+---------------+---------+-------------+---------+
"""
##############################################
def __init__(self, name, node_plus, node_minus,
random_type, duration=0, time_delay=0, parameter1=1, parameter2=0):
# Fixme: random_type and parameters
super().__init__(name, node_plus, node_minus)
self.random_type = random_type
self.duration = as_s(duration)
self.time_delay = as_s(time_delay)
self.parameter1 = parameter1
self.parameter2 = parameter2
##############################################
def format_spice_parameters(self):
if self.random_type == 'uniform':
random_type = 1
elif self.random_type == 'exponential':
random_type = 2
elif self.random_type == 'gaussian':
random_type = 3
elif self.random_type == 'poisson':
random_type = 4
else:
raise ValueError("Wrong random type {}".format(self.random_type))
# Fixme: to func?
return ('TRRANDOM(' +
join_list((random_type, self.duration, self.time_delay,
self.parameter1, self.parameter2)) +
')')
PySpice-v0.4.1/PySpice/Spice/Library.py 0000664 0000000 0000000 00000007016 13140600222 0017645 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import logging
####################################################################################################
from ..Tools.File import Directory
from .Parser import SpiceParser
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class SpiceLibrary:
"""This class implements a Spice sub-circuits and models library.
A library is a directory which is recursively scanned for '.lib' file and parsed for sub-circuit
and models definitions.
Example of usage::
spice_library = SpiceLibrary('/some/path/')
If the directory hierarchy contains a file that define a 1N4148 sub-circuit then we can retrieve
the file path using::
spice_library['1N4148']
"""
_logger = _module_logger.getChild('Library')
##############################################
def __init__(self, root_path):
self._directory = Directory(root_path).expand_vars_and_user()
self._subcircuits = {}
self._models = {}
for path in self._directory.iter_file():
if path.extension.lower() in ('.lib', '.mod'):
self._logger.debug("Parse {}".format(path))
spice_parser = SpiceParser(path)
if spice_parser.is_only_subcircuit():
for subcircuit in spice_parser.subcircuits:
self._subcircuits[subcircuit.name] = path
elif spice_parser.is_only_model():
for model in spice_parser.models:
self._models[model.name] = path
##############################################
def __getitem__(self, name):
if name in self._subcircuits:
return self._subcircuits[name]
elif name in self._models:
return self._models[name]
else:
raise KeyError(name)
##############################################
@property
def subcircuits(self):
""" Dictionary of sub-circuits """
return iter(self._subcircuits)
@property
def models(self):
""" Dictionary of models """
return iter(self._models)
# ##############################################
# def iter_on_subcircuits(self):
# return self._subcircuits.itervalues()
# ##############################################
# def iter_on_models(self):
# return self._models.itervalues()
PySpice-v0.4.1/PySpice/Spice/Netlist.py 0000664 0000000 0000000 00000066633 13140600222 0017675 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This modules implements circuit and subcircuit.
The definition of a netlist follows the same conventions as SPICE. For example this SPICE netlist
is translated to Python like this:
.. code-block:: spice
.title Voltage Divider
Vinput in 0 10V
R1 in out 9k
R2 out 0 1k
.end
.. code-block:: python3
circuit = Circuit('Voltage Divider')
circuit.V('input', 'in', circuit.gnd, 10)
circuit.R(1, 'in', 'out', kilo(9))
circuit.R(2, 'out', circuit.gnd, kilo(1))
or as a class definition:
.. code-block:: python3
class VoltageDivider(Circuit):
def __init__(self, **kwargs):
super().__init__(title='Voltage Divider', **kwargs)
self.V('input', 'in', self.gnd, '10V')
self.R(1, 'in', 'out', kilo(9))
self.R(2, 'out', self.gnd, kilo(1))
The circuit attribute :attr:`gnd` represents the ground of the circuit or subcircuit, usually set to
0.
We can get an element or a model using its name using these two possibilities::
circuit['R1'] # dictionnary style
circuit.R1 # attribute style
The dictionnary style always works, but the attribute only works if it complies with the Python
syntax, i.e. the element or model name is a valide attribute name (identifier), i.e. starting by a
letter and not a keyword like 'in', cf. `Python Language Reference
`_.
We can update an element parameter like this::
circuit.R1.resistance = kilo(1)
To simulate the circuit, we must create a simulator instance using the :meth:`Circuit.simulator`::
simulator = circuit.simulator()
"""
####################################################################################################
#
# Graph:
# dipole
# n-pole: transistor (be, bc) ?
#
# circuit -> element -> node
# circuit.Q1.b
# Element -> ElementQ
# use prefix?
#
####################################################################################################
####################################################################################################
from collections import OrderedDict
import keyword
import logging
import os
# import networkx
####################################################################################################
from ..Tools.StringTools import join_lines, join_list, join_dict
from .ElementParameter import (ParameterDescriptor,
PositionalElementParameter,
FlagParameter, KeyValueParameter)
from .Simulation import SubprocessCircuitSimulator, NgSpiceSharedCircuitSimulator
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class DeviceModel:
""" This class implements a device model.
Ngspice model types:
+------+-------------------------------+
| Code + Model Type |
+------+-------------------------------+
| R + Semiconductor resistor model |
+------+-------------------------------+
| C + Semiconductor capacitor model |
+------+-------------------------------+
| L + Inductor model |
+------+-------------------------------+
| SW + Voltage controlled switch |
+------+-------------------------------+
| CSW + Current controlled switch |
+------+-------------------------------+
| URC + Uniform distributed RC model |
+------+-------------------------------+
| LTRA + Lossy transmission line model |
+------+-------------------------------+
| D + Diode model |
+------+-------------------------------+
| NPN + NPN BJT model |
+------+-------------------------------+
| PNP + PNP BJT model |
+------+-------------------------------+
| NJF + N-channel JFET model |
+------+-------------------------------+
| PJF + P-channel JFET model |
+------+-------------------------------+
| NMOS + N-channel MOSFET model |
+------+-------------------------------+
| PMOS + P-channel MOSFET model |
+------+-------------------------------+
| NMF + N-channel MESFET model |
+------+-------------------------------+
| PMF + P-channel MESFET model |
+------+-------------------------------+
"""
##############################################
def __init__(self, name, modele_type, **parameters):
self._name = str(name)
self._model_type = str(modele_type)
self._parameters = dict(**parameters)
##############################################
@property
def name(self):
return self._name
##############################################
@property
def model_type(self):
return self._model_type
##############################################
def __repr__(self):
return str(self.__class__) + ' ' + self.name
##############################################
def __str__(self):
return ".model {} {} ({})".format(self._name, self._model_type, join_dict(self._parameters))
####################################################################################################
class Pin:
"""This class implements a pin of an element. It stores a reference to the element, the name of the
pin and the node.
"""
_logger = _module_logger.getChild('Pin')
##############################################
def __init__(self, element, name, node):
if keyword.iskeyword(node):
self._logger.warning("Node {} is a Python keyword".format(node))
self._element = element
self._name = name
self._node = node # Fixme: name, not a Node instance, cf. Netlist.nodes
##############################################
@property
def element(self):
return self._element
##############################################
@property
def name(self):
return self._name
##############################################
@property
def node(self):
return self._node
##############################################
def __repr__(self):
return "Pin {} of {} on node {}".format(self._name, self._element.name, self._node)
##############################################
def add_current_probe(self, circuit):
"""Add a current probe between the node and the pin.
The ammeter is named *ElementName_PinName*.
"""
# Fixme: require a reference to circuit
# Fixme: add it to a list
node = self._node
self._node = '_'.join((self._element.name, self._name))
circuit.V(self._node, node, self._node, '0')
####################################################################################################
class ElementParameterMetaClass(type):
""" Metaclass to implements the element parameter machinery. """
__classes__ = {}
##############################################
def __new__(cls, class_name, base_classes, attributes):
# Collect positional and optional parameters from class attribute dict
positional_parameters = {}
parameters = {}
for attribute_name, obj in attributes.items():
if isinstance(obj, ParameterDescriptor):
obj.attribute_name = attribute_name
if isinstance(obj, PositionalElementParameter):
d = positional_parameters
elif isinstance(obj, (FlagParameter, KeyValueParameter)):
d = parameters
# else:
# raise NotImplementedError
d[attribute_name] = obj
# Build dictionnary : attribute_name -> parameter
attributes['_positional_parameters'] = OrderedDict(sorted(list(positional_parameters.items()),
key=lambda t: t[1].position))
# optional parameter order is not required for SPICE, but for unit test
attributes['_optional_parameters'] = OrderedDict(sorted(list(parameters.items()), key=lambda t: t[0]))
# Positional parameter array
attributes['_parameters_from_args'] = [parameter
for parameter in sorted(positional_parameters.values())
if not parameter.key_parameter]
# Implement alias for parameters
attributes['_spice_to_parameters'] = {parameter.spice_name:parameter
for parameter in attributes['_optional_parameters'].values()}
for parameter in attributes['_spice_to_parameters'].values():
if (parameter.spice_name in attributes
and parameter.spice_name != parameter.attribute_name):
_module_logger.error('Spice parameter "{}" clash with attributes'.format(parameter.spice_name))
return super().__new__(cls, class_name, base_classes, attributes)
##############################################
def __init__(cls, class_name, base_classes, attributes):
type.__init__(cls, class_name, base_classes, attributes)
# Collect basic element classes
if 'prefix' in attributes:
prefix = attributes['prefix']
if prefix is not None:
classes = ElementParameterMetaClass.__classes__
if prefix in classes:
classes[prefix].append(cls)
else:
classes[prefix] = [cls]
##############################################
# Notes: These properties are only accessible from the class object, e.g. instance.__class__
@property
def number_of_pins(cls):
return cls._number_of_pins
@property
def number_of_positional_parameters(cls):
return len(cls._positional_parameters)
@property
def positional_parameters(cls):
return cls._positional_parameters
@property
def optional_parameters(cls):
return cls._optional_parameters
@property
def parameters_from_args(cls):
return cls._parameters_from_args
@property
def spice_to_parameters(cls):
return cls._spice_to_parameters
####################################################################################################
class Element(metaclass=ElementParameterMetaClass):
""" This class implements a base class for an element.
It use a metaclass machinery for the declaration of the parameters.
"""
# These attributes are defined in subclasses or via the metaclass.
_number_of_pins = None
_positional_parameters = None
_optional_parameters = None
_parameters_from_args = None
_spice_to_parameters = None
# Fixme: _prefix
#: SPICE element prefix
prefix = None
##############################################
def __init__(self, name, pins, *args, **kwargs):
self._name = str(name)
self._pins = list(pins) # Fixme: pins is not a ordered dict, cf. property
# self._parameters = list(args)
for parameter, value in zip(self._parameters_from_args, args):
setattr(self, parameter.attribute_name, value)
for key, value in kwargs.items():
if key in self._positional_parameters or self._optional_parameters:
setattr(self, key, value)
##############################################
@property
def name(self):
return self.prefix + self._name
@property
def pins(self):
return self._pins
##############################################
@property
def nodes(self):
return [pin.node for pin in self._pins]
##############################################
def __repr__(self):
return self.__class__.__name__ + ' ' + self.name
##############################################
def __setattr__(self, name, value):
# Implement alias for parameters
if name in self._spice_to_parameters:
parameter = self._spice_to_parameters[name]
object.__setattr__(self, parameter.attribute_name, value)
else:
object.__setattr__(self, name, value)
##############################################
def __getattr__(self, name):
# Implement alias for parameters
if name in self._spice_to_parameters:
parameter = self._spice_to_parameters[name]
return object.__getattribute__(self, parameter.attribute_name)
else:
raise AttributeError(name)
##############################################
def format_node_names(self):
""" Return the formatted list of nodes. """
return join_list((self.name, join_list(self.nodes)))
##############################################
def parameter_iterator(self):
""" This iterator returns the parameter in the right order. """
for parameter_dict in self._positional_parameters, self._optional_parameters:
for parameter in parameter_dict.values():
if parameter.nonzero(self):
yield parameter
##############################################
# @property
# def parameters(self):
# return self._parameters
##############################################
def format_spice_parameters(self):
""" Return the formatted list of parameters. """
return join_list([parameter.to_str(self) for parameter in self.parameter_iterator()])
##############################################
def __str__(self):
""" Return the SPICE element definition. """
return join_list((self.format_node_names(), self.format_spice_parameters()))
####################################################################################################
class NPinElement(Element):
pass
####################################################################################################
class AnyPinElement(Element):
_number_of_pins = 0
####################################################################################################
class TwoPinElement(Element):
""" This class implements a base class for a two-pin element. """
# dipole
_number_of_pins = 2
##############################################
def __init__(self, name, node_plus, node_minus, *args, **kwargs):
pins = (Pin(self, 'plus', node_plus), Pin(self, 'minus', node_minus))
super().__init__(name, pins, *args, **kwargs)
##############################################
@property
def plus(self):
return self.pins[0]
##############################################
@property
def minus(self):
return self.pins[1]
####################################################################################################
class ThreePinElement(Element):
_number_of_pins = 3
####################################################################################################
class FourPinElement(Element):
_number_of_pins = 4
####################################################################################################
class TwoPortElement(Element):
""" This class implements a base class for a two-port element.
.. warning:: As opposite to Spice, the input nodes are specified before the output nodes.
"""
_number_of_pins = 4
##############################################
# Fixme: Why the order the inverted ?
def __init__(self, name,
input_node_plus, input_node_minus,
output_node_plus, output_node_minus,
*args, **kwargs):
pins = (Pin(self, 'output_plus', output_node_plus),
Pin(self, 'output_minus', output_node_minus),
Pin(self, 'input_plus', input_node_plus),
Pin(self, 'input_minus', input_node_minus))
super().__init__(name, pins, *args, **kwargs)
##############################################
@property
def output_plus(self):
return self.pins[0]
##############################################
@property
def output_minus(self):
return self.pins[1]
##############################################
@property
def input_plus(self):
return self.pins[2]
##############################################
@property
def input_minus(self):
return self.pins[3]
####################################################################################################
class Node:
"""This class implements a node in the circuit. It stores a reference to the elements connected to
the node."""
# Fixme: but not directly to the pins!
##############################################
def __init__(self, name):
self._name = str(name)
self._elements = set()
##############################################
def __repr__(self):
return 'Node {}'.format(self._name)
##############################################
@property
def name(self):
return self._name
@property
def elements(self):
return self._elements
##############################################
def add_element(self, element):
self._elements.add(element)
####################################################################################################
class Netlist:
""" This class implements a base class for a netlist.
.. note:: This class is completed at running time with elements.
"""
##############################################
def __init__(self):
self._ground = None
self._elements = OrderedDict() # to keep the declaration order
self._models = {}
self._dirty = True
# self._nodes = set()
self._nodes = {}
# self._graph = networkx.Graph()
##############################################
def element_iterator(self):
return iter(self._elements.values())
##############################################
def element_names(self):
return [element.name for element in self.element_iterator()]
##############################################
def model_iterator(self):
return iter(self._models.values())
##############################################
def __str__(self):
""" Return the formatted list of element and model definitions. """
netlist = join_lines(self.element_iterator()) + '\n'
if self._models:
netlist += join_lines(self.model_iterator()) + '\n'
return netlist
##############################################
def _add_element(self, element):
"""Add an element."""
if element.name not in self._elements:
self._elements[element.name] = element
self._dirty = True
else:
raise NameError("Element name {} is already defined".format(element.name))
##############################################
def model(self, name, modele_type, **parameters):
"""Add a model."""
model = DeviceModel(name, modele_type, **parameters)
if model.name not in self._models:
self._models[model.name] = model
else:
raise NameError("Model name {} is already defined".format(name))
##############################################
@property
def nodes(self):
"""Return the nodes."""
if self._dirty:
# nodes = set()
# for element in self.element_iterator():
# nodes |= set(element.nodes)
# if self._ground is not None:
# nodes -= set((self._ground,))
# self._nodes = nodes
self._nodes.clear()
for element in self.element_iterator():
for node_name in element.nodes:
if node_name not in self._nodes:
node = Node(node_name)
self._nodes[node_name] = node
else:
node = self._nodes[node_name]
node.add_element(element)
return list(self._nodes.values())
##############################################
def node_names(self):
return [node.name for node in self.nodes]
##############################################
def __getitem__(self, attribute_name):
if attribute_name in self._elements:
return self._elements[attribute_name]
elif attribute_name in self._models:
return self._models[attribute_name]
elif attribute_name in self._nodes:
return attribute_name
else:
raise IndexError(attribute_name)
##############################################
def __getattr__(self, attribute_name):
try:
return self.__getitem__(attribute_name)
except IndexError:
raise AttributeError(attribute_name)
####################################################################################################
class SubCircuit(Netlist):
"""This class implements a sub-cicuit netlist."""
##############################################
def __init__(self, name, *nodes, **kwargs):
super().__init__()
self.name = str(name)
self._external_nodes = list(nodes)
# Fixme: ok ?
self._ground = kwargs.get('ground', 0)
if 'ground' in kwargs:
del kwargs['ground']
self._parameters = kwargs
##############################################
@property
def gnd(self):
""" Local ground """
return self._ground
##############################################
@property
def parameters(self):
"""Parameters"""
return self._parameters
##############################################
def check_nodes(self):
"""Check for dangling nodes in the subcircuit."""
nodes = set(self._external_nodes)
connected_nodes = set()
for element in self.element_iterator():
connected_nodes.add(nodes & element.nodes)
not_connected_nodes = nodes - connected_nodes
if not_connected_nodes:
raise NameError("SubCircuit Nodes {} are not connected".format(not_connected_nodes))
##############################################
def __str__(self):
"""Return the formatted subcircuit definition."""
nodes = join_list(self._external_nodes)
parameters = join_list(['{}={}'.format(key, value)
for key, value in self._parameters.items()])
netlist = '.subckt ' + join_list((self.name, nodes, parameters)) + '\n'
netlist += super().__str__()
netlist += '.ends ' + self.name + '\n'
return netlist
####################################################################################################
class SubCircuitFactory(SubCircuit):
# Fixme : versus SubCircuit
__name__ = None
__nodes__ = None
##############################################
def __init__(self, **kwargs):
super().__init__(self.__name__, *self.__nodes__, **kwargs)
####################################################################################################
class Circuit(Netlist):
"""This class implements a cicuit netlist.
To get the corresponding Spice netlist use::
circuit = Circuit()
...
str(circuit)
"""
# .lib
# .func
# .csparam
##############################################
def __init__(self, title,
ground=0,
global_nodes=(),
):
super().__init__()
self.title = str(title)
self._ground = ground
self._global_nodes = set(global_nodes) # .global
self._includes = [] # .include
self._parameters = {} # .param
self._subcircuits = {}
# Fixme: not implemented
# .func
# .csparam
# .if
##############################################
@property
def gnd(self):
return self._ground
##############################################
def include(self, path):
"""Include a file."""
if path not in self._includes:
self._includes.append(path)
else:
self._logger.warn("Duplicated include")
##############################################
def parameter(self, name, expression):
"""Set a parameter."""
self._parameters[str(name)] = str(expression)
##############################################
def subcircuit(self, subcircuit):
"""Add a sub-circuit."""
self._subcircuits[str(subcircuit.name)] = subcircuit
##############################################
def subcircuit_iterator(self):
"""Return a sub-circuit iterator."""
return iter(self._subcircuits.values())
##############################################
def __str__(self):
"""Return the formatted desk."""
netlist = '.title {}\n'.format(self.title)
if self._includes:
# ngspice don't like // in path, thus ensure we write real paths
real_paths = [os.path.realpath(str(path)) for path in self._includes]
netlist += join_lines(real_paths, prefix='.include ') + '\n'
if self._global_nodes:
netlist += '.global ' + join_list(self._global_nodes) + '\n'
if self._parameters:
netlist += join_lines(self._parameters, prefix='.param ') + '\n'
if self._subcircuits:
netlist += join_lines(self.subcircuit_iterator())
netlist += super().__str__()
return netlist
##############################################
def str_end(self):
return str(self) + '.end\n'
##############################################
def simulator(self, *args, **kwargs):
"""Return a :obj:`PySpice.Spice.Simulation.SubprocessCircuitSimulator` or
:obj:`PySpice.Spice.Simulation.NgSpiceSharedCircuitSimulator` instance depending of the
value of the *simulator* parameter: ``subprocess`` or ``shared``, respectively. If this
parameter is not specified then a subprocess simulator is returned.
"""
simulator = kwargs.get('simulator', 'subprocess')
if 'simulator' in kwargs:
simulator = kwargs['simulator']
del kwargs['simulator']
else:
simulator = 'subprocess'
if simulator == 'subprocess':
return SubprocessCircuitSimulator(self, *args, **kwargs)
elif simulator == 'shared':
return NgSpiceSharedCircuitSimulator(self, *args, **kwargs)
else:
return ValueError('Unknown simulator type')
PySpice-v0.4.1/PySpice/Spice/NgSpice/ 0000775 0000000 0000000 00000000000 13140600222 0017213 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Spice/NgSpice/Shared.py 0000664 0000000 0000000 00000052055 13140600222 0021002 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This module provides a Python interface to the Ngspice shared library described in the *ngspice
as shared library or dynamic link library* section of the Ngspice user manual.
In comparison to the subprocess interface, it provides an interaction with the simulator through
commands and callbacks and it enables the usage of external voltage and current source in the
circuit.
.. This approach corresponds to the *standard way* to make an interface to a simulator code.
.. warning:: Since we don't simulate a circuit in a fresh environment on demand, this approach is
less safe than the subprocess approach. In case of bugs within Ngspice, we can expect some side
effects like memory leaks or worse unexpected things.
This interface use the CFFI module to interface with the shared library. It is thus suited to run
within the Pypy interpreter which implements JIT optimisation for Python.
It can also be used to experiment parallel simulation as explained in the Ngspice user manual. But
it seems the Ngspice source code was designed with global variables which imply to use one copy of
the shared library by worker as explained in the manual.
.. warning:: This interface can strongly slow down the simulation if the input or output callbacks
is used. If the simulation time goes wrong for you then you need to implement the callbacks at a
lower level than Python. You can have look to Pypy, Cython or a C extension module.
"""
####################################################################################################
import logging
import os
import time
import numpy as np
####################################################################################################
from cffi import FFI
ffi = FFI()
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
from PySpice.Probe.WaveForm import (OperatingPoint, SensitivityAnalysis,
DcAnalysis, AcAnalysis, TransientAnalysis,
WaveForm)
from PySpice.Tools.EnumFactory import EnumFactory
####################################################################################################
def ffi_string_utf8(x):
return ffi.string(x).decode('utf8')
####################################################################################################
class Vector:
""" This class implements a vector in a simulation output.
Public Attributes:
:attr:`data`
:attr:`name`
:attr:`type`
cf. `NgSpiceShared.simulation_type`
"""
##############################################
def __init__(self, name, type_, data):
self.name = str(name)
self.type = type_
self.data = data
##############################################
def __repr__(self):
return 'variable: {0.name} {0.type}'.format(self)
##############################################
def is_voltage_node(self):
return self.type == NgSpiceShared.simulation_type.voltage
##############################################
def is_branch_current(self):
return self.type == NgSpiceShared.simulation_type.current
##############################################
@property
def simplified_name(self):
if self.is_branch_current():
# return self.name.replace('#branch', '')
return self.name[:-7]
else:
return self.name
##############################################
@property
def unit(self):
if self.type == NgSpiceShared.simulation_type.voltage:
return 'V'
elif self.type == NgSpiceShared.simulation_type.current:
return 'A'
elif self.type == NgSpiceShared.simulation_type.time:
return 's'
elif self.type == NgSpiceShared.simulation_type.frequency:
return 'Hz'
else:
return ''
##############################################
def to_waveform(self, abscissa=None, to_real=False, to_float=False):
""" Return a :obj:`PySpice.Probe.WaveForm` instance. """
data = self.data
if to_real:
data = data.real
if to_float:
data = float(data[0])
return WaveForm(self.simplified_name, self.unit, data, abscissa=abscissa)
####################################################################################################
class Plot(dict):
""" This class implements a plot in a simulation output.
Public Attributes:
:attr:`plot_name`
"""
##############################################
def __init__(self, simulation, plot_name):
super().__init__()
self._simulation = simulation
self.plot_name = plot_name
##############################################
def nodes(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.values()
if variable.is_voltage_node()]
##############################################
def branches(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.values()
if variable.is_branch_current()]
##############################################
def elements(self, abscissa=None):
return [variable.to_waveform(abscissa, to_float=True)
for variable in self.values()]
##############################################
def to_analysis(self):
if self.plot_name.startswith('op'):
return self._to_operating_point_analysis()
elif self.plot_name.startswith('sens'):
return self._to_sensitivity_analysis()
elif self.plot_name.startswith('dc'):
return self._to_dc_analysis()
elif self.plot_name.startswith('ac'):
return self._to_ac_analysis()
elif self.plot_name.startswith('tran'):
return self._to_transient_analysis()
else:
raise NotImplementedError("Unsupported plot name {}".format(self.plot_name))
##############################################
def _to_operating_point_analysis(self):
return OperatingPoint(
simulation=self._simulation,
nodes=self.nodes(to_float=True),
branches=self.branches(to_float=True),
)
##############################################
def _to_sensitivity_analysis(self):
# Fixme: separate v(vinput), analysis.R2.m
return SensitivityAnalysis(
simulation=self._simulation,
elements=self.elements(),
)
##############################################
def _to_dc_analysis(self):
if 'v(v-sweep)' in self:
sweep_variable = self['v(v-sweep)']
elif 'v(i-sweep)' in self:
sweep_variable = self['v(i-sweep)']
else:
raise NotImplementedError
sweep = sweep_variable.to_waveform()
return DcAnalysis(
simulation=self._simulation,
sweep=sweep,
nodes=self.nodes(),
branches=self.branches(),
)
##############################################
def _to_ac_analysis(self):
frequency = self['frequency'].to_waveform(to_real=True)
return AcAnalysis(
simulation=self._simulation,
frequency=frequency,
nodes=self.nodes(),
branches=self.branches(),
)
##############################################
def _to_transient_analysis(self):
time = self['time'].to_waveform(to_real=True)
return TransientAnalysis(
simulation=self._simulation,
time=time,
nodes=self.nodes(abscissa=time),
branches=self.branches(abscissa=time),
)
####################################################################################################
class NgSpiceShared:
_logger = _module_logger.getChild('NgSpiceShared')
simulation_type = EnumFactory('SimulationType', (
'no_type',
'time',
'frequency',
'voltage',
'current',
'output_noise_density',
'output_noise',
'input_noise_density',
'input_noise',
'pole',
'zero',
's_parameter',
'temperature',
'res',
'impedance',
'admittance',
'power',
'phase',
'db',
'capacitance',
'charge'))
##############################################
def __init__(self, ngspice_id=0, send_data=False):
""" Set the *send_data* flag if you want to enable the output callback.
Set the *ngspice_id* to an integer value if you want to run NgSpice in parallel.
"""
self._ngspice_id = ngspice_id
self._load_library()
self._init_ngspice(send_data)
##############################################
def _load_library(self):
api_path = os.path.join(os.path.dirname(__file__), 'api.h')
with open(api_path) as f:
ffi.cdef(f.read())
if not self._ngspice_id:
library_prefix = ''
else:
library_prefix = '{}'.format(self._ngspice_id)
library_file = 'libngspice{}.so'.format(library_prefix)
self._ngspice_shared = ffi.dlopen(library_file)
##############################################
def _init_ngspice(self, send_data):
self._send_char_c = ffi.callback('int (char *, int, void *)', self._send_char)
self._send_stat_c = ffi.callback('int (char *, int, void *)', self._send_stat)
self._exit_c = ffi.callback('int (int, bool, bool, int, void *)', self._exit)
self._send_init_data_c = ffi.callback('int (pvecinfoall, int, void *)', self._send_init_data)
if send_data:
self._send_data_c = ffi.callback('int (pvecvaluesall, int, int, void *)', self._send_data)
else:
self._send_data_c = ffi.NULL
self._get_vsrc_data_c = ffi.callback('int (double *, double, char *, int, void *)', self._get_vsrc_data)
self._get_isrc_data_c = ffi.callback('int (double *, double, char *, int, void *)', self._get_isrc_data)
self_c = ffi.new_handle(self)
self._self_c = self_c # To prevent garbage collection
rc = self._ngspice_shared.ngSpice_Init(self._send_char_c,
self._send_stat_c,
self._exit_c,
self._send_data_c,
self._send_init_data_c,
ffi.NULL, # BGThreadRunning
self_c)
if rc:
raise NameError("Ngspice_Init returned {}".format(rc))
ngspice_id_c = ffi.new('int *', self._ngspice_id)
self._ngspice_id = ngspice_id_c # To prevent garbage collection
rc = self._ngspice_shared.ngSpice_Init_Sync(self._get_vsrc_data_c,
self._get_isrc_data_c,
ffi.NULL, # GetSyncData
ngspice_id_c,
self_c)
if rc:
raise NameError("Ngspice_Init_Sync returned {}".format(rc))
##############################################
@staticmethod
def _send_char(message, ngspice_id, user_data):
"""FFI Callback"""
self = ffi.from_handle(user_data)
return self.send_char(ffi_string_utf8(message), ngspice_id)
##############################################
@staticmethod
def _send_stat(message, ngspice_id, user_data):
"""FFI Callback"""
self = ffi.from_handle(user_data)
return self.send_stat(ffi_string_utf8(message), ngspice_id)
##############################################
@staticmethod
def _exit(exit_status, immediate_unloding, quit_exit, ngspice_id, user_data):
"""FFI Callback"""
self = ffi.from_handle(user_data)
self._logger.debug('ngspice_id-{} exit {} {} {} {}'.format(ngspice_id,
exit_status,
immediate_unloding,
quit_exit))
return exit_status
##############################################
@staticmethod
def _send_data(data, number_of_vectors, ngspice_id, user_data):
"""FFI Callback"""
self = ffi.from_handle(user_data)
self._logger.debug('ngspice_id-{} send_data [{}]'.format(ngspice_id, data.vecindex))
actual_vector_values = {}
for i in range(int(number_of_vectors)):
actual_vector_value = data.vecsa[i]
vector_name = ffi_string_utf8(actual_vector_value.name)
value = complex(actual_vector_value.creal, actual_vector_value.cimag)
actual_vector_values[vector_name] = value
self._logger.debug(' Vector: {} {}'.format(vector_name, value))
return self.send_data(actual_vector_values, number_of_vectors, ngspice_id)
##############################################
@staticmethod
def _send_init_data(data, ngspice_id, user_data):
"""FFI Callback"""
self = ffi.from_handle(user_data)
if self._logger.isEnabledFor(logging.DEBUG):
self._logger.debug('ngspice_id-{} send_init_data'.format(ngspice_id))
number_of_vectors = data.veccount
for i in range(number_of_vectors):
self._logger.debug(' Vector: ' + ffi_string_utf8(data.vecs[i].vecname))
return self.send_init_data(data, ngspice_id) # Fixme: should be a Python object
##############################################
@staticmethod
def _get_vsrc_data(voltage, time, node, ngspice_id, user_data):
"""FFI Callback"""
self = ffi.from_handle(user_data)
return self.get_vsrc_data(voltage, time, ffi_string_utf8(node), ngspice_id)
##############################################
@staticmethod
def _get_isrc_data(current, time, node, ngspice_id, user_data):
"""FFI Callback"""
self = ffi.from_handle(user_data)
return self.get_isrc_data(current, time, ffi_string_utf8(node), ngspice_id)
##############################################
def send_char(self, message, ngspice_id):
""" Reimplement this callback in a subclass to process logging messages from the simulator. """
self._logger.debug('ngspice-{} send_char {}'.format(ngspice_id, message))
return 0
##############################################
def send_stat(self, message, ngspice_id):
""" Reimplement this callback in a subclass to process statistic messages from the simulator. """
self._logger.debug('ngspice-{} send_stat {}'.format(ngspice_id, message))
return 0
##############################################
def send_data(self, actual_vector_values, number_of_vectors, ngspice_id):
""" Reimplement this callback in a subclass to process the vector actual values. """
return 0
##############################################
def send_init_data(self, data, ngspice_id):
""" Reimplement this callback in a subclass to process the initial data. """
return 0
##############################################
def get_vsrc_data(self, voltage, time, node, ngspice_id):
""" Reimplement this callback in a subclass to provide external voltage source. """
self._logger.debug('ngspice_id-{} get_vsrc_data @{} node {}'.format(ngspice_id, time, node))
return 0
##############################################
def get_isrc_data(self, current, time, node, ngspice_id):
""" Reimplement this callback in a subclass to provide external current source. """
self._logger.debug('ngspice_id-{} get_isrc_data @{} node {}'.format(ngspice_id, time, node))
return 0
##############################################
def load_circuit(self, circuit):
""" Load the given circuit string. """
circuit_lines = [line for line in str(circuit).split('\n') if line]
circuit_lines_keepalive = [ffi.new("char[]", line.encode('utf8'))
for line in circuit_lines]
circuit_lines_keepalive += [ffi.NULL]
circuit_array = ffi.new("char *[]", circuit_lines_keepalive)
rc = self._ngspice_shared.ngSpice_Circ(circuit_array)
if rc:
raise NameError("ngSpice_Circ returned {}".format(rc))
# for line in circuit_lines:
# rc = self._ngspice_shared.ngSpice_Command('circbyline ' + line)
# if rc:
# raise NameError("ngSpice_Command circbyline returned {}".format(rc))
##############################################
def run(self):
""" Run the simulation in the background thread and wait until the simulation is done. """
rc = self._ngspice_shared.ngSpice_Command(b'bg_run')
if rc:
raise NameError("ngSpice_Command bg_run returned {}".format(rc))
time.sleep(.1) # required before to test if the simulation is running
while (self._ngspice_shared.ngSpice_running()):
time.sleep(.1)
self._logger.debug("Simulation is done")
##############################################
def _convert_string_array(self, array):
strings = []
i = 0
while (True):
if array[i] == ffi.NULL:
break
else:
strings.append(ffi_string_utf8(array[i]))
i += 1
return strings
##############################################
@property
def plot_names(self):
""" Return the list of plot names. """
return self._convert_string_array(self._ngspice_shared.ngSpice_AllPlots())
##############################################
def plot(self, simulation, plot_name):
""" Return the corresponding plot. """
plot = Plot(simulation, plot_name)
all_vectors_c = self._ngspice_shared.ngSpice_AllVecs(plot_name.encode('utf8'))
i = 0
while (True):
if all_vectors_c[i] == ffi.NULL:
break
else:
vector_name = ffi_string_utf8(all_vectors_c[i])
name = '.'.join((plot_name, vector_name))
vector_info = self._ngspice_shared.ngGet_Vec_Info(name.encode('utf8'))
vector_type = vector_info.v_type
length = vector_info.v_length
self._logger.debug("vector[{}] {} type {} flags {} length {}".format(i,
vector_name,
vector_type,
vector_info.v_flags,
length))
# flags: VF_REAL = 1 << 0, VF_COMPLEX = 1 << 1
if vector_info.v_compdata == ffi.NULL:
# for k in xrange(length):
# print(" [{}] {}".format(k, vector_info.v_realdata[k]))
array = np.frombuffer(ffi.buffer(vector_info.v_realdata, length*8), dtype=np.float64)
else:
# for k in xrange(length):
# value = vector_info.v_compdata[k]
# print(ffi.addressof(value, field='cx_real'), ffi.addressof(value, field='cx_imag'))
# print(" [{}] {} + i {}".format(k, value.cx_real, value.cx_imag))
tmp_array = np.frombuffer(ffi.buffer(vector_info.v_compdata, length*8*2), dtype=np.float64)
array = np.array(tmp_array[0::2], dtype=np.complex64)
array.imag = tmp_array[1::2]
plot[vector_name] = Vector(vector_name, self.simulation_type[vector_type], array)
i += 1
return plot
PySpice-v0.4.1/PySpice/Spice/NgSpice/__init__.py 0000664 0000000 0000000 00000001627 13140600222 0021332 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/PySpice/Spice/NgSpice/api.h 0000664 0000000 0000000 00000003433 13140600222 0020140 0 ustar 00root root 0000000 0000000 /* Simplified Ngspice API for CFFI parser */
typedef struct ngcomplex
{
double cx_real;
double cx_imag;
} ngcomplex_t;
typedef struct vector_info
{
char *v_name;
int v_type;
short v_flags;
double *v_realdata;
ngcomplex_t *v_compdata;
int v_length;
} vector_info, *pvector_info;
typedef struct vecvalues
{
char *name;
double creal;
double cimag;
bool is_scale;
bool is_complex;
} vecvalues, *pvecvalues;
typedef struct vecvaluesall
{
int veccount;
int vecindex;
pvecvalues *vecsa;
} vecvaluesall, *pvecvaluesall;
typedef struct vecinfo
{
int number;
char *vecname;
bool is_real;
void *pdvec;
void *pdvecscale;
} vecinfo, *pvecinfo;
typedef struct vecinfoall
{
char *name;
char *title;
char *date;
char *type;
int veccount;
pvecinfo *vecs;
} vecinfoall, *pvecinfoall;
typedef int (SendChar) (char *, int, void *);
typedef int (SendStat) (char *, int, void *);
typedef int (ControlledExit) (int, bool, bool, int, void *);
typedef int (SendData) (pvecvaluesall, int, int, void *);
typedef int (SendInitData) (pvecinfoall, int, void *);
typedef int (BGThreadRunning) (bool, int, void *);
typedef int (GetVSRCData) (double *, double, char *, int, void *);
typedef int (GetISRCData) (double *, double, char *, int, void *);
typedef int (GetSyncData) (double, double *, double, int, int, int, void *);
int ngSpice_Init (SendChar *, SendStat *, ControlledExit *, SendData *, SendInitData *, BGThreadRunning *, void *);
int ngSpice_Init_Sync (GetVSRCData *, GetISRCData *, GetSyncData *, int *, void *);
int ngSpice_Command (char *);
pvector_info ngGet_Vec_Info (char *);
int ngSpice_Circ (char **);
char *ngSpice_CurPlot (void);
char **ngSpice_AllPlots (void);
char **ngSpice_AllVecs (char *);
bool ngSpice_running (void);
bool ngSpice_SetBkpt (double);
/* End */
PySpice-v0.4.1/PySpice/Spice/Parser.py 0000664 0000000 0000000 00000055465 13140600222 0017510 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
"""This module implements a partial SPICE netlist parser.
It would be difficult to implement a full parser for Ngspice since the syntax is mainly contextual.
"""
####################################################################################################
import logging
####################################################################################################
from .ElementParameter import (
FlagParameter,
)
from .Netlist import ElementParameterMetaClass, NPinElement, Circuit
from .BasicElement import SubCircuitElement, BipolarJunctionTransistor
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class PrefixData:
##############################################
def __init__(self, prefix, classes):
self.prefix = prefix
self.classes = classes
number_of_positionals_min = 1000
number_of_positionals_max = 0
has_optionals = False
for element_class in classes:
number_of_positionals = element_class.number_of_positional_parameters
number_of_positionals_min = min(number_of_positionals_min, number_of_positionals)
number_of_positionals_max = max(number_of_positionals_max, number_of_positionals)
has_optionals = max(has_optionals, bool(element_class.optional_parameters))
self.number_of_positionals_min = number_of_positionals_min
self.number_of_positionals_max = number_of_positionals_max
self.has_optionals = has_optionals
self.multi_devices = len(classes) > 1
self.npins = prefix in ('Q', 'X') # NPinElement, Q has 3 to 4 pins
if self.npins:
self.number_of_pins = None
else:
# Q and X are single
self.number_of_pins = classes[0].number_of_pins
self.has_flag = False
for element_class in classes:
for parameter in element_class.optional_parameters.values():
if isinstance(parameter, FlagParameter):
self.has_flag = True
##############################################
def __len__(self):
return len(self.classes)
##############################################
def __iter__(self):
return iter(self.classes)
##############################################
@property
def single(self):
if not self.multi_devices:
return self.classes[0]
else:
raise NameError()
####################################################################################################
_prefix_cache = {}
for prefix, classes in ElementParameterMetaClass.__classes__.items():
prefix_data = PrefixData(prefix, classes)
_prefix_cache[prefix] = prefix_data
_prefix_cache[prefix.lower()] = prefix_data
# for prefix_data in sorted(_prefix_cache.values(), key=lambda x: len(x)):
# print(prefix_data.prefix,
# len(prefix_data),
# prefix_data.number_of_positionals_min, prefix_data.number_of_positionals_max,
# prefix_data.has_optionals)
# Single:
# B 0 True
# D 1 True
# F 2 False
# G 1 False
# H 2 False
# I 1 False
# J 1 True
# K 3 False
# M 1 True
# S 2 False
# V 1 False
# W 3 False
# Z 1 True
# Two:
# E 0 1 False
# L 1 2 True
# Three:
# C 1 2 True
# R 1 2 True
# NPinElement:
# Q 1 1 True
# X 1 1 False
####################################################################################################
class Token:
""" This class implements a token, in fact a line in a Spice netlist. """
##############################################
def __init__(self, line):
self._line = line
##############################################
def __repr__(self):
return "{} {}".format(self.__class__.__name__, repr(self._line))
####################################################################################################
class Comment(Token):
pass
####################################################################################################
class Title(Token):
""" This class implements a title definition. """
##############################################
def __init__(self, line):
super().__init__(line)
self._title = self._line.read_right_of('.title')
##############################################
def __str__(self):
return self._title
##############################################
def __repr__(self):
return "Title {}".format(self._title)
####################################################################################################
class Include(Token):
""" This class implements a include definition. """
##############################################
def __init__(self, line):
super().__init__(line)
self._include = self._line.read_right_of('.include')
##############################################
def __str__(self):
return self._include
##############################################
def __repr__(self):
return "Include {}".format(self._title)
####################################################################################################
class Model(Token):
""" This class implements a model definition.
Spice syntax::
.model mname type (pname1=pval1 pname2=pval2)
"""
##############################################
def __init__(self, line):
super().__init__(line)
# Fixme
parameters, dict_parameters = self._line.split_line('.model')
self._name, self._model_type = parameters[:2]
self._parameters = dict_parameters
##############################################
@property
def name(self):
""" Name of the model """
return self._name
##############################################
def __repr__(self):
return "Model {} {} {}".format(self._name, self._model_type, self._parameters)
####################################################################################################
class SubCircuit(Token):
""" This class implements a sub-circuit definition.
Spice syntax::
.SUBCKT name node1 ... param1=value1 ...
"""
##############################################
def __init__(self, line):
super().__init__(line)
# Fixme:
parameters, dict_parameters = self._line.split_line('.subckt')
self._name, self._nodes = parameters[0], parameters[1:]
self._tokens = []
##############################################
@property
def name(self):
""" Name of the sub-circuit. """
return self._name
##############################################
def __repr__(self):
text = "SubCircuit {} {}\n".format(self._name, self._nodes)
text += '\n'.join([' ' + repr(token) for token in self._tokens])
return text
##############################################
def __iter__(self):
""" Return an iterator on the tokens. """
return iter(self._tokens)
##############################################
def append(self, token):
""" Append a token to the token's list. """
self._tokens .append(token)
####################################################################################################
class Element(Token):
""" This class implements an element definition.
"{ expression }" are allowed in device line.
"""
_logger = _module_logger.getChild('Element')
##############################################
def __init__(self, line):
super().__init__(line)
line_str = str(line)
# self._logger.debug('\n' + line_str)
# Retrieve device prefix
self._prefix = line_str[0]
prefix_data = _prefix_cache[self._prefix]
# Retrieve device name
start_location = 1
stop_location = line_str.find(' ')
# Fixme: if stop_location == -1:
self._name = line_str[start_location:stop_location]
self._nodes = []
self._parameters = []
self._dict_parameters = {}
# Read nodes
if not prefix_data.npins:
number_of_pins = prefix_data.number_of_pins
if number_of_pins:
self._nodes, stop_location = self._line.read_words(stop_location, number_of_pins)
else: # Q or X
if prefix_data.prefix == 'Q':
self._nodes, stop_location = self._line.read_words(stop_location, 3)
# Fixme: optional node
else: # X
args, stop_location = self._line.split_words(stop_location, until='=')
self._nodes = args[:-1]
self._parameters.append(args[-1]) # model name
# Read positionals
number_of_positionals = prefix_data.number_of_positionals_min
if number_of_positionals and stop_location is not None: # model is optional
self._parameters, stop_location = self._line.read_words(stop_location, number_of_positionals)
if prefix_data.multi_devices and stop_location is not None:
remaining, stop_location = self._line.split_words(stop_location, until='=')
self._parameters.extend(remaining)
if prefix_data.prefix in ('V', 'I') and stop_location is not None:
# merge remaining
self._parameters[-1] += line_str[stop_location:]
# Read optionals
if prefix_data.has_optionals and stop_location is not None:
kwargs, stop_location = self._line.split_words(stop_location)
for kwarg in kwargs:
try:
key, value = kwarg.split('=')
self._dict_parameters[key] = value
except ValueError:
if kwarg in ('off',) and prefix_data.has_flag:
self._dict_parameters['off'] = True
else:
self._logger.warn(line_str)
# raise NameError("Bad element line:", line_str)
if prefix_data.multi_devices:
for element_class in prefix_data:
if len(self._parameters) == element_class.number_of_positional_parameters:
break
else:
element_class = prefix_data.single
self.factory = element_class
# Move positionals passed as kwarg
to_delete = []
for parameter in element_class.positional_parameters.values():
if parameter.key_parameter:
i = parameter.position
self._dict_parameters[parameter.attribute_name] = self._parameters[i]
to_delete.append(i)
for i in to_delete:
del self._parameters[i]
self._logger.debug('\n' + self.__repr__())
##############################################
@property
def name(self):
""" Name of the element """
return self._name
##############################################
def __repr__(self):
return "Element {0._prefix} {0._name} {0._nodes} {0._parameters} {0._dict_parameters}".format(self)
####################################################################################################
class Line:
""" This class implements a line in the netlist. """
##############################################
def __init__(self, text, line_range):
text = str(text)
for marker in ('$', ';', '//'):
location = text.find(marker)
if location != -1:
break
if location != -1:
text = text[:location]
comment = text[location:]
else:
comment = ''
self._text = text
self._comment = comment
self._line_range = line_range
##############################################
def __repr__(self):
return "{0._line_range} {0._text}".format(self)
##############################################
def __str__(self):
return self._text
##############################################
def read_right_of(self, text):
return self._text[len(text):].strip()
##############################################
def read_words(self, start_location, number_of_words):
line_str = self._text
number_of_words_read = 0
words = []
while number_of_words_read < number_of_words: # and start_location < len(line_str)
stop_location = line_str.find(' ', start_location)
if stop_location == -1:
stop_location = None # read until end
word = line_str[start_location:stop_location].strip()
if word:
number_of_words_read += 1
words.append(word)
if stop_location is None: # we should stop
if number_of_words_read != number_of_words:
template = "Bad element line, looking for word {}/{}:\n"
raise NameError(template.format(number_of_words_read, number_of_words) +
line_str + '\n' +
' '*start_location + '^')
else:
if start_location < stop_location:
start_location = stop_location
else: # we have read a space
start_location += 1
return words, stop_location
##############################################
def split_words(self, start_location, until=None):
line_str = self._text
stop_location = None
if until is not None:
location = line_str.find(until, start_location)
if location != -1:
stop_location = location
location = line_str.rfind(' ', start_location, stop_location)
if location != -1:
stop_location = location
else:
raise NameError("Bad element line, missing key? " + line_str)
line_str = line_str[start_location:stop_location]
words = [x for x in line_str.split(' ') if x]
return words, stop_location
##############################################
def split_line(self, keyword):
""" Split the line according to the following pattern::
keyword parameter1 parameter2 ... key1=value1 key2=value2 ...
Return the list of parameters and the dictionnary.
"""
raw_parameters = self._text[len(keyword):].split()
parameters = []
dict_parameters = {}
for parameter in raw_parameters:
if '=' in parameter:
key, value = parameter.split('=')
dict_parameters[key.strip()] = value.strip()
else:
parameters.append(parameter)
return parameters, dict_parameters
####################################################################################################
class SpiceParser:
""" This class parse a Spice netlist file and build a syntax tree.
Public Attributes:
:attr:`circuit`
:attr:`models`
:attr:`subcircuits`
"""
_logger = _module_logger.getChild('SpiceParser')
##############################################
def __init__(self, path=None, source=None):
# Fixme: empty source
if path is not None:
with open(str(path), 'r') as f:
raw_lines = f.readlines()
elif source is not None:
raw_lines = source.split('\n') # Fixme: other os
else:
raise ValueError
lines = self._merge_lines(raw_lines)
self._title = None
self._tokens = self._parse(lines)
self._find_sections()
##############################################
def _merge_lines(self, raw_lines):
"""Merge broken lines and return a new list of lines.
A line starting with "+" continues the preceding line.
"""
# Fixme: better using lines[-1] ?
lines = []
current_line = ''
current_line_index = None
for line_index, line in enumerate(raw_lines):
if line.startswith('+'):
current_line += ' ' + line[1:].strip()
else:
if current_line:
lines.append(Line(current_line, slice(current_line_index, line_index)))
current_line = line.strip()
current_line_index = line_index
if current_line:
lines.append(Line(current_line, slice(current_line_index, len(raw_lines))))
return lines
##############################################
def _parse(self, lines):
""" Parse the lines and return a list of tokens. """
tokens = []
sub_circuit = None
scope = tokens
for line in lines:
# print repr(line)
text = str(line)
lower_case_text = text.lower() # !
if text.startswith('*'):
scope.append(Comment(line))
elif lower_case_text.startswith('.'):
lower_case_text = lower_case_text[1:]
if lower_case_text.startswith('subckt'):
sub_circuit = SubCircuit(line)
tokens.append(sub_circuit)
scope = sub_circuit
elif lower_case_text.startswith('ends'):
sub_circuit = None
scope = tokens
elif lower_case_text.startswith('title'):
self._title = Title(line)
scope.append(self._title)
elif lower_case_text.startswith('end'):
pass
elif lower_case_text.startswith('model'):
model = Model(line)
scope.append(model)
elif lower_case_text.startswith('include'):
scope.append(Include(line))
else:
# options param ...
# .global
# .lib filename libname
# .param
# .func .csparam .temp .if
# { expr } are allowed in .model lines and in device lines.
self._logger.warn(line)
else:
element = Element(line)
scope.append(element)
return tokens
##############################################
def _find_sections(self):
""" Look for model, sub-circuit and circuit definitions in the token list. """
self.circuit = None
self.subcircuits = []
self.models = []
for token in self._tokens:
if isinstance(token, Title):
if self.circuit is None:
self.circuit = token
else:
raise NameError("More than one title")
elif isinstance(token, SubCircuit):
self.subcircuits.append(token)
elif isinstance(token, Model):
self.models.append(token)
##############################################
def is_only_subcircuit(self):
return bool(not self.circuit and self.subcircuits)
##############################################
def is_only_model(self):
return bool(not self.circuit and not self.subcircuits and self.models)
##############################################
def build_circuit(self, ground=0):
ground = str(ground)
circuit = Circuit(str(self._title))
for token in self._tokens:
if isinstance(token, Include):
circuit.include(str(token))
for token in self._tokens:
if isinstance(token, Element):
factory = getattr(circuit, token.factory.alias)
nodes = []
for node in token._nodes:
if str(node) == ground:
node = 0
nodes.append(node)
if token._prefix != 'X':
args = nodes + token._parameters
else: # != Spice
args = token._parameters + nodes
kwargs = token._dict_parameters
message = ' '.join([str(x) for x in (token._prefix, token._name, nodes,
token._parameters, token._dict_parameters)])
self._logger.debug(message)
factory(token._name, *args, **kwargs)
return circuit
##############################################
def _to_python(self, value):
try:
int_value = int(value)
value = float(value)
if int_value == value:
return str(int_value)
else:
return str(value)
except ValueError:
return "'{}'".format(value)
##############################################
def to_python_code(self, ground=0):
ground = str(ground)
# for token in self._tokens:
# if isinstance(token, Include):
# circuit.include(str(token))
if self._title:
title = self._title
else:
title = '...'
circuit = "circuit = Circuit('{}')\n".format(title)
for token in self._tokens:
if isinstance(token, Element):
nodes = []
for node in token._nodes:
if str(node) == ground:
node = 0
nodes.append(node)
if token._prefix != 'X':
args = nodes + token._parameters
else: # != Spice
args = token._parameters + nodes
args = [self._to_python(x) for x in args]
kwargs = ['{}={}'.format(key, self._to_python(value))
for key, value in token._dict_parameters.items()]
parameters = ', '.join(args + kwargs)
circuit += "circuit.{}({})\n".format(token._prefix, parameters)
return circuit
PySpice-v0.4.1/PySpice/Spice/RawFile.py 0000664 0000000 0000000 00000037666 13140600222 0017610 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
from ..Unit import u_Degree, U_V, U_A, U_s, U_Hz
####################################################################################################
"""
Header
.. code::
Circuit: 230V Rectifier
Doing analysis at TEMP = 25.000000 and TNOM = 25.000000
Title: 230V Rectifier
Date: Thu Jun 4 23:40:58 2015
Plotname: Transient Analysis
Flags: real
No. Variables: 6
No. Points: 0
Variables:
No. of Data Columns : 6
0 time time
1 v(in) voltage
...
5 i(vinput) current
Binary:
Operating Point
Node voltages and source branch currents:
* v(node_name)
* i(vname)
Sensitivity Analysis
* v({element})
* v({element}_{parameter})
* v(v{source})
DC
* v(v-sweep)
* v({node})
* i(v{source})
AC
Frequency, node voltages and source branch currents:
* frequency
* v({node})
* i(v{name})
Transient Analysis
Time, node voltages and source branch currents:
* time
* v({node})
* i(v{source})
"""
# * v({element}:bv_max)
# * i(e.xdz1.ev1)
####################################################################################################
import logging
import numpy as np
####################################################################################################
from ..Probe.WaveForm import (OperatingPoint, SensitivityAnalysis,
DcAnalysis, AcAnalysis, TransientAnalysis,
WaveForm)
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
# Fixme: self._
class Variable:
"""This class implements a variable or probe in a SPICE simulation output.
Public Attributes:
:attr:`index`
index in the array
:attr:`name`
:attr:`unit`
"""
##############################################
def __init__(self, index, name, unit):
self.index = int(index)
self.name = str(name)
self.unit = unit # could be guessed from name also for voltage node and branch current
self.data = None
##############################################
def __repr__(self):
return 'variable[{0.index}]: {0.name} [{0.unit}]'.format(self)
##############################################
def is_voltage_node(self):
return self.name.startswith('v(')
##############################################
def is_branch_current(self):
# source branch current
return self.name.startswith('i(')
##############################################
@staticmethod
def to_voltage_name(node):
return 'v({})'.format(node)
##############################################
@staticmethod
def to_branch_name(element):
return 'i({})'.format(element)
##############################################
@property
def simplified_name(self):
if self.is_voltage_node() or self.is_branch_current():
return self.name[2:-1]
else:
return self.name
##############################################
def fix_case(self, element_translation, node_translation):
""" Update the name to the right case. """
if self.is_branch_current():
if self.simplified_name in element_translation:
self.name = self.to_branch_name(element_translation[self.simplified_name])
elif self.is_voltage_node():
if self.simplified_name in node_translation:
self.name = self.to_voltage_name(node_translation[self.simplified_name])
##############################################
def to_waveform(self, abscissa=None, to_real=False, to_float=False):
""" Return a :obj:`PySpice.Probe.WaveForm` instance. """
data = self.data
if to_real:
data = data.real
if to_float:
data = float(data[0])
return WaveForm(self.simplified_name, self.unit, data, abscissa=abscissa)
####################################################################################################
class RawFile:
""" This class parse the stdout of ngspice and the raw data output.
Public Attributes:
:attr:`circuit`
same as title
:attr:`data`
:attr:`date`
:attr:`flags`
'real' or 'complex'
:attr:`number_of_points`
:attr:`number_of_variables`
:attr:`plot_name`
AC Analysis, Operating Point, Sensitivity Analysis, DC transfer characteristic
:attr:`temperature`
:attr:`title`
:attr:`variables`
:attr:`warnings`
"""
_logger = _module_logger.getChild('RawFile')
##############################################
def __init__(self, stdout, number_of_points):
self.number_of_points = number_of_points
raw_data = self._read_header(stdout)
self._read_variable_data(raw_data)
# self._to_analysis()
self._simulation = None
##############################################
@property
def simulation(self):
if self._simulation is not None:
return self._simulation
else:
raise NameError('Simulation is undefined')
@simulation.setter
def simulation(self, value):
self._simulation = value
##############################################
@property
def circuit(self):
return self._simulation.circuit
##############################################
__name_to_unit__ = {
'time': U_s,
'voltage': U_V,
'current': U_A,
'frequency': U_Hz,
}
##############################################
def _read_header(self, stdout):
""" Parse the header """
binary_line = b'Binary:\n'
binary_location = stdout.find(binary_line)
if binary_location < 0:
raise NameError('Cannot locate binary data')
raw_data_start = binary_location + len(binary_line)
# self._logger.debug('\n' + stdout[:raw_data_start].decode('utf-8'))
header_lines = stdout[:binary_location].splitlines()
raw_data = stdout[raw_data_start:]
header_line_iterator = iter(header_lines)
self.circuit_name = self._read_header_field_line(header_line_iterator, 'Circuit')
self.temperature, self.nominal_temperature = self._read_temperature_line(header_line_iterator)
self.warnings = [self._read_header_field_line(header_line_iterator, 'Warning')
for i in range(stdout.count(b'Warning'))]
for warning in self.warnings:
self._logger.warn(warning)
self.title = self._read_header_field_line(header_line_iterator, 'Title')
self.date = self._read_header_field_line(header_line_iterator, 'Date')
self.plot_name = self._read_header_field_line(header_line_iterator, 'Plotname')
self.flags = self._read_header_field_line(header_line_iterator, 'Flags')
self.number_of_variables = int(self._read_header_field_line(header_line_iterator, 'No. Variables'))
self._read_header_field_line(header_line_iterator, 'No. Points')
self._read_header_field_line(header_line_iterator, 'Variables', has_value=False)
self._read_header_field_line(header_line_iterator, 'No. of Data Columns ')
self.variables = {}
for i in range(self.number_of_variables):
line = (next(header_line_iterator)).decode('utf-8')
self._logger.debug(line)
items = [x.strip() for x in line.split('\t') if x]
# 0 frequency frequency grid=3
index, name, unit = items[:3]
# unit = time, voltage, current
unit = self.__name_to_unit__[unit] # convert to Unit
self.variables[name] = Variable(index, name, unit)
# self._read_header_field_line(header_line_iterator, 'Binary', has_value=False)
return raw_data
##############################################
def _read_line(self, header_line_iterator):
""" Return the next line """
# Fixme: self._header_line_iterator, etc.
line = None
while not line:
line = next(header_line_iterator)
return line.decode('utf-8')
##############################################
def _read_header_line(self, header_line_iterator, head_line):
""" Read an header line and check it starts with *head_line*. """
line = self._read_line(header_line_iterator)
self._logger.debug(line)
if line.startswith(head_line):
return line
else:
raise NameError("Unexpected line: %s" % (line))
##############################################
def _read_header_field_line(self, header_line_iterator, expected_label, has_value=True):
""" Read an header line and check it starts with *expected_label*.
Return the values next to the label if the flag *has_value* is set.
"""
line = self._read_line(header_line_iterator)
self._logger.debug(line)
if has_value:
# a title can have ': ' after 'title: '
location = line.find(': ') # first occurence
label, value = line[:location], line[location+2:]
else:
label = line[:-1]
if label != expected_label:
raise NameError("Expected label %s instead of %s" % (expected_label, label))
if has_value:
return value.strip()
##############################################
def _read_temperature_line(self, header_line_iterator):
# Doing analysis at TEMP = 25.000000 and TNOM = 25.000000
line = self._read_header_line(header_line_iterator, 'Doing analysis at TEMP')
pattern1 = 'TEMP = '
pattern2 = ' and TNOM = '
pos1 = line.find(pattern1)
pos2 = line.find(pattern2)
if pos1 != -1 and pos2 != -1:
part1 = line[pos1+len(pattern1):pos2]
part2 = line[pos2+len(pattern2):].strip()
temperature = u_Degree(float(part1))
nominal_temperature = u_Degree(float(part2))
else:
temperature = None
nominal_temperature = None
return temperature, nominal_temperature
##############################################
def _read_variable_data(self, raw_data):
""" Read the raw data and set the variable values. """
if self.flags == 'real':
number_of_columns = self.number_of_variables
elif self.flags == 'complex':
number_of_columns = 2*self.number_of_variables
else:
raise NotImplementedError
input_data = np.fromstring(raw_data, count=number_of_columns*self.number_of_points, dtype='f8')
input_data = input_data.reshape((self.number_of_points, number_of_columns))
input_data = input_data.transpose()
# np.savetxt('raw.txt', input_data)
if self.flags == 'complex':
raw_data = input_data
input_data = np.array(raw_data[0::2], dtype='complex64')
input_data.imag = raw_data[1::2]
for variable in self.variables.values():
variable.data = input_data[variable.index]
##############################################
def fix_case(self):
""" Ngspice return lower case names. This method fixes the case of the variable names. """
circuit = self.circuit
element_translation = {element.lower():element for element in circuit.element_names()}
node_translation = {node.lower():node for node in circuit.node_names()}
for variable in self.variables.values():
variable.fix_case(element_translation, node_translation)
##############################################
def nodes(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.variables.values()
if variable.is_voltage_node()]
##############################################
def branches(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.variables.values()
if variable.is_branch_current()]
##############################################
def elements(self, abscissa=None):
return [variable.to_waveform(abscissa, to_float=True)
for variable in self.variables.values()]
##############################################
def to_analysis(self):
self.fix_case()
if self.plot_name == 'Operating Point':
return self._to_operating_point_analysis()
elif self.plot_name == 'Sensitivity Analysis':
return self._to_sensitivity_analysis()
elif self.plot_name == 'DC transfer characteristic':
return self._to_dc_analysis()
elif self.plot_name == 'AC Analysis':
return self._to_ac_analysis()
elif self.plot_name == 'Transient Analysis':
return self._to_transient_analysis()
else:
raise NotImplementedError("Unsupported plot name {}".format(self.plot_name))
##############################################
def _to_operating_point_analysis(self):
return OperatingPoint(
simulation=self.simulation,
nodes=self.nodes(to_float=True),
branches=self.branches(to_float=True),
)
##############################################
def _to_sensitivity_analysis(self):
# Fixme: test .SENS I (VTEST)
# Fixme: separate v(vinput), analysis.R2.m
return SensitivityAnalysis(
simulation=self.simulation,
elements=self.elements(),
)
##############################################
def _to_dc_analysis(self):
if 'v(v-sweep)' in self.variables:
sweep_variable = self.variables['v(v-sweep)']
elif 'v(i-sweep)' in self.variables:
sweep_variable = self.variables['v(i-sweep)']
else:
#
raise NotImplementedError
sweep = sweep_variable.to_waveform()
return DcAnalysis(
simulation=self.simulation,
sweep=sweep,
nodes=self.nodes(),
branches=self.branches(),
)
##############################################
def _to_ac_analysis(self):
frequency = self.variables['frequency'].to_waveform(to_real=True)
return AcAnalysis(
simulation=self.simulation,
frequency=frequency,
nodes=self.nodes(),
branches=self.branches(),
)
##############################################
def _to_transient_analysis(self):
time = self.variables['time'].to_waveform(to_real=True)
return TransientAnalysis(
simulation=self.simulation,
time=time,
nodes=self.nodes(abscissa=time),
branches=self.branches(abscissa=time),
)
PySpice-v0.4.1/PySpice/Spice/Server.py 0000664 0000000 0000000 00000013060 13140600222 0017503 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This module provides an interface to run ngspice in server mode and get back the simulation
output.
When ngspice runs in server mode, it writes on the standard output an header and then the simulation
output in binary format. At the end of the simulation, it writes on the standard error a line of
the form:
.. code::
@@@ \d+ \d+
where the second number is the number of points of the simulation. Due to the iterative and
adaptive nature of a transient simulation, the number of points is only known at the end.
Any line starting with "Error" in the standard output indicates an error in the simulation process.
The line "run simulation(s) aborted" in the standard error indicates the simulation aborted.
Any line starting with *Warning* in the standard error indicates non critical error in the
simulation process.
"""
####################################################################################################
import logging
import re
import subprocess
####################################################################################################
from .RawFile import RawFile
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class SpiceServer:
"""This class wraps the execution of ngspice in server mode and convert the output to a Python data
structure.
Example of usage::
spice_server = SpiceServer(spice_command='/path/to/ngspice')
raw_file = spice_server(spice_input)
It returns a :obj:`PySpice.Spice.RawFile` instance.
"""
_logger = _module_logger.getChild('SpiceServer')
##############################################
def __init__(self, spice_command='ngspice'):
self._spice_command = spice_command
##############################################
def _decode_number_of_points(self, line):
"""Decode the number of points in the given line."""
match = re.match(r'@@@ (\d+) (\d+)', line)
if match is not None:
return int(match.group(2))
else:
raise NameError("Cannot decode the number of points")
##############################################
def _parse_stdout(self, stdout):
"""Parse stdout for errors."""
# self._logger.debug('\n' + stdout)
error_found = False
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 870: invalid start byte
# lines = stdout.decode('utf-8').splitlines()
lines = stdout.splitlines()
for line_index, line in enumerate(lines):
if line.startswith(b'Error '):
error_found = True
self._logger.error('\n' + line.decode('utf-8') + '\n' + lines[line_index+1].decode('utf-8'))
if error_found:
raise NameError("Errors was found by Spice")
##############################################
def _parse_stderr(self, stderr):
"""Parse stderr for warnings and return the number of points."""
self._logger.debug('\n' + stderr)
stderr_lines = stderr.splitlines()
number_of_points = None
for line in stderr_lines:
if line.startswith('Warning:'):
self._logger.warning(line[len('Warning :'):])
elif line == 'run simulation(s) aborted':
raise NameError("Simulation aborted\n" + stderr)
elif line.startswith('@@@'):
number_of_points = self._decode_number_of_points(line)
return number_of_points
##############################################
def __call__(self, spice_input):
"""Run SPICE in server mode as a subprocess for the given input and return a
:obj:`PySpice.RawFile.RawFile` instance.
"""
self._logger.info("Start the spice subprocess")
process = subprocess.Popen((self._spice_command, '-s'),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
input_ = str(spice_input).encode('utf-8')
stdout, stderr = process.communicate(input_)
# stdout = stdout.decode('utf-8')
stderr = stderr.decode('utf-8')
self._parse_stdout(stdout)
number_of_points = self._parse_stderr(stderr)
if number_of_points is None:
raise NameError("The number of points was not found in the standard error buffer,"
" ngspice returned:\n" +
stderr)
return RawFile(stdout, number_of_points)
PySpice-v0.4.1/PySpice/Spice/Simulation.py 0000664 0000000 0000000 00000050347 13140600222 0020372 0 ustar 00root root 0000000 0000000 ###################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import logging
####################################################################################################
from ..Tools.StringTools import join_list, join_dict, str_spice
from ..Unit import Unit, as_V, as_A, as_s, as_Hz, as_Degree, u_Degree
from .NgSpice.Shared import NgSpiceShared
from .Server import SpiceServer
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class AnalysisParameters:
__analysis_name__ = None
##############################################
@property
def analysis_name(self):
return self.__analysis_name__
##############################################
def to_list(self):
return ()
##############################################
def __str__(self):
return '.{0.analysis_name} {1}'.format(self, join_list(self.to_list()))
####################################################################################################
class OperatingPointAnalysisParameters(AnalysisParameters):
__analysis_name__ = 'op'
####################################################################################################
class DcSensitivityAnalysisParameters(AnalysisParameters):
__analysis_name__ = 'sens'
##############################################
def __init__(self, output_variable):
self._output_variable = output_variable
##############################################
@property
def output_variable(self):
return self._output_variable
##############################################
def to_list(self):
return (
self._output_variable,
)
####################################################################################################
class AcSensitivityAnalysisParameters(AnalysisParameters):
__analysis_name__ = 'sens'
##############################################
def __init__(self, output_variable,
variation, number_of_points, start_frequency, stop_frequency):
if variation not in ('dec', 'oct', 'lin'):
raise ValueError("Incorrect variation type")
self._output_variable = output_variable
self._variation = variation
self._number_of_points = number_of_points
self._start_frequency = as_Hz(start_frequency)
self._stop_frequency = as_Hz(stop_frequency)
##############################################
@property
def output_variable(self):
return self._output_variable
@property
def variation(self):
return self._variation
@property
def number_of_points(self):
return self._number_of_points
@property
def start_frequency(self):
return self._start_frequency
@property
def stop_frequencyr(self):
return self._stop_frequency
##############################################
def to_list(self):
return (
self._output_variable,
self._variation,
self._number_of_points,
self._start_frequency,
self._stop_frequency
)
####################################################################################################
class DCAnalysisParameters(AnalysisParameters):
__analysis_name__ = 'dc'
##############################################
def __init__(self, **kwargs):
self._parameters = []
for variable, value_slice in kwargs.items():
variable_lower = variable.lower()
if variable_lower[0] in ('v', 'i', 'r') or variable_lower == 'temp':
self._parameters += [variable, value_slice.start, value_slice.stop, value_slice.step]
else:
raise NameError('Sweep variable must be a voltage/current source, '
'a resistor or the circuit temperature')
##############################################
@property
def parameters(self):
return self._parameters
##############################################
def to_list(self):
return self._parameters
####################################################################################################
class ACAnalysisParameters(AnalysisParameters):
__analysis_name__ = 'ac'
##############################################
def __init__(self, variation, number_of_points, start_frequency, stop_frequency):
# Fixme: use mixin
if variation not in ('dec', 'oct', 'lin'):
raise ValueError("Incorrect variation type")
self._variation = variation
self._number_of_points = number_of_points
self._start_frequency = as_Hz(start_frequency)
self._stop_frequency = as_Hz(stop_frequency)
##############################################
@property
def variation(self):
return self._variation
@property
def number_of_points(self):
return self._number_of_points
@property
def start_frequency(self):
return self._start_frequency
@property
def stop_frequencyr(self):
return self._stop_frequency
##############################################
def to_list(self):
return (
self._variation,
self._number_of_points,
self._start_frequency,
self._stop_frequency
)
####################################################################################################
class TransientAnalysisParameters(AnalysisParameters):
__analysis_name__ = 'tran'
##############################################
def __init__(self, step_time, end_time, start_time=0, max_time=None,
use_initial_condition=False):
if use_initial_condition:
uic = 'uic'
else:
uic = None
self._step_time = as_s(step_time)
self._end_time = as_s(end_time)
self._start_time = as_s(start_time)
self._max_time = as_s(max_time, none=True)
self._use_initial_condition = uic
##############################################
@property
def step_time(self):
return self._step_time
@property
def end_time(self):
return self._end_time
@property
def start_time(self):
return self._start_time
@property
def max_time(self):
return self._max_time
@property
def use_initial_condition(self):
return self._use_initial_condition
##############################################
def to_list(self):
return (
self._step_time,
self._end_time,
self._start_time,
self._max_time,
self._use_initial_condition,
)
####################################################################################################
class CircuitSimulation:
"""Define and generate the spice instruction to perform a circuit simulation.
.. warning:: In some cases NgSpice can perform several analyses one after the other. This case
is partially supported.
"""
_logger = _module_logger.getChild('CircuitSimulation')
##############################################
def __init__(self, circuit,
temperature=27, # u_Degree()
nominal_temperature=27,
pipe=True,
):
self._circuit = circuit
self._options = {} # .options
self._initial_condition = {} # .ic
self._saved_nodes = ()
self._analyses = {}
self.temperature = temperature
self.nominal_temperature = nominal_temperature
if pipe:
self.options('NOINIT')
self.options(filetype='binary')
##############################################
@property
def circuit(self):
return self._circuit
##############################################
def options(self, *args, **kwargs):
for item in args:
self._options[str(item)] = None
for key, value in kwargs.items():
self._options[str(key)] = str(value)
##############################################
@property
def temperature(self):
return self._options['TEMP']
@temperature.setter
def temperature(self, value):
self._options['TEMP'] = as_Degree(value)
##############################################
@property
def nominal_temperature(self):
return self._options['TNOM']
@nominal_temperature.setter
def nominal_temperature(self, value):
self._options['TNOM'] = as_Degree(value)
##############################################
def initial_condition(self, **kwargs):
""" Set initial condition for voltage nodes.
Usage: initial_condition(node_name1=value, ...)
"""
for key, value in kwargs.items():
self._initial_condition['V({})'.format(str(key))] = str(value)
# Fixme: .nodeset
##############################################
def save(self, *args):
# Fixme: pass Node for voltage node, Element for source branch current, ...
"""Set the list of saved vectors.
If no *.save* line is given, then the default set of vectors is saved (node voltages and
voltage source branch currents). If *.save* lines are given, only those vectors specified
are saved.
Node voltages may be saved by giving the node_name or *v(node_name)*. Currents through an
independent voltage source (including inductor) are given by *i(source_name)* or
*source_name#branch*. Internal device data are accepted as *@dev[param]*.
If you want to save internal data in addition to the default vector set, add the parameter
*all* to the additional vectors to be saved.
"""
self._saved_nodes = list(args)
##############################################
@property
def save_currents(self):
""" Save all currents. """
return self._options.get('SAVECURRENTS', False)
@save_currents.setter
def save_currents(self, value):
if value:
self._options['SAVECURRENTS'] = True
else:
del self._options['SAVECURRENTS']
##############################################
def reset_analysis(self):
self._analyses.clear()
##############################################
def analysis_iter(self):
return self._analyses.values()
##############################################
def _add_analysis(self, analysis_parameters):
self._analyses[analysis_parameters.analysis_name] = analysis_parameters
##############################################
def operating_point(self):
"""Compute the operating point of the circuit with capacitors open and inductors shorted."""
self._add_analysis(OperatingPointAnalysisParameters())
##############################################
def dc_sensitivity(self, output_variable):
"""Compute the sensitivity of the DC operating point of a node voltage or voltage-source branch
current to all non-zero device parameters.
General form:
.. code::
.sens outvar
Examples:
.. code::
.SENS V(1, OUT)
.SENS I(VTEST)
"""
self._add_analysis(DcSensitivityAnalysisParameters(output_variable))
##############################################
def ac_sensitivity(self, output_variable,
variation, number_of_points, start_frequency, stop_frequency):
"""Compute the sensitivity of the AC values of a node voltage or voltage-source branch
current to all non-zero device parameters.
General form:
.. code::
.sens outvar ac dec nd fstart fstop
.sens outvar ac oct no fstart fstop
.sens outvar ac lin np fstart fstop
Examples:
.. code::
.SENS V(OUT) AC DEC 10 100 100 k
"""
self._add_analysis(
AcSensitivityAnalysisParameters(
output_variable,
variation, number_of_points, start_frequency, stop_frequency
))
##############################################
def dc(self, **kwargs):
"""Compute the DC transfer fonction of the circuit with capacitors open and inductors shorted.
General form:
.. code::
.dc srcnam vstart vstop vincr [ src2 start2 stop2 incr2 ]
*srcnam* is the name of an independent voltage or current source, a resistor or the circuit
temperature. *vstart*, *vstop*, and *vincr* are the starting, final, and incrementing values
respectively.
A second source (*src2*) may optionally be specified with associated sweep parameters. In
this case, the first source is swept over its range for each value of the second source.
Examples:
.. code::
.dc VIN 0 .2 5 5.0 0.25
.dc VDS 0 10 .5 VGS 0 5 1
.dc VCE 0 10 .2 5 IB 0 10U 1U
.dc RLoad 1k 2k 100
.dc TEMP -15 75 5
"""
self._add_analysis(DCAnalysisParameters(**kwargs))
##############################################
def ac(self, variation, number_of_points, start_frequency, stop_frequency):
# fixme: concise keyword ?
"""Perform a small-signal AC analysis of the circuit where all non-linear devices are linearized
around their actual DC operating point.
Note that in order for this analysis to be meaningful, at least one independent source must
have been specified with an AC value. Typically it does not make much sense to specify more
than one AC source. If you do, the result will be a superposition of all sources, thus
difficult to interpret.
Examples:
.. code::
.ac dec nd fstart fstop
.ac oct no fstart fstop
.ac lin np fstart fstop
The parameter *variation* must be either `dec`, `oct` or `lin`.
"""
self._add_analysis(
ACAnalysisParameters(
variation, number_of_points, start_frequency, stop_frequency
))
##############################################
def transient(self, step_time, end_time, start_time=0, max_time=None,
use_initial_condition=False):
"""Perform a transient analysis of the circuit.
General Form:
.. code::
.tran tstep tstop >
"""
self._add_analysis(
TransientAnalysisParameters(
step_time, end_time, start_time, max_time,
use_initial_condition
))
##############################################
def __str__(self):
netlist = str(self._circuit)
if self.options:
for key, value in self._options.items():
if value is not None:
netlist += '.options {} = {}\n'.format(key, str_spice(value))
else:
netlist += '.options {}\n'.format(key)
if self.initial_condition:
netlist += '.ic ' + join_dict(self._initial_condition) + '\n'
if self._saved_nodes:
netlist += '.save ' + join_list(self._saved_nodes) + '\n'
for analysis_parameters in self._analyses.values():
netlist += str(analysis_parameters) + '\n'
netlist += '.end\n'
return netlist
####################################################################################################
class CircuitSimulator(CircuitSimulation):
""" This class implements a circuit simulator. Each analysis mode is performed by a method that
return the measured probes.
For *ac* and *transient* analyses, the user must specify a list of nodes using the *probes* key
argument.
"""
_logger = _module_logger.getChild('CircuitSimulator')
##############################################
def _run(self, analysis_method, *args, **kwargs):
self.reset_analysis()
if 'probes' in kwargs:
self.save(* kwargs.pop('probes'))
method = getattr(CircuitSimulation, analysis_method)
method(self, *args, **kwargs)
self._logger.debug('desk\n' + str(self))
##############################################
def operating_point(self, *args, **kwargs):
return self._run('operating_point', *args, **kwargs)
##############################################
def dc(self, *args, **kwargs):
return self._run('dc', *args, **kwargs)
##############################################
def dc_sensitivity(self, *args, **kwargs):
return self._run('dc_sensitivity', *args, **kwargs)
##############################################
def ac(self, *args, **kwargs):
return self._run('ac', *args, **kwargs)
##############################################
def transient(self, *args, **kwargs):
return self._run('transient', *args, **kwargs)
####################################################################################################
class SubprocessCircuitSimulator(CircuitSimulator):
_logger = _module_logger.getChild('SubprocessCircuitSimulator')
##############################################
def __init__(self, circuit,
temperature=27,
nominal_temperature=27,
spice_command='ngspice',
):
# Fixme: kwargs
super().__init__(circuit, temperature, nominal_temperature, pipe=True)
self._spice_server = SpiceServer()
##############################################
def _run(self, analysis_method, *args, **kwargs):
super()._run(analysis_method, *args, **kwargs)
raw_file = self._spice_server(str(self))
self.reset_analysis()
raw_file.simulation = self
# for field in raw_file.variables:
# print field
return raw_file.to_analysis()
####################################################################################################
class NgSpiceSharedCircuitSimulator(CircuitSimulator):
_logger = _module_logger.getChild('NgSpiceSharedCircuitSimulator')
##############################################
def __init__(self, circuit,
temperature=27,
nominal_temperature=27,
ngspice_shared=None,
):
# Fixme: kwargs
super().__init__(circuit, temperature, nominal_temperature, pipe=False)
if ngspice_shared is None:
self._ngspice_shared = NgSpiceShared(send_data=False)
else:
self._ngspice_shared = ngspice_shared
##############################################
def _run(self, analysis_method, *args, **kwargs):
super()._run(analysis_method, *args, **kwargs)
self._ngspice_shared.load_circuit(str(self))
self._ngspice_shared.run()
self._logger.debug(str(self._ngspice_shared.plot_names))
self.reset_analysis()
if analysis_method == 'dc':
plot_name = 'dc1'
elif analysis_method == 'ac':
plot_name = 'ac1'
elif analysis_method == 'transient':
plot_name = 'tran1'
else:
raise NotImplementedError
return self._ngspice_shared.plot(self, plot_name).to_analysis()
PySpice-v0.4.1/PySpice/Spice/__init__.py 0000664 0000000 0000000 00000004377 13140600222 0020007 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
from . import BasicElement
from . import HighLevelElement
from .Netlist import Netlist, ElementParameterMetaClass
####################################################################################################
def _get_elements(module):
element_classes = []
for item in module.__dict__.values():
if (type(item) is ElementParameterMetaClass
and item.prefix is not None
):
element_classes.append(item)
return element_classes
####################################################################################################
#
# Add a method to create elements to the Netlist class
#
spice_elements = _get_elements(BasicElement)
high_level_elements = _get_elements(HighLevelElement)
for element_class in spice_elements + high_level_elements:
def _make_function(element_class):
def function(self, *args, **kwargs):
element = element_class(*args, **kwargs)
self._add_element(element)
return element
return function
if element_class in spice_elements and hasattr(element_class, 'alias'):
function_name = element_class.alias
else:
function_name = element_class.__name__
setattr(Netlist, function_name, _make_function(element_class))
PySpice-v0.4.1/PySpice/Tools/ 0000775 0000000 0000000 00000000000 13140600222 0015720 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Tools/EnumFactory.py 0000664 0000000 0000000 00000011526 13140600222 0020533 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
""" This module provides an implementation for enumerate.
The enumerate factory :func:`EnumFactory` builds a enumerate from a list of names and assigns to
these constants a value from 0 to N-1, where N is the number of constants. For example::
enum = EnumFactory('Enum1', ('cst1', 'cst2'))
builds a enumerate with *cst1* set to 0 and *cst2* set to 1.
We can get a constant's value using an integer context like::
int(enum.cst1)
and the constant's name using::
repr(enum.cst1)
We can test constant equality using::
enum1.cst == enum2.cst
or with something that understand the *int* protocol::
enum1.cst == obj
# equivalent to
int(enum1.cst) == int(obj)
The number of constants could be retrieved with::
len(enum)
The enumerate factory :func:`ExplicitEnumFactory` is a variant that permits to specify the values of
the constants::
enum2 = ExplicitEnumFactory('Enum2', {'cst1':1, 'cst2':3})
We can test if a value is in the enumerate using::
constant_value in enum2
"""
####################################################################################################
# __all__ = ['EnumFactory', 'ExplicitEnumFactory']
####################################################################################################
class ReadOnlyMetaClass(type):
""" This meta class implements a class where attributes are read only. """
##############################################
def __setattr__(self, name, value):
raise NotImplementedError
####################################################################################################
class EnumMetaClass(ReadOnlyMetaClass):
""" This meta class implements the :func:`len` protocol. """
##############################################
def __len__(self):
return self._size
##############################################
def __getitem__(self, i):
return self._index[i]
####################################################################################################
class ExplicitEnumMetaClass(ReadOnlyMetaClass):
""" This meta class implements the operator ``in``. """
##############################################
def __contains__(self, item):
return item in self.constants
####################################################################################################
class EnumConstant:
""" Define an Enum Constant """
##############################################
def __init__(self, name, value):
self._name = name
self._value = value
##############################################
def __eq__(self, other):
return self._value == int(other)
##############################################
def __int__(self):
return self._value
##############################################
def __repr__(self):
return self._name
####################################################################################################
def EnumFactory(enum_name, enum_tuple):
""" Return an :class:`EnumMetaClass` instance, where *enum_name* is the class name and
*enum_tuple* is an iterable of constant's names.
"""
index = [EnumConstant(name, value) for value, name in enumerate(enum_tuple)]
obj_dict = {}
obj_dict['_size'] = len(enum_tuple)
obj_dict['_index'] = index
obj_dict.update({str(enum):enum for enum in index})
return EnumMetaClass(enum_name, (), obj_dict)
####################################################################################################
def ExplicitEnumFactory(enum_name, enum_dict):
""" Return an :class:`ExplicitEnumMetaClass` instance, where *enum_name* is the class name and
*enum_dict* is a dict of constant's names and their values.
"""
obj_dict = {}
obj_dict['constants'] = list(enum_dict.values())
for name, value in list(enum_dict.items()):
obj_dict[name] = EnumConstant(name, value)
return ExplicitEnumMetaClass(enum_name, (), obj_dict)
PySpice-v0.4.1/PySpice/Tools/File.py 0000664 0000000 0000000 00000016454 13140600222 0017163 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import os
import subprocess
####################################################################################################
def file_name_has_extension(file_name, extension):
return file_name.endswith(extension)
####################################################################################################
def file_extension(filename):
# index = filename.rfind(os.path.extsep)
# if index == -1:
# return None
# else:
# return filename[index:]
return os.path.splitext(filename)[1]
####################################################################################################
def run_shasum(filename, algorithm=1, text=False, binary=False, portable=False):
if algorithm not in (1, 224, 256, 384, 512, 512224, 512256):
raise ValueError
args = ['shasum', '--algorithm=' + str(algorithm)]
if text:
args.append('--text')
elif binary:
args.append('--binary')
elif portable:
args.append('--portable')
args.append(filename)
output = subprocess.check_output(args)
shasum = output[:output.find(' ')]
return shasum
####################################################################################################
class Path:
##############################################
def __init__(self, path):
self._path = str(path)
##############################################
def __bool__(self):
return os.path.exists(self._path)
##############################################
def __str__(self):
return self._path
##############################################
@property
def path(self):
return self._path
##############################################
def is_absolut(self):
return os.path.isabs(self._path)
##############################################
def absolut(self):
return self.clone_for_path(os.path.abspath(self._path))
##############################################
def normalise(self):
return self.clone_for_path(os.path.normpath(self._path))
##############################################
def normalise_case(self):
return self.clone_for_path(os.path.normcase(self._path))
##############################################
def expand_vars_and_user(self):
return self.clone_for_path(os.path.expandvars(os.path.expanduser(self._path)))
##############################################
def real_path(self):
return self.clone_for_path(os.path.realpath(self._path))
##############################################
def relative_to(self, directory):
return self.clone_for_path(os.path.relpath(self._path, str(directory)))
##############################################
def clone_for_path(self, path):
return self.__class__(path)
##############################################
def split(self):
return self._path.split(os.path.sep)
##############################################
def directory_part(self):
return Directory(os.path.dirname(self._path))
##############################################
def filename_part(self):
return os.path.basename(self._path)
##############################################
def is_directory(self):
return os.path.isdir(self._path)
##############################################
def is_file(self):
return os.path.isfile(self._path)
##############################################
@property
def inode(self):
return os.stat(self._path).st_ino
##############################################
@property
def creation_time(self):
return os.stat(self._path).st_ctime
####################################################################################################
class Directory(Path):
##############################################
def __bool__(self):
return super().__nonzero__() and self.is_directory()
##############################################
def join_directory(self, directory):
return self.__class__(os.path.join(self._path, str(directory)))
##############################################
def join_filename(self, filename):
return File(filename, self._path)
##############################################
def iter_file(self, followlinks=False):
for root, directories, files in os.walk(self._path, followlinks=followlinks):
for filename in files:
yield File(filename, root)
##############################################
def iter_directories(self, followlinks=False):
for root, directories, files in os.walk(self._path, followlinks=followlinks):
for directory in directories:
yield Path(os.path.join(root, directory))
####################################################################################################
class File(Path):
default_shasum_algorithm = 256
##############################################
def __init__(self, filename, path=''):
super().__init__(os.path.join(str(path), str(filename)))
self._filename = self.filename_part()
if not self._filename:
raise ValueError
self._directory = self.directory_part()
self._shasum = None # lazy computation
##############################################
def __bool__(self):
return super().__nonzero__() and os.path.isfile(self._path)
##############################################
@property
def directory(self):
return self._directory
##############################################
@property
def filename(self):
return self._filename
##############################################
@property
def extension(self):
return file_extension(self._filename)
##############################################
@property
def shasum(self):
if self._shasum is None:
return self.compute_shasum()
else:
return self._shasum
##############################################
def compute_shasum(self, algorithm=None):
if algorithm is None:
algorithm = self.default_shasum_algorithm
self._shasum = run_shasum(self._path, algorithm, portable=True)
return self._shasum
PySpice-v0.4.1/PySpice/Tools/Path.py 0000664 0000000 0000000 00000004016 13140600222 0017167 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import os
import types
####################################################################################################
def to_absolute_path(path):
# Expand ~ . and Remove trailing '/'
return os.path.abspath(os.path.expanduser(path))
####################################################################################################
def parent_directory_of(file_name, step=1):
directory = file_name
for i in range(step):
directory = os.path.dirname(directory)
return directory
####################################################################################################
def find(file_name, directories):
if isinstance(directories, bytes):
directories = (directories,)
for directory in directories:
for directory_path, sub_directories, file_names in os.walk(directory):
if file_name in file_names:
return os.path.join(directory_path, file_name)
raise NameError("File %s not found in directories %s" % (file_name, str(directories)))
PySpice-v0.4.1/PySpice/Tools/StringTools.py 0000664 0000000 0000000 00000003706 13140600222 0020567 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
from ..Unit.Unit import UnitValue
####################################################################################################
def str_spice(obj):
"""Convert an object to a Spice compatible string."""
if isinstance(obj, UnitValue):
return obj.str_spice()
else:
return str(obj)
####################################################################################################
def join_lines(items, prefix=''):
return '\n'.join([prefix + str(item) for item in items if item is not None])
####################################################################################################
def join_list(items):
return ' '.join([str_spice(item) for item in items if item is not None])
####################################################################################################
def join_dict(d):
return ' '.join(["{}={}".format(key, str_spice(value)) for key, value in d.items() if value is not None])
PySpice-v0.4.1/PySpice/Tools/__init__.py 0000664 0000000 0000000 00000001630 13140600222 0020031 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/PySpice/Unit/ 0000775 0000000 0000000 00000000000 13140600222 0015537 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/PySpice/Unit/SiUnits.py 0000664 0000000 0000000 00000017506 13140600222 0017520 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2017 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
"""This module defines SI prefixes and units.
"""
####################################################################################################
from .Unit import UnitPrefix, SiBaseUnit, Unit
####################################################################################################
# Define SI unit prefixes
class Yotta(UnitPrefix):
__power__ = 24
__prefix__ = 'Y'
__spice_prefix__ = None
class Zetta(UnitPrefix):
__power__ = 21
__prefix__ = 'Z'
__spice_prefix__ = None
class Exa(UnitPrefix):
__power__ = 18
__prefix__ = 'E'
__spice_prefix__ = None
class Peta(UnitPrefix):
__power__ = 15
__prefix__ = 'P'
__spice_prefix__ = None
class Tera(UnitPrefix):
__power__ = 12
__prefix__ = 'T'
class Giga(UnitPrefix):
__power__ = 9
__prefix__ = 'G'
class Mega(UnitPrefix):
__power__ = 6
__prefix__ = 'M'
__spice_prefix__ = 'Meg'
class Kilo(UnitPrefix):
__power__ = 3
__prefix__ = 'k'
class Hecto(UnitPrefix):
__power__ = 2
__prefix__ = 'h'
__spice_prefix__ = None
class Deca(UnitPrefix):
__power__ = 1
__prefix__ = 'da'
__spice_prefix__ = None
class Milli(UnitPrefix):
__power__ = -3
__prefix__ = 'm'
class Micro(UnitPrefix):
__power__ = -6
__prefix__ = 'μ'
__spice_prefix__ = 'u'
class Nano(UnitPrefix):
__power__ = -9
__prefix__ = 'n'
class Pico(UnitPrefix):
__power__ = -12
__prefix__ = 'p'
class Femto(UnitPrefix):
__power__ = -15
__prefix__ = 'f'
__spice_prefix__ = None
class Atto(UnitPrefix):
__power__ = -18
__prefix__ = 'a'
__spice_prefix__ = None
class Zepto(UnitPrefix):
__power__ = -21
__prefix__ = 'z'
__spice_prefix__ = None
class Yocto(UnitPrefix):
__power__ = -24
__prefix__ = 'y'
__spice_prefix__ = None
# Fixme: ngspice defines mil
####################################################################################################
# Define SI units
class Metre(SiBaseUnit):
__unit_name__ = 'metre'
__unit_suffix__ = 'm'
__quantity__ = 'length'
class Kilogram(SiBaseUnit):
__unit_name__ = 'kilogram'
__unit_suffix__ = 'kg'
__quantity__ = 'mass'
class Second(SiBaseUnit):
__unit_name__ = 'second'
__unit_suffix__ = 's'
__quantity__ = 'time'
__is_si__ = True
class Ampere(SiBaseUnit):
__unit_name__ = 'ampere'
__unit_suffix__ = 'A'
__quantity__ = 'electric current'
class Kelvin(SiBaseUnit):
__unit_name__ = 'kelvin'
__unit_suffix__ = 'K'
__quantity__ = 'thermodynamic temperature'
class Mole(SiBaseUnit):
__unit_name__ = 'mole'
__unit_suffix__ = 'mol'
__quantity__ = 'amount of substance'
class Candela(SiBaseUnit):
__unit_name__ = 'candela'
__unit_suffix__ = 'cd'
__quantity__ = 'luminosity intensity'
####################################################################################################
# Define Derived units
class Radian(Unit):
__unit_name__ = 'radian'
__unit_suffix__ = 'rad'
__quantity__ = 'angle'
__si_unit__ = 'm*m^-1'
__default_unit__ = True
class Steradian(Unit):
__unit_name__ = 'steradian'
__unit_suffix__ = 'sr'
__quantity__ = 'solid angle'
__si_unit__ = 'm^2*m^-2'
__default_unit__ = True
class Hertz(Unit):
__unit_name__ = 'frequency'
__unit_suffix__ = 'Hz'
__quantity__ = 'frequency'
__si_unit__ = 's^-1'
__default_unit__ = True
class Newton(Unit):
__unit_name__ = 'newton'
__unit_suffix__ = 'N'
__quantity__ = 'force'
__si_unit__ = 'kg*m*s^-2'
__default_unit__ = True
class Pascal(Unit):
__unit_name__ = 'pascal'
__unit_suffix__ = 'Pa'
__quantity__ = 'pressure'
__si_unit__ = 'kg*m^-1*s^-2'
__default_unit__ = True
# N/m^2
class Joule(Unit):
__unit_name__ = 'joule'
__unit_suffix__ = 'J'
__quantity__ = 'energy'
__si_unit__ = 'kg*m^2*s^-2'
__default_unit__ = True
# N*m
class Watt(Unit):
__unit_name__ = 'watt'
__unit_suffix__ = 'W'
__quantity__ = 'power'
__si_unit__ = 'kg*m^2*s^-3'
__default_unit__ = True
# J/s
class Coulomb(Unit):
__unit_name__ = 'coulomb'
__unit_suffix__ = 'C'
__quantity__ = 'electric charge'
__si_unit__ = 's*A'
__default_unit__ = True
class Volt(Unit):
__unit_name__ = 'volt'
__unit_suffix__ = 'V'
__quantity__ = 'voltage'
__si_unit__ = 'kg*m^2*s^-3*A^-1'
__default_unit__ = True
# W/A
class Farad(Unit):
__unit_name__ = 'farad'
__unit_suffix__ = 'F'
__quantity__ = 'capacitance'
__si_unit__ = 'kg^-1*m^-2*s^4*A^2'
__default_unit__ = True
# C/V
class Ohm(Unit):
__unit_name__ = 'ohm'
__unit_suffix__ = 'Ω'
__quantity__ = 'electric resistance, impedance, reactance'
__si_unit__ = 'kg*m^2*s^-3*A^-2'
__default_unit__ = True
# V/A
class Siemens(Unit):
__unit_name__ = 'siemens'
__unit_suffix__ = 'S'
__quantity__ = 'electrical conductance'
__si_unit__ = 'kg^-1*m^-2*s^3*A^2'
__default_unit__ = True
# A/V
class Weber(Unit):
__unit_name__ = 'weber'
__unit_suffix__ = 'Wb'
__quantity__ = 'magnetic flux'
__si_unit__ = 'kg*m^2*s^-2*A^-1'
__default_unit__ = True
# V*s
class Tesla(Unit):
__unit_name__ = 'tesla'
__unit_suffix__ = ''
__quantity__ = 'T'
__si_unit__ = 'kg*s^-2*A^-1'
__default_unit__ = True
# Wb/m2
class Henry(Unit):
__unit_name__ = 'henry'
__unit_suffix__ = 'H'
__quantity__ = 'inductance'
__si_unit__ = 'kg*m^2*s^-2*A^-2'
__default_unit__ = True
# Wb/A
class DegreeCelcius(Unit):
__unit_name__ = 'degree celcuis'
__unit_suffix__ = '°C'
__quantity__ = 'temperature relative to 273.15 K'
__si_unit__ = 'K'
class Lumen(Unit):
__unit_name__ = 'lumen'
__unit_suffix__ = 'lm'
__quantity__ = 'luminous flux'
__si_unit__ = 'cd'
# cd*sr
class Lux(Unit):
__unit_name__ = 'lux'
__unit_suffix__ = 'lx'
__quantity__ = 'illuminance'
__si_unit__ = 'm^-2*cd'
__default_unit__ = True
# lm/m2
class Becquerel(Unit):
__unit_name__ = 'becquerel'
__unit_suffix__ = 'Bq'
__quantity__ = 'radioactivity (decays per unit time)'
__si_unit__ = 's^-1' # same as Hertz
class Gray(Unit):
__unit_name__ = 'gray'
__unit_suffix__ = 'Gy'
__quantity__ = 'absorbed dose (of ionizing radiation)'
__si_unit__ = 'm^2*s^-2'
# J/kg
class Sievert(Unit):
__unit_name__ = 'sievert'
__unit_suffix__ = 'Sv'
__quantity__ = ' equivalent dose (of ionizing radiation)'
__si_unit__ = 'm^2*s^-2'
class Katal(Unit):
__unit_name__ = 'katal'
__unit_suffix__ = 'kat'
__quantity__ = 'catalytic activity'
__si_unit__ = 'mol*s^-1'
__default_unit__ = True
####################################################################################################
# class Mil(Unit):
# __scale__ = 25.4e-6 # mm
# __spice_suffix__ = 'mil'
PySpice-v0.4.1/PySpice/Unit/Unit.py 0000664 0000000 0000000 00000112532 13140600222 0017034 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
"""This module implements units.
A shortcut is defined for each unit prefix, e.g. :class:`pico`, :class:`nano`, :class:`micro`,
:class:`milli`, :class:`kilo`, :class:`mega`, :class:`tera`.
"""
####################################################################################################
import logging
import collections
import math
# import numbers
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class UnitPrefixMetaclass(type):
"""Metaclass to register unit prefixes"""
__prefixes__ = {} # singletons
##############################################
def __new__(meta, class_name, base_classes, attributes):
cls = type.__new__(meta, class_name, base_classes, attributes)
if class_name != 'UnitPrefix':
meta.register_prefix(cls)
return cls
##############################################
@classmethod
def register_prefix(meta, cls):
power = cls.__power__
if power is None:
raise ValueError('Power is None for {}'.format(cls.__name__))
meta.__prefixes__[power] = cls()
##############################################
@classmethod
def prefix_iter(cls):
return cls.__prefixes__.values()
##############################################
@classmethod
def get(cls, power):
return cls.__prefixes__[power]
####################################################################################################
class UnitPrefix(metaclass=UnitPrefixMetaclass):
"""This class implements a unit prefix like kilo"""
__power__ = None
__prefix__ = ''
##############################################
def __repr__(self):
return '{}({}, {})'.format(self.__class__.__name__, self.__power__, self.__prefix__)
##############################################
def __int__(self):
return self.__power__
##############################################
def __str__(self):
return self.__prefix__
##############################################
@property
def power(self):
return self.__power__
@property
def prefix(self):
return self.__prefix__
@property
def is_unit(self):
return self.__power__ == 0
@property
def scale(self):
return 10**self.__power__
##############################################
@property
def spice_prefix(self):
if hasattr(self, '__spice_prefix__'):
return self.__spice_prefix__
else:
return self.__prefix__
##############################################
@property
def is_defined_in_spice(self):
return self.spice_prefix is not None
##############################################
def __eq__(self, other):
return self.__power__ == other.__power__
##############################################
def __ne__(self, other):
return self.__power__ != other.__power__
##############################################
def __lt__(self, other):
return self.__power__ < other.__power__
##############################################
def __gt__(self, other):
return self.__power__ > other.__power__
##############################################
def str(self, spice=False):
if spice:
return self.spice_prefix
else:
return self.__prefix__
####################################################################################################
class ZeroPower(UnitPrefix):
__power__ = 0
__prefix__ = ''
__spice_prefix__ = ''
_zero_power = UnitPrefixMetaclass.get(0)
####################################################################################################
class SiDerivedUnit:
"""This class implements an unit defined as powers of SI base units.
"""
# SI base units
__base_units__ = (
'm',
'kg',
's',
'A',
'K',
'mol',
'cd',
)
##############################################
def __init__(self, string=None, powers=None):
if powers is not None:
self._powers = self.new_powers()
self._powers.update(powers)
elif string is not None:
self._powers = self.parse_si(string)
else:
self._powers = self.new_powers()
self._hash = self.to_hash(self._powers)
self._string = self.to_string(self._powers)
##############################################
@property
def powers(self):
return self._powers
@property
def hash(self):
return self._hash
@property
def string(self):
return self._string
def __str__(self):
return self._string
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self._string)
##############################################
@classmethod
def new_powers(cls):
return {unit: 0 for unit in cls.__base_units__}
##############################################
@classmethod
def parse_si(cls, string):
si_powers = cls.new_powers()
if string:
for unit_powers in string.split('*'):
parts = unit_powers.split('^')
unit = parts[0]
if len(parts) == 1:
powers = 1
else:
powers = int(parts[1])
si_powers[unit] += powers
return si_powers
##############################################
@classmethod
def to_hash(cls, powers):
hash_ = ''
for unit in cls.__base_units__:
hash_ += str(powers[unit])
return hash_
##############################################
@classmethod
def to_string(cls, si_powers):
units = []
for unit in cls.__base_units__:
powers = si_powers[unit]
if powers == 1:
units.append(unit)
elif powers > 1 or powers < 0:
units.append('{}^{}'.format(unit, powers))
return '*'.join(units)
##############################################
# @property
def is_base_unit(self):
count = 0
for powers in self._powers.values():
if powers == 1:
count += 1
elif powers != 0:
return False
return count == 1
##############################################
# @property
def is_anonymous(self):
return self._hash == '0'*len(self.__base_units__)
##############################################
def __bool__(self):
return not self.is_anonymous()
##############################################
def clone(self):
return self.__class__(powers=self._powers)
##############################################
def __eq__(self, other):
return self._hash == other.hash
##############################################
def __ne__(self, other):
return self._hash != other.hash
##############################################
def __mul__(self, other):
powers = {unit: self._powers[unit] + other._powers[unit]
for unit in self.__base_units__}
return self.__class__(powers=powers)
##############################################
def __imul__(self, other):
for unit in self.__base_units__:
self._powers[unit] += other.powers[unit]
self._hash = self.to_hash(self._powers)
self._string = self.to_string(self._powers)
return self
##############################################
def __truediv__(self, other):
powers = {unit: self._powers[unit] - other._powers[unit]
for unit in self.__base_units__}
return self.__class__(powers=powers)
##############################################
def __itruediv__(self, other):
for unit in self.__base_units__:
self._powers[unit] -= other.powers[unit]
self._hash = self.to_hash(self._powers)
self._string = self.to_string(self._powers)
return self
##############################################
def inverse(self):
powers = {unit: -self._powers[unit]
for unit in self.__base_units__}
return self.__class__(powers=powers)
####################################################################################################
class UnitMetaclass(type):
"""Metaclass to register units"""
__units__ = {}
__hash_map__ = {}
##############################################
def __new__(meta, class_name, base_classes, attributes):
cls = type.__new__(meta, class_name, base_classes, attributes)
meta.init_unit(cls)
meta.register_unit(cls)
return cls
##############################################
@classmethod
def init_unit(meta, cls):
si_unit = cls.__si_unit__
if not (isinstance(si_unit, SiDerivedUnit) and si_unit):
# si_unit is not defined
if cls.is_base_unit():
si_unit = SiDerivedUnit(cls.__unit_suffix__)
else: # str
si_unit = SiDerivedUnit(si_unit)
cls.__si_unit__ = si_unit
##############################################
@classmethod
def register_unit(meta, cls):
obj = cls()
meta.__units__[obj.unit_suffix] = obj
if obj.si_unit:
hash_ = obj.si_unit.hash
if hash_ in meta.__hash_map__:
meta.__hash_map__[hash_].append(obj)
else:
meta.__hash_map__[hash_] = [obj]
##############################################
@classmethod
def unit_iter(meta):
return meta.__units__.values()
##############################################
@classmethod
def from_prefix(meta, prefix):
return meta._units__.get(prefix, None)
##############################################
@classmethod
def from_hash(meta, hash_):
return meta.__hash_map__.get(hash_, None)
##############################################
@classmethod
def from_si_unit(meta, si_unit, unique=True):
units = meta.__hash_map__.get(si_unit.hash, None)
if unique and units is not None:
if len(units) > 1:
units = [unit for unit in units if unit.is_default_unit()]
if len(units) == 1:
return units[0]
else:
raise NameError("Unit clash", units)
else:
return units[0]
else:
return units
####################################################################################################
class UnitError(ValueError):
pass
####################################################################################################
class Unit(metaclass=UnitMetaclass):
"""This class implements an unit.
"""
__unit_name__ = ''
__unit_suffix__ = ''
__quantity__ = ''
__si_unit__ = SiDerivedUnit()
__default_unit__ = False
# __spice_suffix__ = ''
_logger = _module_logger.getChild('Unit')
##############################################
def __init__(self, si_unit=None):
self._unit_name = self.__unit_name__
self._unit_suffix = self.__unit_suffix__
self._quantity = self.__quantity__
if si_unit is None:
self._si_unit = self.__si_unit__
else:
self._si_unit = si_unit
##############################################
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, str(self))
##############################################
@property
def unit_name(self):
return self._unit_name
@property
def unit_suffix(self):
return self._unit_suffix
@property
def quantity(self):
return self._quantity
@property
def si_unit(self):
return self._si_unit
##############################################
@property
def is_anonymous(self):
return self._si_unit.is_anonymous()
##############################################
@classmethod
def is_default_unit(cls):
return cls.__default_unit__
@classmethod
def is_base_unit(cls):
return False
##############################################
def __eq__(self, other):
"""self == other"""
return self._si_unit == other.si_unit
##############################################
def __ne__(self, other):
"""self != other"""
# The default __ne__ doesn't negate __eq__ until 3.0.
return not (self == other)
##############################################
def _equivalent_unit_power(self, si_unit):
equivalent_unit = UnitPower.from_si_unit(si_unit)
if equivalent_unit is not None:
return equivalent_unit
else:
return UnitPower(Unit(si_unit))
##############################################
def _equivalent_unit(self, si_unit):
equivalent_unit = UnitMetaclass.from_si_unit(si_unit)
if equivalent_unit is not None:
return equivalent_unit
else:
return Unit(si_unit)
##############################################
def _equivalent_unit_or_power(self, si_unit, unit_power):
if unit_power:
return self._equivalent_unit_power(si_unit)
else:
return self._equivalent_unit(si_unit)
##############################################
def mul(self, other, unit_power=False):
si_unit = self._si_unit * other.si_unit
return self._equivalent_unit_or_power(si_unit, unit_power)
##############################################
def div(self, other, unit_power=False):
si_unit = self._si_unit / other.si_unit
return self._equivalent_unit_or_power(si_unit, unit_power)
##############################################
def inverse(self, unit_power=False):
si_unit = self._si_unit.inverse()
return self._equivalent_unit_or_power(si_unit, unit_power)
##############################################
def __str__(self):
if self._unit_suffix:
return self._unit_suffix
else:
return str(self._si_unit)
##############################################
def is_same_unit(self, value):
return value.unit == self
##############################################
def validate(self, value, none=False):
if none and value is None:
return None
if isinstance(value, UnitValue):
if self.is_same_unit(value):
return value
else:
raise UnitError
else:
unit_power = UnitPower.from_unit_power(self)
return unit_power.new_value(value)
####################################################################################################
class SiBaseUnit(Unit):
"""This class implements an SI base unit."""
##############################################
@classmethod
def is_base_unit(cls):
return True
##############################################
@classmethod
def is_default_unit(cls):
return True
####################################################################################################
class UnitPower:
"""This class implements an unit power.
"""
__unit_map__ = {} # Unit power singletons
__unit_power_map__ = {}
__value_ctor__ = None
##############################################
@classmethod
def register(cls, unit_power):
unit = unit_power.unit
unit_prefix = unit_power.power
if unit_prefix.is_unit and unit.is_default_unit():
key = unit.si_unit.hash
# print('Register', key, unit_power)
cls.__unit_map__[key] = unit_power
if unit.unit_suffix:
unit_key = str(unit)
else:
unit_key = '_'
power_key = unit_prefix.power
# print('Register', unit_key, power_key, unit_power)
if unit_key not in cls.__unit_power_map__:
cls.__unit_power_map__[unit_key] = {}
cls.__unit_power_map__[unit_key][power_key] = unit_power
##############################################
@classmethod
def from_si_unit(cls, si_unit):
return cls.__unit_map__.get(si_unit.hash, None)
##############################################
@classmethod
def from_unit_power(cls, unit, power=0):
if unit.unit_suffix:
unit_key = str(unit)
else:
if power == 0:
return _simple_unit_power
unit_key = '_'
try:
return cls.__unit_power_map__[unit_key][power]
except KeyError:
return None
##############################################
def __init__(self, unit=None, power=None, value_ctor=None):
if unit is None:
self._unit = Unit()
else:
self._unit = unit
if power is None:
self._power = _zero_power
else:
self._power = power
if value_ctor is None:
self._value_ctor = self.__value_ctor__
else:
self._value_ctor = value_ctor
##############################################
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, str(self))
##############################################
@property
def unit(self):
return self._unit
@property
def power(self):
return self._power
@property
def scale(self):
return self._power.scale
##############################################
@property
def is_anonymous(self):
return self._unit.is_anonymous
##############################################
def clone(self):
return self.__class__(self._unit, self._power)
##############################################
def is_same_unit(self, other):
return self._unit == other.unit
##############################################
def check_unit(self, other):
if not self.is_same_unit(other):
raise UnitError
##############################################
def is_same_power(self, other):
return self._power == other.power
##############################################
def __eq__(self, other):
"""self == other"""
return self.is_same_unit(other) and self.is_same_power(other)
##############################################
def __ne__(self, other):
"""self != other"""
# The default __ne__ doesn't negate __eq__ until 3.0.
return not (self == other)
##############################################
def str(self, spice=False, unit=True):
string = self._power.str(spice)
if unit:
string += str(self._unit)
if spice:
# Ngspice don't support utf-8
string = string.replace('Ω', 'Ohm') # utf-8 cea0
string = string.replace('μ', 'u') # utf-8 cebc
return string
##############################################
def str_spice(self):
# Ngspice User Manual Section 2.3.1 Some naming conventions
#
# Letters immediately following a number that are not scale factors are ignored, and letters
# im- mediately following a scale factor are ignored. Hence, 10, 10V, 10Volts, and 10Hz all
# represent the same number, and M, MA, MSec, and MMhos all represent the same scale
# factor. Note that 1000, 1000.0, 1000Hz, 1e3, 1.0e3, 1kHz, and 1k all represent the same
# number. Note that M or m denote ’milli’, i.e. 10−3 . Suffix meg has to be used for 106 .
# Fixme: unit clash, e.g. mm ???
return self.str(spice=True, unit=True)
##############################################
def __str__(self):
return self.str(spice=False, unit=True)
##############################################
def new_value(self, value):
if isinstance(value, collections.Iterable):
return [self._value_ctor(self, x) for x in value]
else:
return self._value_ctor(self, value)
####################################################################################################
class UnitValue: # numbers.Real
"""This class implements a value with an unit and a power (prefix).
The value is not converted to float if the value is an int.
"""
_logger = _module_logger.getChild('UnitValue')
##############################################
@classmethod
def simple_value(cls, value):
return cls(_simple_unit_power, value)
##############################################
def __init__(self, unit_power, value):
self._unit_power = unit_power
if isinstance(value, UnitValue):
# Fixme: anonymous ???
if not self.is_same_unit(value):
raise UnitError
if self.is_same_power(value):
self._value = value.value
else:
self._value = self._convert_scalar_value(value)
elif isinstance(value, int):
self._value = value # to keep as int
else:
self._value = float(value)
##############################################
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, str(self))
##############################################
@property
def unit_power(self):
return self._unit_power
@property
def unit(self):
return self._unit_power.unit
@property
def power(self):
return self._unit_power.power
@property
def scale(self):
return self._unit_power.power.scale
@property
def value(self):
return self._value
##############################################
def clone(self):
return self.__class__(self._unit_power, self._value)
##############################################
def clone_unit_power(self, value):
return self.__class__(self._unit_power, value)
##############################################
# def clone_unit(self, value, power):
# return self.__class__(UnitPower(self.unit, power), value)
##############################################
def is_same_unit(self, other):
return self._unit_power.is_same_unit(other.unit_power)
##############################################
def _check_unit(self, other):
if not self.is_same_unit(other):
raise UnitError
##############################################
def is_same_power(self, other):
return self._unit_power.is_same_power(other.unit_power)
##############################################
def __eq__(self, other):
"""self == other"""
if isinstance(other, UnitValue):
return self.is_same_unit(other) and float(self) == float(other)
else:
return float(self) == float(other)
##############################################
def __ne__(self, other):
"""self != other"""
# The default __ne__ doesn't negate __eq__ until 3.0.
return not (self == other)
##############################################
def _convert_value(self, other):
"""Convert the value of other to the power of self."""
self._check_unit(other)
if self.is_same_power(other):
return other.value
else:
return other.value * (other.scale / self.scale) # for numerical precision
##############################################
def _convert_scalar_value(self, value):
return float(value) / self.scale
##############################################
def __int__(self):
return int(self._value * self.scale)
##############################################
def __float__(self):
return float(self._value * self.scale)
##############################################
def str(self, spice=False, space=False, unit=True):
string = str(self._value)
if space:
string += ' '
string += self._unit_power.str(spice, unit)
return string
##############################################
def str_space(self):
return self.str(space=True)
##############################################
def str_spice(self):
return self.str(spice=True, space=False, unit=True)
##############################################
def __str__(self):
return self.str(spice=False, space=True, unit=True)
##############################################
def __bool__(self):
"""True if self != 0. Called for bool(self)."""
return self._value != 0
##############################################
def __add__(self, other):
"""self + other"""
if (isinstance(other, UnitValue)):
self._check_unit(other)
new_obj = self.clone()
new_obj._value += self._convert_value(other)
return new_obj
else:
return float(self) + other
##############################################
def __iadd__(self, other):
"""self += other"""
self._check_unit(other)
self._value += self._convert_value(other)
return self
##############################################
def __radd__(self, other):
"""other + self"""
return float(self) + other
##############################################
def __neg__(self):
"""-self"""
return self.clone_unit_power(-self._value)
##############################################
def __pos__(self):
"""+self"""
return self.clone()
##############################################
def __sub__(self, other):
"""self - other"""
if (isinstance(other, UnitValue)):
self._check_unit(other)
new_obj = self.clone()
new_obj._value -= self._convert_value(other)
return new_obj
else:
return float(self) - other
##############################################
def __isub__(self, other):
"""self -= other"""
self._check_unit(other)
self._value -= self._convert_value(other)
return self
##############################################
def __rsub__(self, other):
"""other - self"""
return other - float(self)
##############################################
def __mul__(self, other):
"""self * other"""
if (isinstance(other, UnitValue)):
equivalent_unit = self.unit.mul(other.unit, True)
value = float(self) * float(other)
return equivalent_unit.new_value(value)
else:
try: # scale value
scalar = float(other)
new_obj = self.clone()
new_obj._value *= scalar
return new_obj
except (ValueError, TypeError): # Numpy raises TypeError
return float(self) * other
##############################################
def __imul__(self, other):
"""self *= other"""
if (isinstance(other, UnitValue)):
raise UnitError
else: # scale value
# Fixme: right ?
self._value *= self._convert_value(other)
return self
##############################################
def __rmul__(self, other):
"""other * self"""
if (isinstance(other, UnitValue)):
raise NotImplementedError # Fixme: when ???
else: # scale value
return self.__mul__(other)
##############################################
def __floordiv__(self, other):
"""self // other """
if (isinstance(other, UnitValue)):
equivalent_unit = self.unit.div(other.unit, True)
value = float(self) // float(other)
return equivalent_unit.new_value(value)
else:
try: # scale value
scalar = float(other)
new_obj = self.clone()
new_obj._value //= scalar
return new_obj
except (ValueError, TypeError): # Numpy raises TypeError
return float(self) // other
##############################################
def __ifloordiv__(self, other):
"""self //= other """
if (isinstance(other, UnitValue)):
raise NotImplementedError
else: # scale value
self._value //= float(other)
return self
##############################################
def __rfloordiv__(self, other):
"""other // self"""
if (isinstance(other, UnitValue)):
raise NotImplementedError # Fixme: when ???
else: # scale value
return other // float(self)
##############################################
def __truediv__(self, other):
"""self / other"""
if (isinstance(other, UnitValue)):
equivalent_unit = self.unit.div(other.unit, True)
value = float(self) / float(other)
return equivalent_unit.new_value(value)
else:
try: # scale value
scalar = float(other)
new_obj = self.clone()
new_obj._value /= scalar
return new_obj
except (ValueError, TypeError): # Numpy raises TypeError
return float(self) / other
##############################################
def __itruediv__(self, other):
"""self /= other"""
if (isinstance(other, UnitValue)):
raise NotImplementedError
else: # scale value
self._value /= float(other)
return self
##############################################
def __rtruediv__(self, other):
"""other / self"""
if (isinstance(other, UnitValue)):
raise NotImplementedError # Fixme: when ???
else: # scale value
return other / float(self)
##############################################
def __pow__(self, exponent):
"""self**exponent; should promote to float or complex when necessary."""
new_obj = self.clone()
new_obj._value **= float(exponent)
return new_obj
##############################################
def __ipow__(self, exponent):
self._value **= float(exponent)
return self
##############################################
def __rpow__(self, base):
"""base ** self"""
raise NotImplementedError
##############################################
def __abs__(self):
"""Returns the Real distance from 0. Called for abs(self)."""
return self.clone_unit_power(abs(self._value))
##############################################
def __trunc__(self):
"""trunc(self): Truncates self to an Integral.
Returns an Integral i such that:
* i>0 iff self>0;
* abs(i) <= abs(self);
* for any Integral j satisfying the first two conditions,
abs(i) >= abs(j) [i.e. i has "maximal" abs among those].
i.e. "truncate towards 0".
"""
raise NotImplementedError
##############################################
def __divmod__(self, other):
"""divmod(self, other): The pair (self // other, self % other).
Sometimes this can be computed faster than the pair of
operations.
"""
return (self // other, self % other)
##############################################
def __rdivmod__(self, other):
"""divmod(other, self): The pair (self // other, self % other).
Sometimes this can be computed faster than the pair of
operations.
"""
return (other // self, other % self)
##############################################
def __mod__(self, other):
"""self % other"""
raise NotImplementedError
##############################################
def __rmod__(self, other):
"""other % self"""
raise NotImplementedError
##############################################
def __lt__(self, other):
"""self < other
< on Reals defines a total ordering, except perhaps for NaN."""
return float(self) < float(other)
##############################################
def __le__(self, other):
"""self <= other"""
return float(self) <= float(other)
##############################################
def __ceil__(self):
return math.ceil(float(self))
##############################################
def __floor__(self):
return math.floor(float(self))
##############################################
def __round__(self):
return round(float(self))
##############################################
def inverse(self):
equivalent_unit = self.unit.inverse(unit_power=True)
inverse_value = 1. / float(self)
return equivalent_unit.new_value(inverse_value)
##############################################
def get_unit_power(self, power=0):
unit_power = UnitPower.from_unit_power(self.unit, power)
if unit_power is not None:
return unit_power
else:
raise NameError("Unit power not found for {} and power {}".format(self, power))
##############################################
def convert(self, unit_power):
"""Convert the value to another power."""
self._unit_power.check_unit(unit_power)
if self._unit_power.is_same_power(unit_power):
return self
else:
value = float(self) / unit_power.scale
return unit_power.new_value(value)
##############################################
def convert_to_power(self, power=0):
"""Convert the value to another power."""
if power == 0:
value = float(self)
else:
value = float(self) / 10**power
return self.get_unit_power(power).new_value(value)
##############################################
def canonise(self):
# log10(10**n) = n log10(1) = 0 log10(10**-n) = -n log10(0) = -oo
try:
abs_value = abs(float(self))
log = math.log(abs_value)/math.log(1000)
# if abs_value >= 1:
# power = 3 * int(log)
# else:
# if log - int(log): # frac
# power = 3 * (int(log) -1)
# else:
# power = 3 * int(log)
power = int(log)
if abs_value < 1 and (log - int(log)):
power -= 1
power *= 3
# print('Unit.canonise', self, self._value, int(self._power), '->', float(self), power)
if power == int(self.power):
# print('Unit.canonise noting to do for', self)
return self
else:
# print('Unit.canonise convert', self, 'to', power)
# print('Unit.canonise convert', self, 'to', Unit)
return self.convert_to_power(power)
except Exception as e: # Fixme: fallback
self._logger.warning(e)
return self
####################################################################################################
# Reset
UnitPower.__value_ctor__ = UnitValue
_simple_unit_power = UnitPower()
####################################################################################################
class FrequencyMixin:
""" This class implements a frequency mixin. """
##############################################
@property
def period(self):
r""" Return the period :math:`T = \frac{1}{f}`. """
return self.inverse()
##############################################
@property
def pulsation(self):
r""" Return the pulsation :math:`\omega = 2\pi f`. """
return float(self * 2 * math.pi)
####################################################################################################
class PeriodMixin:
""" This class implements a period mixin. """
##############################################
@property
def frequency(self):
r""" Return the period :math:`f = \frac{1}{T}`. """
return self.inverse()
##############################################
@property
def pulsation(self):
r""" Return the pulsation :math:`\omega = \frac{2\pi}{T}`. """
return self.frequency.pulsation
PySpice-v0.4.1/PySpice/Unit/__init__.py 0000664 0000000 0000000 00000016157 13140600222 0017662 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2017 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This module implements units.
Shortcuts are defined to build unit values easily :
* for each unit prefix, e.g. :func:`pico`, :func:`nano`, :func:`micro`, :func:`milli`, :func:`kilo`,
:func:`mega`, :func:`tera`. These shortcuts return unit less values.
* for each unit and prefix as the concatenation of *u_*, the unit prefix and the
unit suffix, e.g. :func:`u_pV`, :func:`u_nV`, :func:`u_uV` :func:`u_mV`, :func:`u_V`,
:func:`u_kV`, :func:`u_MV`, :func:`u_TV`.
Theses unit value constructors accept int, float, object that can be converted to float,
:class:`UnitValue` instance and an iterable on these types.
A shortcut is defined to check an unit value match a particular unit, e.g. :func:`as_V`. Theses
shortcuts return the value if the unit match else it raises the exception *UnitError*.
A shortcut is defined to access each unit, e.g. :func:`U_V`, :func:`U_A`, :func:`U_s`, :func:`U_Hz`,
:func:`U_Ω`, :func:`U_F`, :func:`U_H.`
Some shortcuts have Unicode and ASCII variants:
* For micro, we have the prefix *μ* and *u*.
* For Ohm, we have :func:`u_Ω` and :func:`u_Ohm`.
Some examples of usage:
.. code-block:: python3
foo = kilo(1) # unit less
resistance_unit = U_Ω
resistance1 = u_kΩ(1)
resistance1 = u_kOhm(1) # ASCII variant
resistance1 = 1@u_kΩ # using Python 3.5 syntax
resistance1 = 1 @u_kΩ # space doesn't matter
resistance1 = 1 @ u_kΩ #
resistance2 = as_Ω(resistance1) # check unit
resistances = u_kΩ(range(1, 11)) # same as [u_kΩ(x) for x in range(1, 11)]
resistances = range(1, 11)@u_kΩ # using Python 3.5 syntax
capacitance = u_uF(200)
inductance = u_mH(1)
temperature = u_Degree(25)
voltage = resistance1 * u_mA(1) # compute unit
frequency = u_ms(20).frequency
period = u_Hz(50).period
pulsation = frequency.pulsation
pulsation = period.pulsation
"""
####################################################################################################
import logging
import sys
from . import Unit as _Unit
from . import SiUnits as _SiUnits
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
_version_info = sys.version_info
_has_matmul = _version_info.major * 10 + _version_info.minor >= 35
if not _has_matmul:
_module_logger.warning("Your Python version doesn't implement @ operator")
####################################################################################################
class UnitValueShorcut:
##############################################
def __init__(self, unit_power):
self._unit_power = unit_power
##############################################
def _new_value(self, other):
return self._unit_power.new_value(other)
##############################################
def __call__(self, other):
"""self(other)"""
return self._new_value(other)
##############################################
def __rmatmul__(self, other):
"""other @ self"""
return self._new_value(other)
####################################################################################################
def _to_ascii(name):
ascii_name = name
for args in (
('μ', 'u'),
('Ω', 'Ohm'),
('°C', 'Degree'),
):
ascii_name = ascii_name.replace(*args)
return ascii_name
def define_shortcut(name, shortcut) :
# ° is illegal in Python 3.5
# see https://docs.python.org/3/reference/lexical_analysis.html#identifiers
# https://www.python.org/dev/peps/pep-3131/
if '°' not in name:
globals()[name] = shortcut
ascii_name = _to_ascii(name)
if ascii_name != name:
globals()[ascii_name] = shortcut
####################################################################################################
# Define shortcuts for unit prefixes : ..., micro, milli, kilo, mega, ...
def _build_prefix_shortcut(unit_prefix):
unit_cls_name = unit_prefix.__class__.__name__
name = unit_cls_name.lower()
unit_power = _Unit.UnitPower(power=unit_prefix)
_Unit.UnitPower.register(unit_power)
shortcut = lambda value: _Unit.UnitValue(unit_power, value)
define_shortcut(name, shortcut)
for unit_prefix in _Unit.UnitPrefixMetaclass.prefix_iter():
if unit_prefix.__class__ != _Unit.ZeroPower:
_build_prefix_shortcut(unit_prefix) # capture unit_prefix
####################################################################################################
# Fixme: better ???
class FrequencyValue(_Unit.UnitValue, _Unit.FrequencyMixin):
pass
class PeriodValue(_Unit.UnitValue, _Unit.PeriodMixin):
pass
####################################################################################################
# Define unit shortcuts
def _build_unit_type_shortcut(unit):
name = 'U_' + unit.unit_suffix
define_shortcut(name, unit)
def _build_as_unit_shortcut(unit):
name = 'as_' + unit.unit_suffix
shortcut = unit.validate
define_shortcut(name, shortcut)
def _exec_body(ns, unit_prefix):
ns['__power__'] = unit_prefix
def _build_unit_prefix_shortcut(unit, unit_prefix):
name = 'u_' + str(unit_prefix) + unit.unit_suffix
if unit.__class__ == _SiUnits.Hertz:
value_ctor = FrequencyValue
elif unit.__class__ == _SiUnits.Second:
value_ctor = PeriodValue
else:
value_ctor = _Unit.UnitValue
unit_power = _Unit.UnitPower(unit, unit_prefix, value_ctor)
_Unit.UnitPower.register(unit_power)
shortcut = UnitValueShorcut(unit_power)
define_shortcut(name, shortcut)
def _build_unit_shortcut(unit):
_build_as_unit_shortcut(unit)
_build_unit_type_shortcut(unit)
for unit_prefix in _Unit.UnitPrefixMetaclass.prefix_iter():
if unit_prefix.is_defined_in_spice:
_build_unit_prefix_shortcut(unit, unit_prefix)
for unit in _Unit.UnitMetaclass.unit_iter():
if unit.unit_suffix and unit.__class__ not in (_SiUnits.Kilogram,):
# Fixme: kilogram
_build_unit_shortcut(unit)
####################################################################################################
unit_value = _Unit.UnitValue.simple_value
Frequency = u_Hz
Period = u_s
PySpice-v0.4.1/PySpice/__init__.py 0000664 0000000 0000000 00000001627 13140600222 0016737 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.4.1/README.html 0000664 0000000 0000000 00000047771 13140600222 0015107 0 ustar 00root root 0000000 0000000
Git repository cleanup: filtered generated doc and useless files so as to shrink the repository size
Improved documentation generator: Implemented format for RST content and Tikz figure.
Improved unit support: It implements now the International System of Units.
And we can now use unit helper like u_mV or compute the value of 1.2@u_kΩ / 2@u_mA.
The relevant documentation is on this page.
Added the Simulation instance to the Analysis class.
Refactored simulation parameters as classes.
V0.3.2
fixed CCCS and CCVS
V0.3.1
fixed ngspice shared
V0.3.0
Added an example to show how to use the NgSpice Shared Simulation Mode.
Completed the Spice netlist parser and added examples, we could now use a schematic editor
to define the circuit. The program cir2py translates a circuit file to Python.
PySpice is a Python 3 library which interplay with Berkeley SPICE, the industrial circuit
simulator reference.
SPICE (Simulation Program with Integrated Circuit Emphasis) was developed at the Electronics
Research Laboratory of the University of California, Berkeley in 1973 by Laurence Nagel with
direction from his research advisor, Prof. Donald Pederson. Then Spice emerged as an industrial
standard through its descendants and is still the reference 40 years later.
PySpice is born as a personal project to relearn electronics where circuit simulation is a part of
this goal. Since I use the Python language every day, I quickly feel the need to plug SPICE and Python.
The aim of PySpice is to address several needs:
SPICE language is fine to describe circuits, but it lacks a real language for circuit
steering. By contrast Python is a powerful high level, oriented object and dynamic language which
is perfectly suited for steering and reusing circuit. But it comes at the price its more general
syntax is less fluent than SPICE for circuit description.
Ngspice provides some extension to Berkeley SPICE for data analysis, but its interactive
environment or TCL module are now outdated. By contrast Python has scientific framework like
Numpy and Matplotlib that compete with Matlab.
Ngspice source code is derived from Berkeley SPICE and thus has a very old basis. Moreover the
sources are poorly documented. So it is really difficult to understand how it works and modify
it. PySpice could serve to easily experiment extension.
As opposite to other SPICE derivatives, PySpice focus on programming and not on graphical user
interface. Thus it doesn't feature a schematic capture editor and we cannot pickup a node or an
element and plot the associated waveform. Moreover we can notice the Modelica language treats
diagrams as annotations. A choice which I consider judicious. Thus we can imagine to follow the
same principle and extend PySpice later.
an oriented-object API to describe circuit in a way similar to SPICE
a library and model manager that index recursively a directory
an (experimental) SPICE netlist parser. Kicad can be used as schematic editor to simplify the
netlist writing.
a circuit can be simulated using a subprocess (aka server mode) or using the NgSpice shared library,
NgSpice vectors are converted to Numpy array
the NgSpice shared library permits to plug voltage/current sources from Python to NgSpice and vice versa.
some data analysis add-ons
Since PySpice is born with a learning goal, many examples are provided with the sources. These
examples could serve as learning materials. A tool to generate an HTML and PDF documentation is
included in the tools directory. This tool could be extended to generate IPython Notebook as well.
Planned Features
These features are planned in the future:
implement a basic simulator featuring passive element like resistor, capacitor and inductor.
implement a Modelica backend. Modelica is a very interesting solution for transient analysis.
Advertisements
Users should be aware of these advertisements.
Warning
Ngspice and PySpice are provided without any warranty. Thus you must use it with care
for real design. Best is to cross check the simulation using an industrial grade
simulator.
Warning
Simulation is a design tool and not a perfect description of the real world.
Installation
The installation of PySpice by itself is quite simple. However it will be easier to get the
dependencies on Linux.
Usually Ngspice is available as a package on the most popular Linux distributions. But I recommend
to check the compilation options before to use it extensively. For example the Fedora package
enables too many experimental codes that have side effects. The recommended way to compile Ngspice
is given in the manual and in the INSTALLATION file. Ngspice is an example of complex software
where we should not enable everything without care.
Warning
Compilation option --enable-ndev is known to broke the server mode.
You have to solution to get the source code, the first one is to clone the repository, but if you
are not familiar with Git then you can simply download an archive either from the PySpice Pypi page
(at the bottom) or the GitHub page (see clone or download button).
To clone the Git repository, run this command in a console:
Then to build and install PySpice run these commands:
python setup.py build
python setup.py install
PySpice-v0.4.1/README.rst 0000664 0000000 0000000 00000025412 13140600222 0014737 0 ustar 00root root 0000000 0000000 .. -*- Mode: rst -*-
.. -*- Mode: rst -*-
..
|PySpiceUrl|
|PySpiceHomePage|_
|PySpiceDoc|_
|PySpice@github|_
|PySpice@readthedocs|_
|PySpice@readthedocs-badge|
|PySpice@pypi|_
.. |ohloh| image:: https://www.openhub.net/accounts/230426/widgets/account_tiny.gif
:target: https://www.openhub.net/accounts/fabricesalvaire
:alt: Fabrice Salvaire's Ohloh profile
:height: 15px
:width: 80px
.. |PySpiceUrl| replace:: https://pyspice.fabrice-salvaire.fr
.. |PySpiceHomePage| replace:: PySpice Home Page
.. _PySpiceHomePage: https://pyspice.fabrice-salvaire.fr
.. |PySpice@readthedocs-badge| image:: https://readthedocs.org/projects/pyspice/badge/?version=latest
:target: http://pyspice.readthedocs.org/en/latest
.. |PySpice@github| replace:: https://github.com/FabriceSalvaire/PySpice
.. .. _PySpice@github: https://github.com/FabriceSalvaire/PySpice
.. |PySpice@pypi| replace:: https://pypi.python.org/pypi/PySpice
.. .. _PySpice@pypi: https://pypi.python.org/pypi/PySpice
.. |Build Status| image:: https://travis-ci.org/FabriceSalvaire/PySpice.svg?branch=master
:target: https://travis-ci.org/FabriceSalvaire/PySpice
:alt: PySpice build status @travis-ci.org
.. |Pypi Version| image:: https://img.shields.io/pypi/v/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice last version
.. |Pypi License| image:: https://img.shields.io/pypi/l/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice license
.. |Pypi Python Version| image:: https://img.shields.io/pypi/pyversions/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice python version
.. coverage test
.. https://img.shields.io/pypi/status/Django.svg
.. https://img.shields.io/github/stars/badges/shields.svg?style=social&label=Star
.. End
.. -*- Mode: rst -*-
.. |Ngspice| replace:: Ngspice
.. _Ngspice: http://ngspice.sourceforge.net
.. |Python| replace:: Python
.. _Python: http://python.org
.. |PyPI| replace:: PyPI
.. _PyPI: https://pypi.python.org/pypi
.. |Numpy| replace:: Numpy
.. _Numpy: http://www.numpy.org
.. |Matplotlib| replace:: Matplotlib
.. _Matplotlib: http://matplotlib.org
.. |CFFI| replace:: CFFI
.. _CFFI: http://cffi.readthedocs.org/en/latest/
.. |IPython| replace:: IPython
.. _IPython: http://ipython.org
.. |Sphinx| replace:: Sphinx
.. _Sphinx: http://sphinx-doc.org
.. |Modelica| replace:: Modelica
.. _Modelica: http://www.modelica.org
.. |Kicad| replace:: Kicad
.. _Kicad: http://www.kicad-pcb.org
.. |Circuit_macros| replace:: Circuit_macros
.. _Circuit_macros: http://ece.uwaterloo.ca/~aplevich/Circuit_macros
.. End
=========
PySpice
=========
Developed by `Fabrice Salvaire `_.
The official PySpice Home Page is located at |PySpiceUrl|
`PyParis2017 / Circuit Simulation using Python, by Fabrice Salvaire
`_
: talk given at the `PyParis 2017 `_ conference
(`PDF file `_)
|Pypi License|
|Pypi Python Version|
|Pypi Version|
-----
.. -*- Mode: rst -*-
V0.4.0
------
* Git repository cleanup: filtered generated doc and useless files so as to shrink the repository size
* Improved documentation generator: Implemented :code:`format` for RST content and Tikz figure.
* Improved unit support: It implements now the International System of Units.
And we can now use unit helper like :code:`u_mV` or compute the value of :code:`1.2@u_kΩ / 2@u_mA`.
The relevant documentation is on this `page `_.
* Added the Simulation instance to the Analysis class.
* Refactored simulation parameters as classes.
V0.3.2
------
* fixed CCCS and CCVS
V0.3.1
------
* fixed ngspice shared
V0.3.0
------
* Added an example to show how to use the NgSpice Shared Simulation Mode.
* Completed the Spice netlist parser and added examples, we could now use a schematic editor
to define the circuit. The program *cir2py* translates a circuit file to Python.
.. End
.. -*- Mode: rst -*-
PySpice is a |Python|_ 3 library which interplay with Berkeley SPICE, the industrial circuit
simulator reference.
SPICE (Simulation Program with Integrated Circuit Emphasis) was developed at the Electronics
Research Laboratory of the University of California, Berkeley in 1973 by Laurence Nagel with
direction from his research advisor, Prof. Donald Pederson. Then Spice emerged as an industrial
standard through its descendants and is still the reference 40 years later.
PySpice is born as a personal project to relearn electronics where circuit simulation is a part of
this goal. Since I use the Python language every day, I quickly feel the need to plug SPICE and Python.
The aim of PySpice is to address several needs:
* SPICE language is fine to describe circuits, but it lacks a real language for circuit
steering. By contrast Python is a powerful high level, oriented object and dynamic language which
is perfectly suited for steering and reusing circuit. But it comes at the price its more general
syntax is less fluent than SPICE for circuit description.
* Ngspice provides some extension to Berkeley SPICE for data analysis, but its interactive
environment or TCL module are now outdated. By contrast Python has scientific framework like
|Numpy|_ and |Matplotlib|_ that compete with Matlab.
* Ngspice source code is derived from Berkeley SPICE and thus has a very old basis. Moreover the
sources are poorly documented. So it is really difficult to understand how it works and modify
it. PySpice could serve to easily experiment extension.
As opposite to other SPICE derivatives, PySpice focus on programming and not on graphical user
interface. Thus it doesn't feature a schematic capture editor and we cannot pickup a node or an
element and plot the associated waveform. Moreover we can notice the |Modelica|_ language treats
diagrams as annotations. A choice which I consider judicious. Thus we can imagine to follow the
same principle and extend PySpice later.
==========
Features
==========
.. -*- Mode: rst -*-
The main features of PySpice are:
* actually PySpice only supports |Ngspice|_
* an oriented-object API to describe circuit in a way similar to SPICE
* a library and model manager that index recursively a directory
* an (experimental) SPICE netlist parser. |Kicad|_ can be used as schematic editor to simplify the
netlist writing.
* a circuit can be simulated using a subprocess (aka server mode) or using the NgSpice shared library,
NgSpice vectors are converted to Numpy array
* the NgSpice shared library permits to plug voltage/current sources from Python to NgSpice and vice versa.
* some data analysis add-ons
Since PySpice is born with a learning goal, many examples are provided with the sources. These
examples could serve as learning materials. A tool to generate an HTML and PDF documentation is
included in the *tools* directory. This tool could be extended to generate IPython Notebook as well.
..
* an incomplete SPICE parser (mainly used for the library and model indexer)
* a circuit can be simulated using a subprocess (aka server mode) or using the NgSpice shared
library, NgSpice vectors are converted to Numpy array the NgSpice shared library permits to interact
with the simulator and provides Python callback for external voltage and current source
* implement a SPICE to Python converted using the parser. It could be used for the following
workflow: quick circuit sketching using > SPICE netlist > spice2python > PySpice which
could help for complex circuit.
.. end
==================
Planned Features
==================
These features are planned in the future:
* implement a basic simulator featuring passive element like resistor, capacitor and inductor.
* implement a Modelica backend. |Modelica|_ is a very interesting solution for transient analysis.
================
Advertisements
================
Users should be aware of these advertisements.
.. .. Warning:: The API is quite unstable until now. Some efforts is made to have a smooth API.
.. Warning:: Ngspice and PySpice are provided without any warranty. Thus you must use it with care
for real design. Best is to cross check the simulation using an industrial grade
simulator.
.. Warning:: Simulation is a design tool and not a perfect description of the real world.
.. End
.. -*- Mode: rst -*-
.. _installation-page:
==============
Installation
==============
The installation of PySpice by itself is quite simple. However it will be easier to get the
dependencies on Linux.
Dependencies
------------
PySpice requires the following dependencies:
* |Python|_ 3
* |Numpy|_
* |Matplotlib|_
* |Ngspice|_
* |CFFI|_ (only required for Ngspice shared)
Also it is recommanded to have these Python modules:
* |IPython|_
.. * pip
.. * virtualenv
To generate the documentation, you will need in addition:
* |Sphinx|_
* circuit_macros and a LaTeX environment
Ngspice Compilation
-------------------
Usually Ngspice is available as a package on the most popular Linux distributions. But I recommend
to **check the compilation options** before to use it extensively. For example the Fedora package
enables too many experimental codes that have side effects. The recommended way to compile Ngspice
is given in the manual and in the ``INSTALLATION`` file. Ngspice is an example of complex software
where we should not enable everything without care.
.. :file:`INSTALLATION`
.. warning::
Compilation option **--enable-ndev** is known to broke the server mode.
Installation from PyPi Repository
---------------------------------
PySpice is available on the Python Packages |Pypi|_ repository at |PySpice@pypi|
Run this command in the console to install the latest release:
.. code-block:: sh
pip install PySpice
How to get the Examples
-----------------------
Examples are not installed by ``pip`` or ``setup.pip``. The installation process only install
PySpice on your Python environment.
**You have to download the PySpice archive or clone the Git repository to get the examples.** See "Installation from Source".
Installation from Source
------------------------
The PySpice source code is hosted at |PySpice@github|
.. add link to pages ...
You have to solution to get the source code, the first one is to clone the repository, but if you
are not familiar with Git then you can simply download an archive either from the PySpice Pypi page
(at the bottom) or the GitHub page (see clone or download button).
To clone the Git repository, run this command in a console:
.. code-block:: sh
git clone git@github.com:FabriceSalvaire/PySpice.git
Then to build and install PySpice run these commands:
.. code-block:: sh
python setup.py build
python setup.py install
.. End
.. End
PySpice-v0.4.1/README.txt 0000664 0000000 0000000 00000001323 13140600222 0014741 0 ustar 00root root 0000000 0000000 .. -*- Mode: rst -*-
.. include:: project-links.txt
.. include:: abbreviation.txt
=========
PySpice
=========
Developed by `Fabrice Salvaire `_.
The official PySpice Home Page is located at |PySpiceUrl|
`PyParis2017 / Circuit Simulation using Python, by Fabrice Salvaire
`_
: talk given at the `PyParis 2017 `_ conference
(`PDF file `_)
|Pypi License|
|Pypi Python Version|
|Pypi Version|
-----
.. include:: news.txt
.. include:: introduction.txt
.. include:: installation.rst
.. End
PySpice-v0.4.1/bin/ 0000775 0000000 0000000 00000000000 13140600222 0014014 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/bin/cir2py 0000775 0000000 0000000 00000004340 13140600222 0015153 0 ustar 00root root 0000000 0000000 #! /usr/bin/env python3
####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import argparse
####################################################################################################
# import PySpice.Logging.Logging as Logging
# logger = Logging.setup_logging()
####################################################################################################
from PySpice.Spice.Parser import SpiceParser
####################################################################################################
parser = argparse.ArgumentParser(description='Convert a circuit file to PySpice')
parser.add_argument('circuit_file', # metavar='circuit_file',
help='.cir file')
parser.add_argument('-o', '--output',
default=None,
help='Output file')
parser.add_argument('--ground',
type=int,
default=0,
help='Ground node')
args = parser.parse_args()
####################################################################################################
parser = SpiceParser(path=args.circuit_file)
circuit = parser.to_python_code(ground=args.ground)
if args.output is not None:
with open(args.output, 'w') as f:
f.write(circuit)
else:
print(circuit)
PySpice-v0.4.1/bower/ 0000775 0000000 0000000 00000000000 13140600222 0014362 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/bower/bower.json 0000664 0000000 0000000 00000000716 13140600222 0016377 0 ustar 00root root 0000000 0000000 {
"name": "PySpice",
"version": "0.1.0",
"homepage": "https://github.com/FabriceSalvaire/PySpice",
"authors": [
"Fabrice Salvaire "
],
"description": "A Spice Package for Python",
"main": "",
"keywords": [
""
],
"license": "GPLv3",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"MathJax": "~2.5.3",
"jquery": "~2.1.4"
}
}
PySpice-v0.4.1/doc/ 0000775 0000000 0000000 00000000000 13140600222 0014011 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/doc/datasheets/ 0000775 0000000 0000000 00000000000 13140600222 0016136 5 ustar 00root root 0000000 0000000 PySpice-v0.4.1/doc/datasheets/1N4148_1N4448.pdf 0000664 0000000 0000000 00000363213 13140600222 0020322 0 ustar 00root root 0000000 0000000 %PDF-1.3
%
1260 0 obj <>
endobj
xref
1260 50
0000000016 00000 n
0000002124 00000 n
0000002258 00000 n
0000002502 00000 n
0000002530 00000 n
0000002575 00000 n
0000002610 00000 n
0000002818 00000 n
0000003012 00000 n
0000003525 00000 n
0000003563 00000 n
0000003641 00000 n
0000003718 00000 n
0000003910 00000 n
0000004631 00000 n
0000005242 00000 n
0000005809 00000 n
0000006408 00000 n
0000006977 00000 n
0000007113 00000 n
0000007557 00000 n
0000007937 00000 n
0000008158 00000 n
0000008496 00000 n
0000008723 00000 n
0000008950 00000 n
0000009545 00000 n
0000010054 00000 n
0000010245 00000 n
0000011066 00000 n
0000011869 00000 n
0000012602 00000 n
0000020597 00000 n
0000020830 00000 n
0000021060 00000 n
0000023731 00000 n
0000023791 00000 n
0000023872 00000 n
0000023961 00000 n
0000024070 00000 n
0000024177 00000 n
0000024278 00000 n
0000024388 00000 n
0000024507 00000 n
0000024615 00000 n
0000024727 00000 n
0000024827 00000 n
0000024924 00000 n
0000001917 00000 n
0000001342 00000 n
trailer
<<0879F2361EC44440AC282DF15550B83E>]>>
startxref
0
%%EOF
1309 0 obj<>stream
<ዼ ^C1ˏ$A>
n髀b+®S2;Tހ#ǛAN9\vڜ3L-uHW*wi3FLyoӤ20Gj*-7p/j7km(
k칈.CԶi"9+0y#<9dDVJn-A4`SzU7ѾFG~2K0^8y"
omL?!0KiESO@_+[ĸ[ƮhA"clpܞ)رũdqY8J&f"3j.FS31$7FL71E[e
I̠b
5IZ#iJx܇OBo0TK@kh[ӆygh 3?_+Aw/.PKGrٰ
endstream
endobj
1308 0 obj<>/Size 1260/Type/XRef>>stream
x1 0ð4t\GbG&`'MF[!<ăx= 2
endstream
endobj
1261 0 obj<>
endobj
1262 0 obj<>/PageMode/UseOutlines/Names 1265 0 R/Outlines 1295 0 R/Metadata 106 0 R/Pages 105 0 R/OpenAction[1266 0 R/XYZ null null null]/Threads 1263 0 R/StructTreeRoot 108 0 R/Type/Catalog/PageLabels 103 0 R>>
endobj
1263 0 obj[1264 0 R]
endobj
1264 0 obj<>>>
endobj
1265 0 obj<>
endobj
1266 0 obj<>
endobj
1267 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>
endobj
1268 0 obj<>
endobj
1269 0 obj[/ICCBased 1294 0 R]
endobj
1270 0 obj<>
endobj
1271 0 obj<>
endobj
1272 0 obj<>
endobj
1273 0 obj<>stream
>/f3z"@xϩٖ1m}W{:m=snN6v3r p"rAx5ڠ6m߰nO Cg-DV\7ݘdžH}^.yKws7+3+U'V>!S%B_ָQ :6Mus%'N֦ѦP"*k§D/bŹi|x_K[EmM.8rƐ'wL/,bL
pȶ <Ij.vrHy
<ڭy :r}5YBg?9xva%`P-CAF,[9leϖA"a >. =:iM|2}Ӥ|ڑD#N7C)P>*%Ҍ}LNFuIɞV efڡwjaʊ6#?]#
S2kėJV.eG{;ԝn䷿l9irgUGY*%hQ -
endstream
endobj
1274 0 obj<>stream
?#YXYm7:K1<9آMrIBj2ImI+$!'T{z".J