├── .gitignore ├── LICENSE ├── README.md ├── examples ├── .ipynb_checkpoints │ ├── advanced baseline processing-checkpoint.ipynb │ ├── fitting over and undecoupled resonators in refelction-checkpoint.ipynb │ └── pandas_and_circlefit-checkpoint.ipynb ├── S11.txt ├── S21testdata.s2p ├── advanced baseline processing.ipynb ├── fitting over and undecoupled resonators in reflection.ipynb ├── notch_port_example.py ├── notch_port_example_withGUI.py ├── pandas_and_circlefit.ipynb ├── reflection_port_example.py ├── reflection_port_example_withGUI.py └── remove_wiggly_baseline_with GUI.py ├── old_discontinued_circlefit └── Circlefit_V3.1.zip ├── resonator_tools ├── __init__.py ├── calibration.py ├── circlefit.py ├── circuit.py ├── noise.py └── utilities.py ├── setup.py └── tests ├── __init__.py └── resonator_tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # resonator_tools 2 | 3 | A small python library to fit complex resonator scattering data. 4 | It supports transmission, reflection and notch-type measurements. 5 | 6 | For installation of the module execute: 7 | python setup.py install 8 | 9 | Description of the algorithm: 10 | http://scitation.aip.org/content/aip/journal/rsi/86/2/10.1063/1.4907935 11 | 12 | Where is it used? 13 | The "resonator_tools" have contributed to the following publications: 14 | https://scholar.google.de/scholar?oi=bibs&hl=de&cites=690000812747125148&as_sdt=5 15 | 16 | Questions? Contact the author: 17 | https://www.phi.kit.edu/ustinov_downloads.php 18 | -------------------------------------------------------------------------------- /examples/.ipynb_checkpoints/pandas_and_circlefit-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:25d58d0ad878b5b3eb3c04f9ab73c1310b0ec623d81be671993f72be36d8a557" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "Fitting a resonator measured in reflection" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "collapsed": false, 22 | "input": [ 23 | "from resonator_tools import circuit\n", 24 | "import numpy as np\n", 25 | "import pandas as pd\n", 26 | "from IPython.display import display\n", 27 | "%matplotlib inline" 28 | ], 29 | "language": "python", 30 | "metadata": {}, 31 | "outputs": [], 32 | "prompt_number": 1 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "Although we could use the resonator tools to load data, here we want to use the Pandas library, which is used for statistical data analysis. It can handle many different file types including hdf5." 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "collapsed": false, 44 | "input": [ 45 | "df = pd.read_csv('S11.txt',sep='\\t')" 46 | ], 47 | "language": "python", 48 | "metadata": {}, 49 | "outputs": [], 50 | "prompt_number": 2 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "Pandas has a very nice way of displaying the data. Let's look at the first few entries:" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "collapsed": false, 62 | "input": [ 63 | "display(df.head())" 64 | ], 65 | "language": "python", 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "html": [ 70 | "
\n", 71 | "\n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | "
freqmagphase
0 7.112886e+09-29.041420 40.656170
1 7.112886e+09-29.042747 40.569664
2 7.112887e+09-29.045673 40.464161
3 7.112887e+09-29.049311 40.361294
4 7.112887e+09-29.051012 40.265739
\n", 113 | "
" 114 | ], 115 | "metadata": {}, 116 | "output_type": "display_data", 117 | "text": [ 118 | " freq mag phase\n", 119 | "0 7.112886e+09 -29.041420 40.656170\n", 120 | "1 7.112886e+09 -29.042747 40.569664\n", 121 | "2 7.112887e+09 -29.045673 40.464161\n", 122 | "3 7.112887e+09 -29.049311 40.361294\n", 123 | "4 7.112887e+09 -29.051012 40.265739" 124 | ] 125 | } 126 | ], 127 | "prompt_number": 3 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "Next, we define a reflection port measurement and add the data." 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "collapsed": false, 139 | "input": [ 140 | "port1 = circuit.reflection_port(f_data=df[\"freq\"].values,\n", 141 | " z_data_raw=10**(df[\"mag\"].values/20.)*np.exp(1j*df[\"phase\"].values/180.*np.pi))" 142 | ], 143 | "language": "python", 144 | "metadata": {}, 145 | "outputs": [], 146 | "prompt_number": 4 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Perform an automated fit." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "collapsed": false, 158 | "input": [ 159 | "port1.autofit()" 160 | ], 161 | "language": "python", 162 | "metadata": {}, 163 | "outputs": [], 164 | "prompt_number": 5 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "Let's plot the data and the fit!" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "collapsed": false, 176 | "input": [ 177 | "port1.plotall()" 178 | ], 179 | "language": "python", 180 | "metadata": {}, 181 | "outputs": [ 182 | { 183 | "metadata": {}, 184 | "output_type": "display_data", 185 | "png": "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\n", 186 | "text": [ 187 | "" 188 | ] 189 | } 190 | ], 191 | "prompt_number": 6 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "Next, let us have a look at the fit results. Here, we convert the dictionary of results into a dataframe to display it in a nicer way." 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "collapsed": false, 203 | "input": [ 204 | "display(pd.DataFrame([port1.fitresults]).applymap(lambda x: \"{0:.2e}\".format(x)))" 205 | ], 206 | "language": "python", 207 | "metadata": {}, 208 | "outputs": [ 209 | { 210 | "html": [ 211 | "
\n", 212 | "\n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | "
QcQc_errQiQi_errQlQl_errchi_squarefrfr_errtheta0
0 3.48e+05 2.46e+02 9.30e+05 1.74e+03 2.53e+05 1.96e+02 2.87e-05 7.11e+09 7.19e+00 -4.18e-03
\n", 244 | "
" 245 | ], 246 | "metadata": {}, 247 | "output_type": "display_data", 248 | "text": [ 249 | " Qc Qc_err Qi Qi_err Ql Ql_err chi_square \\\n", 250 | "0 3.48e+05 2.46e+02 9.30e+05 1.74e+03 2.53e+05 1.96e+02 2.87e-05 \n", 251 | "\n", 252 | " fr fr_err theta0 \n", 253 | "0 7.11e+09 7.19e+00 -4.18e-03 " 254 | ] 255 | } 256 | ], 257 | "prompt_number": 7 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "Finally, we can calculate the single photon limit, i.e., the input power necessary to maintain one photon on average in the resonator:" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "collapsed": false, 269 | "input": [ 270 | "print 'Single photon limit: %.2f dBm' % port1.get_single_photon_limit()" 271 | ], 272 | "language": "python", 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "output_type": "stream", 277 | "stream": "stdout", 278 | "text": [ 279 | "Single photon limit: -163.42 dBm\n" 280 | ] 281 | } 282 | ], 283 | "prompt_number": 8 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "Or, we can compute the photons in the resonator for a given power:" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "collapsed": false, 295 | "input": [ 296 | "print 'At -100dBm, we have %.2e photons in the resonator' % port1.get_photons_in_resonator(-100)" 297 | ], 298 | "language": "python", 299 | "metadata": {}, 300 | "outputs": [ 301 | { 302 | "output_type": "stream", 303 | "stream": "stdout", 304 | "text": [ 305 | "At -100dBm, we have 2.20e+06 photons in the resonator\n" 306 | ] 307 | } 308 | ], 309 | "prompt_number": 9 310 | } 311 | ], 312 | "metadata": {} 313 | } 314 | ] 315 | } -------------------------------------------------------------------------------- /examples/S11.txt: -------------------------------------------------------------------------------- 1 | freq mag phase 2 | 7112886151.79 -29.0414199829 40.6561698914 3 | 7112886351.79 -29.0427474976 40.5696640015 4 | 7112886551.79 -29.0456733704 40.4641609192 5 | 7112886751.79 -29.0493106842 40.3612937927 6 | 7112886951.79 -29.0510120392 40.2657394409 7 | 7112887151.79 -29.0533924103 40.1798477173 8 | 7112887351.79 -29.0567092896 40.0749206543 9 | 7112887551.79 -29.0580387115 39.9818305969 10 | 7112887751.79 -29.0593109131 39.8811187744 11 | 7112887951.79 -29.0630283356 39.7868423462 12 | 7112888151.79 -29.0647144318 39.6951637268 13 | 7112888351.79 -29.0678272247 39.5831642151 14 | 7112888551.79 -29.0699329376 39.4860153198 15 | 7112888751.79 -29.0715961456 39.391872406 16 | 7112888951.79 -29.0759296417 39.2727966309 17 | 7112889151.79 -29.0784244537 39.1619377136 18 | 7112889351.79 -29.0816917419 39.0605621338 19 | 7112889551.79 -29.0843925476 38.9425621033 20 | 7112889751.79 -29.0861759186 38.8364524841 21 | 7112889951.79 -29.0888748169 38.7343864441 22 | 7112890151.79 -29.0907878876 38.639629364 23 | 7112890351.79 -29.0935974121 38.5414237976 24 | 7112890551.79 -29.0982971191 38.4248390198 25 | 7112890751.79 -29.1013412476 38.303730011 26 | 7112890951.79 -29.1045303345 38.1727790833 27 | 7112891151.79 -29.1083679199 38.0731201172 28 | 7112891351.79 -29.1106014252 37.9570999146 29 | 7112891551.79 -29.1139678955 37.8442077637 30 | 7112891751.79 -29.1176128387 37.7267723083 31 | 7112891951.79 -29.1202831268 37.6098442078 32 | 7112892151.79 -29.12317276 37.4988555908 33 | 7112892351.79 -29.1271800995 37.3827476501 34 | 7112892551.79 -29.1292800903 37.2751083374 35 | 7112892751.79 -29.1342372894 37.1326980591 36 | 7112892951.79 -29.1358661652 37.0348052979 37 | 7112893151.79 -29.1389389038 36.9019088745 38 | 7112893351.79 -29.1438274384 36.764705658 39 | 7112893551.79 -29.1457023621 36.6560096741 40 | 7112893751.79 -29.1498298645 36.5187606812 41 | 7112893951.79 -29.1539573669 36.3929328918 42 | 7112894151.79 -29.158700943 36.2456436157 43 | 7112894351.79 -29.1623821259 36.1094322205 44 | 7112894551.79 -29.1661434174 35.9690322876 45 | 7112894751.79 -29.1693553925 35.8453178406 46 | 7112894951.79 -29.173658371 35.7035751343 47 | 7112895151.79 -29.1772575378 35.5668106079 48 | 7112895351.79 -29.1809139252 35.4237327576 49 | 7112895551.79 -29.1855316162 35.3064651489 50 | 7112895751.79 -29.1897659302 35.1513328552 51 | 7112895951.79 -29.193862915 35.0018997192 52 | 7112896151.79 -29.197807312 34.8756599426 53 | 7112896351.79 -29.2012386322 34.7470245361 54 | 7112896551.79 -29.2064361572 34.5761680603 55 | 7112896751.79 -29.2108345032 34.4437828064 56 | 7112896951.79 -29.2154197693 34.2875938416 57 | 7112897151.79 -29.2198905945 34.1348381042 58 | 7112897351.79 -29.2246608734 33.9788627625 59 | 7112897551.79 -29.2296981812 33.8254547119 60 | 7112897751.79 -29.23412323 33.678817749 61 | 7112897951.79 -29.2384815216 33.5318450928 62 | 7112898151.79 -29.2431163788 33.3696174622 63 | 7112898351.79 -29.247713089 33.2201156616 64 | 7112898551.79 -29.2534046173 33.0596809387 65 | 7112898751.79 -29.2590484619 32.8931732178 66 | 7112898951.79 -29.263174057 32.7329368591 67 | 7112899151.79 -29.2676391602 32.5650787354 68 | 7112899351.79 -29.2732505798 32.3928413391 69 | 7112899551.79 -29.2797985077 32.2118835449 70 | 7112899751.79 -29.2849655151 32.0393829346 71 | 7112899951.79 -29.2901611328 31.8732681274 72 | 7112900151.79 -29.2954788208 31.692609787 73 | 7112900351.79 -29.3021087646 31.5010166168 74 | 7112900551.79 -29.308221817 31.3414974213 75 | 7112900751.79 -29.3129444122 31.1600170135 76 | 7112900951.79 -29.3180503845 30.9811191559 77 | 7112901151.79 -29.3241786957 30.8062534332 78 | 7112901351.79 -29.3310012817 30.6291217804 79 | 7112901551.79 -29.3376045227 30.4313850403 80 | 7112901751.79 -29.343624115 30.261631012 81 | 7112901951.79 -29.3514900208 30.046251297 82 | 7112902151.79 -29.3568229675 29.8720684052 83 | 7112902351.79 -29.3621273041 29.6753311157 84 | 7112902551.79 -29.3707256317 29.470293045 85 | 7112902751.79 -29.3775997162 29.2704486847 86 | 7112902951.79 -29.3850440979 29.0597782135 87 | 7112903151.79 -29.3923797607 28.8383846283 88 | 7112903351.79 -29.4014110565 28.6495018005 89 | 7112903551.79 -29.4076557159 28.4371509552 90 | 7112903751.79 -29.4147796631 28.2311878204 91 | 7112903951.79 -29.4230575562 28.0089435577 92 | 7112904151.79 -29.4311981201 27.7708396912 93 | 7112904351.79 -29.4387378693 27.5503063202 94 | 7112904551.79 -29.4471683502 27.3329238892 95 | 7112904751.79 -29.4538383484 27.1263504028 96 | 7112904951.79 -29.4621543884 26.897726059 97 | 7112905151.79 -29.4718761444 26.6621742249 98 | 7112905351.79 -29.478723526 26.4633388519 99 | 7112905551.79 -29.4891242981 26.2256698608 100 | 7112905751.79 -29.4975471497 25.9607372284 101 | 7112905951.79 -29.506986618 25.7253665924 102 | 7112906151.79 -29.5159072876 25.4717540741 103 | 7112906351.79 -29.5269393921 25.2051677704 104 | 7112906551.79 -29.5357112885 24.9685630798 105 | 7112906751.79 -29.5446071625 24.7307376862 106 | 7112906951.79 -29.556219101 24.4516067505 107 | 7112907151.79 -29.5650367737 24.1830482483 108 | 7112907351.79 -29.5750083923 23.9382514954 109 | 7112907551.79 -29.5856361389 23.6791477203 110 | 7112907751.79 -29.5960712433 23.4198246002 111 | 7112907951.79 -29.6069087982 23.1382446289 112 | 7112908151.79 -29.6169109344 22.8575630188 113 | 7112908351.79 -29.6278076172 22.58411026 114 | 7112908551.79 -29.6418056488 22.2553825378 115 | 7112908751.79 -29.6521530151 22.0025959015 116 | 7112908951.79 -29.6649265289 21.7176818848 117 | 7112909151.79 -29.6777992249 21.3886528015 118 | 7112909351.79 -29.6896877289 21.0978393555 119 | 7112909551.79 -29.7010478973 20.8152046204 120 | 7112909751.79 -29.713596344 20.5196762085 121 | 7112909951.79 -29.7254142761 20.1888332367 122 | 7112910151.79 -29.740694046 19.8599643707 123 | 7112910351.79 -29.7530784607 19.5694332123 124 | 7112910551.79 -29.7673988342 19.2151088715 125 | 7112910751.79 -29.781671524 18.8664455414 126 | 7112910951.79 -29.7962684631 18.5416488647 127 | 7112911151.79 -29.8091106415 18.2299690247 128 | 7112911351.79 -29.8248672485 17.8784599304 129 | 7112911551.79 -29.8409881592 17.5183734894 130 | 7112911751.79 -29.8560905457 17.1820011139 131 | 7112911951.79 -29.8706893921 16.8432331085 132 | 7112912151.79 -29.8866004944 16.4722194672 133 | 7112912351.79 -29.9025096893 16.1211509705 134 | 7112912551.79 -29.9201469421 15.7231464386 135 | 7112912751.79 -29.9373950958 15.3374013901 136 | 7112912951.79 -29.9542217255 14.9592084885 137 | 7112913151.79 -29.9754161835 14.5362529755 138 | 7112913351.79 -29.9948310852 14.1298074722 139 | 7112913551.79 -30.0123271942 13.7574520111 140 | 7112913751.79 -30.02876091 13.3768577576 141 | 7112913951.79 -30.050365448 12.9352073669 142 | 7112914151.79 -30.0715389252 12.4788103104 143 | 7112914351.79 -30.0896682739 12.0710163116 144 | 7112914551.79 -30.1083698273 11.7014408112 145 | 7112914751.79 -30.134021759 11.1996707916 146 | 7112914951.79 -30.1552562714 10.7055835724 147 | 7112915151.79 -30.1743125916 10.3083209991 148 | 7112915351.79 -30.1995048523 9.8061170578 149 | 7112915551.79 -30.219789505 9.3702955246 150 | 7112915751.79 -30.2444820404 8.89730548859 151 | 7112915951.79 -30.2691516876 8.39351654053 152 | 7112916151.79 -30.2937870026 7.90898084641 153 | 7112916351.79 -30.3196315765 7.41554737091 154 | 7112916551.79 -30.3464679718 6.85498189926 155 | 7112916751.79 -30.3712825775 6.37863826752 156 | 7112916951.79 -30.3977909088 5.86556816101 157 | 7112917151.79 -30.4246349335 5.33062934875 158 | 7112917351.79 -30.4554672241 4.7455496788 159 | 7112917551.79 -30.4852657318 4.17402982712 160 | 7112917751.79 -30.5133953094 3.63583946228 161 | 7112917951.79 -30.545009613 3.07135176659 162 | 7112918151.79 -30.5737876892 2.52529907227 163 | 7112918351.79 -30.607831955 1.88873744011 164 | 7112918551.79 -30.640871048 1.25803089142 165 | 7112918751.79 -30.6717910767 0.67052179575 166 | 7112918951.79 -30.7087745667 0.0257931556553 167 | 7112919151.79 -30.7478637695 -0.664188325405 168 | 7112919351.79 -30.7801132202 -1.23034977913 169 | 7112919551.79 -30.8115139008 -1.8547513485 170 | 7112919751.79 -30.8507270813 -2.54084444046 171 | 7112919951.79 -30.887845993 -3.20032525063 172 | 7112920151.79 -30.9318161011 -3.95836472511 173 | 7112920351.79 -30.9733943939 -4.70586299896 174 | 7112920551.79 -31.0163440704 -5.44456672668 175 | 7112920751.79 -31.0589828491 -6.18624830246 176 | 7112920951.79 -31.1006069183 -6.9016046524 177 | 7112921151.79 -31.1454391479 -7.69521045685 178 | 7112921351.79 -31.1958217621 -8.55935382843 179 | 7112921551.79 -31.236497879 -9.22374153137 180 | 7112921751.79 -31.2865467072 -10.1176490784 181 | 7112921951.79 -31.3375377655 -10.9914064407 182 | 7112922151.79 -31.3891906738 -11.8570833206 183 | 7112922351.79 -31.4326152802 -12.6113204956 184 | 7112922551.79 -31.490196228 -13.5534944534 185 | 7112922751.79 -31.5448493958 -14.4647626877 186 | 7112922951.79 -31.6002445221 -15.372885704 187 | 7112923151.79 -31.6581115723 -16.3201522827 188 | 7112923351.79 -31.7132797241 -17.2387619019 189 | 7112923551.79 -31.776058197 -18.2660293579 190 | 7112923751.79 -31.8362770081 -19.2835788727 191 | 7112923951.79 -31.8941993713 -20.2449588776 192 | 7112924151.79 -31.9586372375 -21.3429145813 193 | 7112924351.79 -32.0264320374 -22.3861370087 194 | 7112924551.79 -32.093296051 -23.4916381836 195 | 7112924751.79 -32.1588668823 -24.5453548431 196 | 7112924951.79 -32.2366714478 -25.8191928864 197 | 7112925151.79 -32.305557251 -26.9577274323 198 | 7112925351.79 -32.3772392273 -28.1541690826 199 | 7112925551.79 -32.4515304565 -29.3701267242 200 | 7112925751.79 -32.5195617676 -30.5391597748 201 | 7112925951.79 -32.6015472412 -31.8435344696 202 | 7112926151.79 -32.6865158081 -33.1942749023 203 | 7112926351.79 -32.7633743286 -34.5236968994 204 | 7112926551.79 -32.839679718 -35.8208808899 205 | 7112926751.79 -32.9245681763 -37.2521743774 206 | 7112926951.79 -33.0130119324 -38.7764625549 207 | 7112927151.79 -33.0939445496 -40.1389846802 208 | 7112927351.79 -33.1859512329 -41.6905059814 209 | 7112927551.79 -33.2627830505 -43.0973205566 210 | 7112927751.79 -33.3534927368 -44.6995887756 211 | 7112927951.79 -33.4381713867 -46.2194366455 212 | 7112928151.79 -33.531124115 -47.9219055176 213 | 7112928351.79 -33.631526947 -49.6385688782 214 | 7112928551.79 -33.7270507813 -51.3959732056 215 | 7112928751.79 -33.8128738403 -53.0928916931 216 | 7112928951.79 -33.9056282043 -54.9134407043 217 | 7112929151.79 -34.0023612976 -56.7591667175 218 | 7112929351.79 -34.0971069336 -58.6793022156 219 | 7112929551.79 -34.1995506287 -60.6295661926 220 | 7112929751.79 -34.2840881348 -62.6748542786 221 | 7112929951.79 -34.3806266785 -64.7666244507 222 | 7112930151.79 -34.4722518921 -66.9149475098 223 | 7112930351.79 -34.55519104 -68.9359893799 224 | 7112930551.79 -34.6272697449 -70.6857833862 225 | 7112930751.79 -34.7184791565 -72.9267730713 226 | 7112930951.79 -34.8082771301 -75.3440170288 227 | 7112931151.79 -34.8979263306 -77.6356811523 228 | 7112931351.79 -34.9735679626 -79.917755127 229 | 7112931551.79 -35.0453948975 -82.1688079834 230 | 7112931751.79 -35.1205863953 -84.5878677368 231 | 7112931951.79 -35.1787223816 -86.8336868286 232 | 7112932151.79 -35.2483863831 -89.5069274902 233 | 7112932351.79 -35.3001251221 -91.767578125 234 | 7112932551.79 -35.354473114 -94.3734512329 235 | 7112932751.79 -35.3971214294 -96.8362045288 236 | 7112932951.79 -35.4430618286 -99.4056396484 237 | 7112933151.79 -35.476776123 -101.983596802 238 | 7112933351.79 -35.5099220276 -104.712265015 239 | 7112933551.79 -35.530166626 -107.081314087 240 | 7112933751.79 -35.5481872559 -109.7394104 241 | 7112933951.79 -35.5557594299 -112.536514282 242 | 7112934151.79 -35.5540008545 -115.205032349 243 | 7112934351.79 -35.5517158508 -118.057945251 244 | 7112934551.79 -35.5384597778 -120.509887695 245 | 7112934751.79 -35.5222091675 -123.022529602 246 | 7112934951.79 -35.4969024658 -125.591384888 247 | 7112935151.79 -35.4675750732 -128.331680298 248 | 7112935351.79 -35.4320793152 -130.703582764 249 | 7112935551.79 -35.3930549622 -133.249710083 250 | 7112935751.79 -35.338924408 -135.628738403 251 | 7112935951.79 -35.2889137268 -138.098434448 252 | 7112936151.79 -35.2266120911 -140.532104492 253 | 7112936351.79 -35.1740951538 -142.719711304 254 | 7112936551.79 -35.1016960144 -145.238311768 255 | 7112936751.79 -35.0282936096 -147.639633179 256 | 7112936951.79 -34.9553565979 -149.752853394 257 | 7112937151.79 -34.8829994202 -152.167602539 258 | 7112937351.79 -34.8024520874 -154.365356445 259 | 7112937551.79 -34.7155532837 -156.601791382 260 | 7112937751.79 -34.6332168579 -158.659408569 261 | 7112937951.79 -34.5460090637 -160.783325195 262 | 7112938151.79 -34.4520263672 -162.958175659 263 | 7112938351.79 -34.3624534607 -164.960891724 264 | 7112938551.79 -34.2794685364 -166.985214233 265 | 7112938751.79 -34.1870193481 -168.800354004 266 | 7112938951.79 -34.0872077942 -170.755645752 267 | 7112939151.79 -34.0014419556 -172.477767944 268 | 7112939351.79 -33.9034843445 -174.525054932 269 | 7112939551.79 -33.8182525635 -176.175720215 270 | 7112939751.79 -33.7129402161 -178.052658081 271 | 7112939951.79 -33.6330032349 -179.584976196 272 | 7112940151.79 -33.5493659973 178.867004395 273 | 7112940351.79 -33.4641304016 177.321472168 274 | 7112940551.79 -33.3733634949 175.664611816 275 | 7112940751.79 -33.2829742432 174.079620361 276 | 7112940951.79 -33.2026252747 172.715698242 277 | 7112941151.79 -33.1108818054 171.152160645 278 | 7112941351.79 -33.0248184204 169.702438354 279 | 7112941551.79 -32.9448661804 168.314956665 280 | 7112941751.79 -32.8555717468 166.826248169 281 | 7112941951.79 -32.7840194702 165.6222229 282 | 7112942151.79 -32.6991271973 164.20362854 283 | 7112942351.79 -32.6267280579 162.983413696 284 | 7112942551.79 -32.5468482971 161.676559448 285 | 7112942751.79 -32.4721450806 160.401489258 286 | 7112942951.79 -32.3967971802 159.129776001 287 | 7112943151.79 -32.3263931274 158.013076782 288 | 7112943351.79 -32.2536430359 156.816375732 289 | 7112943551.79 -32.1832809448 155.651916504 290 | 7112943751.79 -32.1221656799 154.591781616 291 | 7112943951.79 -32.0533752441 153.483551025 292 | 7112944151.79 -31.9818096161 152.325286865 293 | 7112944351.79 -31.9196796417 151.336471558 294 | 7112944551.79 -31.8532066345 150.249511719 295 | 7112944751.79 -31.7993202209 149.328384399 296 | 7112944951.79 -31.7368488312 148.310516357 297 | 7112945151.79 -31.6748104095 147.258712769 298 | 7112945351.79 -31.6172161102 146.302246094 299 | 7112945551.79 -31.5684185028 145.506484985 300 | 7112945751.79 -31.5048503876 144.471557617 301 | 7112945951.79 -31.4497699738 143.544723511 302 | 7112946151.79 -31.3979454041 142.652526855 303 | 7112946351.79 -31.3529453278 141.872787476 304 | 7112946551.79 -31.2977485657 140.95111084 305 | 7112946751.79 -31.2492275238 140.17137146 306 | 7112946951.79 -31.2056732178 139.383224487 307 | 7112947151.79 -31.1584758759 138.552856445 308 | 7112947351.79 -31.1131496429 137.804168701 309 | 7112947551.79 -31.0691814423 137.027282715 310 | 7112947751.79 -31.0244541168 136.255279541 311 | 7112947951.79 -30.9798908234 135.480041504 312 | 7112948151.79 -30.9421787262 134.793243408 313 | 7112948351.79 -30.8987483978 134.041702271 314 | 7112948551.79 -30.8584671021 133.338607788 315 | 7112948751.79 -30.8216533661 132.661376953 316 | 7112948951.79 -30.7861328125 131.989776611 317 | 7112949151.79 -30.7465438843 131.274887085 318 | 7112949351.79 -30.7089862823 130.622650146 319 | 7112949551.79 -30.6722068787 129.933578491 320 | 7112949751.79 -30.640499115 129.336761475 321 | 7112949951.79 -30.6060428619 128.718688965 322 | 7112950151.79 -30.5748462677 128.107513428 323 | 7112950351.79 -30.5430850983 127.524238586 324 | 7112950551.79 -30.5122318268 126.948028564 325 | 7112950751.79 -30.4828643799 126.354553223 326 | 7112950951.79 -30.4553337097 125.842216492 327 | 7112951151.79 -30.4205951691 125.204673767 328 | 7112951351.79 -30.3943500519 124.677429199 329 | 7112951551.79 -30.3673591614 124.127189636 330 | 7112951751.79 -30.3358020782 123.539993286 331 | 7112951951.79 -30.3113803864 123.033332825 332 | 7112952151.79 -30.2865943909 122.554969788 333 | 7112952351.79 -30.2561340332 121.979286194 334 | 7112952551.79 -30.2325611115 121.474884033 335 | 7112952751.79 -30.2070503235 120.951858521 336 | 7112952951.79 -30.1823558807 120.461898804 337 | 7112953151.79 -30.1591205597 119.988265991 338 | 7112953351.79 -30.1364135742 119.527740479 339 | 7112953551.79 -30.1154403687 119.103790283 340 | 7112953751.79 -30.092918396 118.645195007 341 | 7112953951.79 -30.0740394592 118.202514648 342 | 7112954151.79 -30.0506305695 117.736694336 343 | 7112954351.79 -30.0288848877 117.308036804 344 | 7112954551.79 -30.0097751617 116.870368958 345 | 7112954751.79 -29.9900131226 116.465042114 346 | 7112954951.79 -29.9710769653 116.023330688 347 | 7112955151.79 -29.9527664185 115.641098022 348 | 7112955351.79 -29.9347457886 115.230293274 349 | 7112955551.79 -29.9151096344 114.783607483 350 | 7112955751.79 -29.8980789185 114.365966797 351 | 7112955951.79 -29.8792762756 113.969696045 352 | 7112956151.79 -29.8629131317 113.609535217 353 | 7112956351.79 -29.845741272 113.224334717 354 | 7112956551.79 -29.8300285339 112.832984924 355 | 7112956751.79 -29.8144760132 112.469009399 356 | 7112956951.79 -29.7985687256 112.11681366 357 | 7112957151.79 -29.7815341949 111.726341248 358 | 7112957351.79 -29.7651786804 111.347503662 359 | 7112957551.79 -29.7504177094 111.037971497 360 | 7112957751.79 -29.737405777 110.697219849 361 | 7112957951.79 -29.7211151123 110.35735321 362 | 7112958151.79 -29.7078323364 110.023330688 363 | 7112958351.79 -29.6969413757 109.721611023 364 | 7112958551.79 -29.683921814 109.410339355 365 | 7112958751.79 -29.6686267853 109.068977356 366 | 7112958951.79 -29.6563796997 108.746902466 367 | 7112959151.79 -29.6432304382 108.403335571 368 | 7112959351.79 -29.631652832 108.138656616 369 | 7112959551.79 -29.6204032898 107.823356628 370 | 7112959751.79 -29.6069107056 107.5184021 371 | 7112959951.79 -29.5953388214 107.201972961 372 | 7112960151.79 -29.5831317902 106.917694092 373 | 7112960351.79 -29.5739021301 106.638137817 374 | 7112960551.79 -29.5603084564 106.328033447 375 | 7112960751.79 -29.5516147614 106.050346375 376 | 7112960951.79 -29.5401935577 105.768669128 377 | 7112961151.79 -29.5289783478 105.47467041 378 | 7112961351.79 -29.5183124542 105.217750549 379 | 7112961551.79 -29.5076732635 104.963874817 380 | 7112961751.79 -29.4981689453 104.685142517 381 | 7112961951.79 -29.4886188507 104.417533875 382 | 7112962151.79 -29.4794845581 104.168746948 383 | 7112962351.79 -29.4704017639 103.924591064 384 | 7112962551.79 -29.4610939026 103.660041809 385 | 7112962751.79 -29.4517192841 103.416282654 386 | 7112962951.79 -29.4432506561 103.172599792 387 | 7112963151.79 -29.4336585999 102.915756226 388 | 7112963351.79 -29.4238929749 102.665168762 389 | 7112963551.79 -29.4152946472 102.410377502 390 | 7112963751.79 -29.407497406 102.177383423 391 | 7112963951.79 -29.3989219666 101.935676575 392 | 7112964151.79 -29.3931102753 101.713623047 393 | 7112964351.79 -29.3840446472 101.508033752 394 | 7112964551.79 -29.3753509521 101.274932861 395 | 7112964751.79 -29.3675117493 101.065917969 396 | 7112964951.79 -29.3609313965 100.841255188 397 | 7112965151.79 -29.3530654907 100.618225098 398 | 7112965351.79 -29.3464336395 100.397903442 399 | 7112965551.79 -29.3387184143 100.192245483 400 | 7112965751.79 -29.3313159943 99.9787826538 401 | 7112965951.79 -29.3240680695 99.7603530884 402 | 7112966151.79 -29.3187103271 99.5674057007 403 | 7112966351.79 -29.3104038239 99.3256835938 404 | 7112966551.79 -29.3049411774 99.1575241089 405 | 7112966751.79 -29.2975254059 98.9616088867 406 | 7112966951.79 -29.29180336 98.7493591309 407 | 7112967151.79 -29.284986496 98.5568237305 408 | 7112967351.79 -29.2794017792 98.3647232056 409 | 7112967551.79 -29.2726421356 98.1539993286 410 | 7112967751.79 -29.2661838531 97.9649734497 411 | 7112967951.79 -29.2600288391 97.7969894409 412 | 7112968151.79 -29.2558269501 97.6447143555 413 | 7112968351.79 -29.2499752045 97.4524459839 414 | 7112968551.79 -29.2453022003 97.2811050415 415 | 7112968751.79 -29.2385616302 97.0701599121 416 | 7112968951.79 -29.2327194214 96.8921356201 417 | 7112969151.79 -29.2274570465 96.7140884399 418 | 7112969351.79 -29.2226772308 96.5478897095 419 | 7112969551.79 -29.2166023254 96.3553161621 420 | 7112969751.79 -29.2117080688 96.1945800781 421 | 7112969951.79 -29.2060184479 96.0274429321 422 | 7112970151.79 -29.2024688721 95.872505188 423 | 7112970351.79 -29.1971282959 95.7041854858 424 | 7112970551.79 -29.191116333 95.5210342407 425 | 7112970751.79 -29.1867809296 95.3535003662 426 | 7112970951.79 -29.181016922 95.2219772339 427 | 7112971151.79 -29.1773490906 95.0492172241 428 | 7112971351.79 -29.172296524 94.8775863647 429 | 7112971551.79 -29.1677970886 94.7388916016 430 | 7112971751.79 -29.1637401581 94.5770111084 431 | 7112971951.79 -29.1579742432 94.4201202393 432 | 7112972151.79 -29.1546440125 94.2627716064 433 | 7112972351.79 -29.150636673 94.1110305786 434 | 7112972551.79 -29.1462879181 93.9831466675 435 | 7112972751.79 -29.1428947449 93.8402252197 436 | 7112972951.79 -29.1397857666 93.685874939 437 | 7112973151.79 -29.1339092255 93.5384674072 438 | 7112973351.79 -29.1303596497 93.3998794556 439 | 7112973551.79 -29.1263179779 93.2400360107 440 | 7112973751.79 -29.1232681274 93.1196136475 441 | 7112973951.79 -29.1195087433 92.9893112183 442 | 7112974151.79 -29.1146831512 92.841293335 443 | 7112974351.79 -29.1118297577 92.7020797729 444 | 7112974551.79 -29.1077423096 92.5562667847 445 | 7112974751.79 -29.1055641174 92.4065170288 446 | 7112974951.79 -29.1019954681 92.3015365601 447 | 7112975151.79 -29.0970172882 92.1622695923 448 | 7112975351.79 -29.0922546387 92.0345611572 449 | 7112975551.79 -29.0895709991 91.9001235962 450 | 7112975751.79 -29.0866203308 91.7699661255 451 | 7112975951.79 -29.0832939148 91.6366043091 452 | 7112976151.79 -29.0810985565 91.5446090698 453 | 7112976351.79 -29.077703476 91.4200439453 454 | 7112976551.79 -29.0735569 91.2852020264 455 | 7112976751.79 -29.0710048676 91.1828765869 456 | 7112976951.79 -29.0680656433 91.0568313599 457 | 7112977151.79 -29.0649929047 90.9418869019 458 | 7112977351.79 -29.0614242554 90.8108520508 459 | 7112977551.79 -29.0575656891 90.6791610718 460 | 7112977751.79 -29.0541229248 90.548248291 461 | 7112977951.79 -29.05210495 90.4298095703 462 | 7112978151.79 -29.0500869751 90.3356704712 463 | 7112978351.79 -29.0456295013 90.2152786255 464 | 7112978551.79 -29.0414524078 90.0981826782 465 | 7112978751.79 -29.0398960114 89.9874420166 466 | 7112978951.79 -29.0376052856 89.8669586182 467 | 7112979151.79 -29.0351257324 89.7706069946 468 | 7112979351.79 -29.0314216614 89.6592178345 469 | 7112979551.79 -29.0295543671 89.5618286133 470 | 7112979751.79 -29.0273132324 89.4590301514 471 | 7112979951.79 -29.0245571136 89.3369827271 472 | 7112980151.79 -29.022108078 89.2261352539 473 | 7112980351.79 -29.0186595917 89.1377563477 474 | 7112980551.79 -29.0174312592 89.0398101807 475 | 7112980751.79 -29.0136165619 88.9458084106 476 | 7112980951.79 -29.0119419098 88.8277282715 477 | 7112981151.79 -29.010591507 88.7223358154 478 | 7112981351.79 -29.0085849762 88.625793457 479 | 7112981551.79 -29.0049781799 88.524810791 480 | 7112981751.79 -29.0038719177 88.4309539795 481 | 7112981951.79 -28.9999523163 88.3362426758 482 | 7112982151.79 -28.9971370697 88.2193069458 483 | 7112982351.79 -28.9951400757 88.1351470947 484 | 7112982551.79 -28.9943256378 88.0488128662 485 | 7112982751.79 -28.9916267395 87.9335403442 486 | 7112982951.79 -28.990032196 87.8511962891 487 | 7112983151.79 -28.9867687225 87.747833252 488 | 7112983351.79 -28.9849834442 87.6607208252 489 | 7112983551.79 -28.9839553833 87.5676727295 490 | 7112983751.79 -28.9811935425 87.4755630493 491 | 7112983951.79 -28.9777202606 87.3772888184 492 | 7112984151.79 -28.9758930206 87.2980117798 493 | 7112984351.79 -28.9738864899 87.2104949951 494 | 7112984551.79 -28.9727306366 87.1030654907 495 | 7112984751.79 -28.9708786011 87.0149078369 496 | 7112984951.79 -28.9685821533 86.9465332031 497 | 7112985151.79 -28.9666900635 86.845489502 498 | 7112985351.79 -28.9654083252 86.7677001953 499 | 7112985551.79 -28.9634037018 86.6886062622 500 | 7112985751.79 -28.9617195129 86.5867996216 501 | 7112985951.79 -28.9602642059 86.5100860596 502 | 7112986151.79 -28.9576091766 86.4353103638 503 | -------------------------------------------------------------------------------- /examples/notch_port_example.py: -------------------------------------------------------------------------------- 1 | 2 | from resonator_tools import circuit 3 | 4 | 5 | port1 = circuit.notch_port() 6 | port1.add_froms2p('S21testdata.s2p',3,4,'realimag',fdata_unit=1e9,delimiter=None) 7 | port1.autofit() 8 | print("Fit results:", port1.fitresults) 9 | port1.plotall() 10 | print("single photon limit:", port1.get_single_photon_limit(diacorr=True), "dBm") 11 | print("photons in reso for input -140dBm:", port1.get_photons_in_resonator(-140,unit='dBm',diacorr=True), "photons") 12 | print("done") 13 | -------------------------------------------------------------------------------- /examples/notch_port_example_withGUI.py: -------------------------------------------------------------------------------- 1 | 2 | from resonator_tools import circuit 3 | 4 | 5 | port1 = circuit.notch_port() 6 | port1.add_froms2p('S21testdata.s2p',3,4,'realimag',fdata_unit=1e9,delimiter=None) 7 | port1.GUIfit() 8 | print("Fit results:", port1.fitresults) 9 | port1.plotall() 10 | print("single photon limit:", port1.get_single_photon_limit(diacorr=True), "dBm") 11 | print("photons in reso for input -140dBm:", port1.get_photons_in_resonator(-140,unit='dBm',diacorr=True), "photons") 12 | print("done") 13 | -------------------------------------------------------------------------------- /examples/pandas_and_circlefit.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:25d58d0ad878b5b3eb3c04f9ab73c1310b0ec623d81be671993f72be36d8a557" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "Fitting a resonator measured in reflection" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "collapsed": false, 22 | "input": [ 23 | "from resonator_tools import circuit\n", 24 | "import numpy as np\n", 25 | "import pandas as pd\n", 26 | "from IPython.display import display\n", 27 | "%matplotlib inline" 28 | ], 29 | "language": "python", 30 | "metadata": {}, 31 | "outputs": [], 32 | "prompt_number": 1 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "Although we could use the resonator tools to load data, here we want to use the Pandas library, which is used for statistical data analysis. It can handle many different file types including hdf5." 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "collapsed": false, 44 | "input": [ 45 | "df = pd.read_csv('S11.txt',sep='\\t')" 46 | ], 47 | "language": "python", 48 | "metadata": {}, 49 | "outputs": [], 50 | "prompt_number": 2 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "Pandas has a very nice way of displaying the data. Let's look at the first few entries:" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "collapsed": false, 62 | "input": [ 63 | "display(df.head())" 64 | ], 65 | "language": "python", 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "html": [ 70 | "
\n", 71 | "\n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | "
freqmagphase
0 7.112886e+09-29.041420 40.656170
1 7.112886e+09-29.042747 40.569664
2 7.112887e+09-29.045673 40.464161
3 7.112887e+09-29.049311 40.361294
4 7.112887e+09-29.051012 40.265739
\n", 113 | "
" 114 | ], 115 | "metadata": {}, 116 | "output_type": "display_data", 117 | "text": [ 118 | " freq mag phase\n", 119 | "0 7.112886e+09 -29.041420 40.656170\n", 120 | "1 7.112886e+09 -29.042747 40.569664\n", 121 | "2 7.112887e+09 -29.045673 40.464161\n", 122 | "3 7.112887e+09 -29.049311 40.361294\n", 123 | "4 7.112887e+09 -29.051012 40.265739" 124 | ] 125 | } 126 | ], 127 | "prompt_number": 3 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "Next, we define a reflection port measurement and add the data." 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "collapsed": false, 139 | "input": [ 140 | "port1 = circuit.reflection_port(f_data=df[\"freq\"].values,\n", 141 | " z_data_raw=10**(df[\"mag\"].values/20.)*np.exp(1j*df[\"phase\"].values/180.*np.pi))" 142 | ], 143 | "language": "python", 144 | "metadata": {}, 145 | "outputs": [], 146 | "prompt_number": 4 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Perform an automated fit." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "collapsed": false, 158 | "input": [ 159 | "port1.autofit()" 160 | ], 161 | "language": "python", 162 | "metadata": {}, 163 | "outputs": [], 164 | "prompt_number": 5 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "Let's plot the data and the fit!" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "collapsed": false, 176 | "input": [ 177 | "port1.plotall()" 178 | ], 179 | "language": "python", 180 | "metadata": {}, 181 | "outputs": [ 182 | { 183 | "metadata": {}, 184 | "output_type": "display_data", 185 | "png": "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\n", 186 | "text": [ 187 | "" 188 | ] 189 | } 190 | ], 191 | "prompt_number": 6 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "Next, let us have a look at the fit results. Here, we convert the dictionary of results into a dataframe to display it in a nicer way." 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "collapsed": false, 203 | "input": [ 204 | "display(pd.DataFrame([port1.fitresults]).applymap(lambda x: \"{0:.2e}\".format(x)))" 205 | ], 206 | "language": "python", 207 | "metadata": {}, 208 | "outputs": [ 209 | { 210 | "html": [ 211 | "
\n", 212 | "\n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | "
QcQc_errQiQi_errQlQl_errchi_squarefrfr_errtheta0
0 3.48e+05 2.46e+02 9.30e+05 1.74e+03 2.53e+05 1.96e+02 2.87e-05 7.11e+09 7.19e+00 -4.18e-03
\n", 244 | "
" 245 | ], 246 | "metadata": {}, 247 | "output_type": "display_data", 248 | "text": [ 249 | " Qc Qc_err Qi Qi_err Ql Ql_err chi_square \\\n", 250 | "0 3.48e+05 2.46e+02 9.30e+05 1.74e+03 2.53e+05 1.96e+02 2.87e-05 \n", 251 | "\n", 252 | " fr fr_err theta0 \n", 253 | "0 7.11e+09 7.19e+00 -4.18e-03 " 254 | ] 255 | } 256 | ], 257 | "prompt_number": 7 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "Finally, we can calculate the single photon limit, i.e., the input power necessary to maintain one photon on average in the resonator:" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "collapsed": false, 269 | "input": [ 270 | "print 'Single photon limit: %.2f dBm' % port1.get_single_photon_limit()" 271 | ], 272 | "language": "python", 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "output_type": "stream", 277 | "stream": "stdout", 278 | "text": [ 279 | "Single photon limit: -163.42 dBm\n" 280 | ] 281 | } 282 | ], 283 | "prompt_number": 8 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "Or, we can compute the photons in the resonator for a given power:" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "collapsed": false, 295 | "input": [ 296 | "print 'At -100dBm, we have %.2e photons in the resonator' % port1.get_photons_in_resonator(-100)" 297 | ], 298 | "language": "python", 299 | "metadata": {}, 300 | "outputs": [ 301 | { 302 | "output_type": "stream", 303 | "stream": "stdout", 304 | "text": [ 305 | "At -100dBm, we have 2.20e+06 photons in the resonator\n" 306 | ] 307 | } 308 | ], 309 | "prompt_number": 9 310 | } 311 | ], 312 | "metadata": {} 313 | } 314 | ] 315 | } -------------------------------------------------------------------------------- /examples/reflection_port_example.py: -------------------------------------------------------------------------------- 1 | 2 | from resonator_tools import circuit 3 | 4 | 5 | port1 = circuit.reflection_port() 6 | port1.add_fromtxt('S11.txt','dBmagphasedeg',1) 7 | port1.autofit() 8 | print("Fit results:", port1.fitresults) 9 | port1.plotall() 10 | print("single photon limit:", port1.get_single_photon_limit(), "dBm") 11 | print("done") 12 | -------------------------------------------------------------------------------- /examples/reflection_port_example_withGUI.py: -------------------------------------------------------------------------------- 1 | 2 | from resonator_tools import circuit 3 | 4 | 5 | port1 = circuit.reflection_port() 6 | port1.add_fromtxt('S11.txt','dBmagphasedeg',1) 7 | port1.GUIfit() 8 | print("Fit results:", port1.fitresults) 9 | port1.plotall() 10 | print("single photon limit:", port1.get_single_photon_limit(), "dBm") 11 | print("done") 12 | -------------------------------------------------------------------------------- /examples/remove_wiggly_baseline_with GUI.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from resonator_tools import circuit 4 | 5 | #--------------------------------------- 6 | #generate test data 7 | fr = 7e9 #resonance frequency in Hz 8 | Qi = 200e3 9 | Qc = 300e3 10 | freq = np.linspace(fr-5e6, fr+5e6, 5000) 11 | port1 = circuit.reflection_port() #define a reflection port 12 | #add noise 13 | noise = np.random.normal(loc=1.0,scale=0.01,size=(len(freq),)) 14 | S11 = noise * port1._S11_directrefl(freq,fr=fr,Ql=Qi*Qc/(Qc+Qi),Qc=Qc,a=1.,alpha=0.,delay=.0) 15 | # add wiggly baseline 16 | baseline = 1.+0.1*np.cos(2.*np.pi*0.001e-4*freq)+0.05*np.sin(2.*np.pi*0.0069e-4*freq)+0.001*np.sin(2.*np.pi*0.0001e-4*freq) 17 | S11b = S11*baseline 18 | #----------------------------------------- 19 | 20 | # create fitting object 21 | port1 = circuit.reflection_port() 22 | port1.add_data(freq,S11b) 23 | 24 | # fit and remove base line 25 | port1.GUIbaselinefit() 26 | 27 | # fit the corrected data 28 | port1.GUIfit() 29 | print("Fit results:", port1.fitresults) 30 | port1.plotall() 31 | print("single photon limit:", port1.get_single_photon_limit(), "dBm") 32 | print("done") 33 | 34 | -------------------------------------------------------------------------------- /old_discontinued_circlefit/Circlefit_V3.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastianprobst/resonator_tools/bbd6bca255eb7ebdf550fde9f6c526a7d0791eac/old_discontinued_circlefit/Circlefit_V3.1.zip -------------------------------------------------------------------------------- /resonator_tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastianprobst/resonator_tools/bbd6bca255eb7ebdf550fde9f6c526a7d0791eac/resonator_tools/__init__.py -------------------------------------------------------------------------------- /resonator_tools/calibration.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | from scipy import sparse 4 | from scipy.interpolate import interp1d 5 | 6 | class calibration(object): 7 | ''' 8 | some useful tools for manual calibration 9 | ''' 10 | def normalize_zdata(self,z_data,cal_z_data): 11 | return z_data/cal_z_data 12 | 13 | def normalize_amplitude(self,z_data,cal_ampdata): 14 | return z_data/cal_ampdata 15 | 16 | def normalize_phase(self,z_data,cal_phase): 17 | return z_data*np.exp(-1j*cal_phase) 18 | 19 | def normalize_by_func(self,f_data,z_data,func): 20 | return z_data/func(f_data) 21 | 22 | def _baseline_als(self,y, lam, p, niter=10): 23 | ''' 24 | see http://zanran_storage.s3.amazonaws.com/www.science.uva.nl/ContentPages/443199618.pdf 25 | "Asymmetric Least Squares Smoothing" by P. Eilers and H. Boelens in 2005. 26 | http://stackoverflow.com/questions/29156532/python-baseline-correction-library 27 | "There are two parameters: p for asymmetry and lambda for smoothness. Both have to be 28 | tuned to the data at hand. We found that generally 0.001<=p<=0.1 is a good choice 29 | (for a signal with positive peaks) and 10e2<=lambda<=10e9, but exceptions may occur." 30 | ''' 31 | L = len(y) 32 | D = sparse.csc_matrix(np.diff(np.eye(L), 2)) 33 | w = np.ones(L) 34 | for i in range(niter): 35 | W = sparse.spdiags(w, 0, L, L) 36 | Z = W + lam * D.dot(D.transpose()) 37 | z = sparse.linalg.spsolve(Z, w*y) 38 | w = p * (y > z) + (1-p) * (y < z) 39 | return z 40 | 41 | def fit_baseline_amp(self,z_data,lam,p,niter=10): 42 | ''' 43 | for this to work, you need to analyze a large part of the baseline 44 | tune lam and p until you get the desired result 45 | ''' 46 | return self._baseline_als(np.absolute(z_data),lam,p,niter=niter) 47 | 48 | def baseline_func_amp(self,z_data,f_data,lam,p,niter=10): 49 | ''' 50 | for this to work, you need to analyze a large part of the baseline 51 | tune lam and p until you get the desired result 52 | returns the baseline as a function 53 | the points in between the datapoints are computed by cubic interpolation 54 | ''' 55 | return interp1d(f_data, self._baseline_als(np.absolute(z_data),lam,p,niter=niter), kind='cubic') 56 | 57 | def baseline_func_phase(self,z_data,f_data,lam,p,niter=10): 58 | ''' 59 | for this to work, you need to analyze a large part of the baseline 60 | tune lam and p until you get the desired result 61 | returns the baseline as a function 62 | the points in between the datapoints are computed by cubic interpolation 63 | ''' 64 | return interp1d(f_data, self._baseline_als(np.angle(z_data),lam,p,niter=niter), kind='cubic') 65 | 66 | def fit_baseline_phase(self,z_data,lam,p,niter=10): 67 | ''' 68 | for this to work, you need to analyze a large part of the baseline 69 | tune lam and p until you get the desired result 70 | ''' 71 | return self._baseline_als(np.angle(z_data),lam,p,niter=niter) 72 | 73 | def GUIbaselinefit(self): 74 | ''' 75 | A GUI to help you fit the baseline 76 | ''' 77 | self.__lam = 1e6 78 | self.__p = 0.9 79 | niter = 10 80 | self.__baseline = self._baseline_als(np.absolute(self.z_data_raw),self.__lam,self.__p,niter=niter) 81 | import matplotlib.pyplot as plt 82 | from matplotlib.widgets import Slider 83 | fig, (ax0,ax1) = plt.subplots(nrows=2) 84 | plt.suptitle('Use the sliders to make the green curve match the baseline.') 85 | plt.subplots_adjust(left=0.25, bottom=0.25) 86 | l0, = ax0.plot(np.absolute(self.z_data_raw)) 87 | l0b, = ax0.plot(np.absolute(self.__baseline)) 88 | l1, = ax1.plot(np.absolute(self.z_data_raw/self.__baseline)) 89 | ax0.set_ylabel('amp, rawdata vs. baseline') 90 | ax1.set_ylabel('amp, corrected') 91 | axcolor = 'lightgoldenrodyellow' 92 | axSmooth = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor) 93 | axAsym = plt.axes([0.25, 0.15, 0.65, 0.03], axisbg=axcolor) 94 | axbcorr = plt.axes([0.25, 0.05, 0.65, 0.03], axisbg=axcolor) 95 | sSmooth = Slider(axSmooth, 'Smoothness', 0.1, 10., valinit=np.log10(self.__lam),valfmt='1E%f') 96 | sAsym = Slider(axAsym, 'Asymmetry', 1e-4,0.99999, valinit=self.__p,valfmt='%f') 97 | sbcorr = Slider(axbcorr, 'vertical shift',0.7,1.1,valinit=1.) 98 | def update(val): 99 | self.__lam = 10**sSmooth.val 100 | self.__p = sAsym.val 101 | self.__baseline = sbcorr.val*self._baseline_als(np.absolute(self.z_data_raw),self.__lam,self.__p,niter=niter) 102 | l0.set_ydata(np.absolute(self.z_data_raw)) 103 | l0b.set_ydata(np.absolute(self.__baseline)) 104 | l1.set_ydata(np.absolute(self.z_data_raw/self.__baseline)) 105 | fig.canvas.draw_idle() 106 | sSmooth.on_changed(update) 107 | sAsym.on_changed(update) 108 | sbcorr.on_changed(update) 109 | plt.show() 110 | self.z_data_raw /= self.__baseline 111 | plt.close() 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /resonator_tools/circlefit.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import scipy.optimize as spopt 4 | from scipy import stats 5 | 6 | 7 | class circlefit(object): 8 | ''' 9 | contains all the circlefit procedures 10 | see http://scitation.aip.org/content/aip/journal/rsi/86/2/10.1063/1.4907935 11 | arxiv version: http://arxiv.org/abs/1410.3365 12 | ''' 13 | def _remove_cable_delay(self,f_data,z_data, delay): 14 | return z_data/np.exp(2j*np.pi*f_data*delay) 15 | 16 | def _center(self,z_data,zc): 17 | return z_data-zc 18 | 19 | def _dist(self,x): 20 | np.absolute(x,x) 21 | c = (x > np.pi).astype(int) 22 | return x+c*(-2.*x+2.*np.pi) 23 | 24 | def _periodic_boundary(self,x,bound): 25 | return np.fmod(x,bound)-np.trunc(x/bound)*bound 26 | 27 | def _phase_fit_wslope(self,f_data,z_data,theta0, Ql, fr, slope): 28 | phase = np.angle(z_data) 29 | def residuals(p,x,y): 30 | theta0, Ql, fr, slope = p 31 | err = self._dist(y - (theta0+2.*np.arctan(2.*Ql*(1.-x/fr))-slope*x)) 32 | return err 33 | p0 = [theta0, Ql, fr, slope] 34 | p_final = spopt.leastsq(residuals,p0,args=(np.array(f_data),np.array(phase))) 35 | return p_final[0] 36 | 37 | def _phase_fit(self,f_data,z_data,theta0, Ql, fr): 38 | phase = np.angle(z_data) 39 | def residuals_1(p,x,y,Ql): 40 | theta0, fr = p 41 | err = self._dist(y - (theta0+2.*np.arctan(2.*Ql*(1.-x/fr)))) 42 | return err 43 | def residuals_2(p,x,y,theta0): 44 | Ql, fr = p 45 | err = self._dist(y - (theta0+2.*np.arctan(2.*Ql*(1.-x/fr)))) 46 | return err 47 | def residuals_3(p,x,y,theta0,Ql): 48 | fr = p 49 | err = self._dist(y - (theta0+2.*np.arctan(2.*Ql*(1.-x/fr)))) 50 | return err 51 | def residuals_4(p,x,y,theta0,fr): 52 | Ql = p 53 | err = self._dist(y - (theta0+2.*np.arctan(2.*Ql*(1.-x/fr)))) 54 | return err 55 | def residuals_5(p,x,y): 56 | theta0, Ql, fr = p 57 | err = self._dist(y - (theta0+2.*np.arctan(2.*Ql*(1.-x/fr)))) 58 | return err 59 | p0 = [theta0, fr] 60 | p_final = spopt.leastsq(lambda a,b,c: residuals_1(a,b,c,Ql),p0,args=(f_data,phase))#,ftol=1e-12,xtol=1e-12) 61 | theta0, fr = p_final[0] 62 | p0 = [Ql, fr] 63 | p_final = spopt.leastsq(lambda a,b,c: residuals_2(a,b,c,theta0),p0,args=(f_data,phase))#,ftol=1e-12,xtol=1e-12) 64 | Ql, fr = p_final[0] 65 | p0 = fr 66 | p_final = spopt.leastsq(lambda a,b,c: residuals_3(a,b,c,theta0,Ql),p0,args=(f_data,phase))#,ftol=1e-12,xtol=1e-12) 67 | fr = p_final[0][0] 68 | p0 = Ql 69 | p_final = spopt.leastsq(lambda a,b,c: residuals_4(a,b,c,theta0,fr),p0,args=(f_data,phase))#,ftol=1e-12,xtol=1e-12) 70 | Ql = p_final[0][0] 71 | p0 = np.array([theta0, Ql, fr], dtype='float64') 72 | p_final = spopt.leastsq(residuals_5,p0,args=(f_data,phase)) 73 | return p_final[0] 74 | 75 | def _fit_skewed_lorentzian(self,f_data,z_data): 76 | amplitude = np.absolute(z_data) 77 | amplitude_sqr = amplitude**2 78 | A1a = np.minimum(amplitude_sqr[0],amplitude_sqr[-1]) 79 | A3a = -np.max(amplitude_sqr) 80 | fra = f_data[np.argmin(amplitude_sqr)] 81 | def residuals(p,x,y): 82 | A2, A4, Ql = p 83 | err = y -(A1a+A2*(x-fra)+(A3a+A4*(x-fra))/(1.+4.*Ql**2*((x-fra)/fra)**2)) 84 | return err 85 | p0 = [0., 0., 1e3] 86 | p_final = spopt.leastsq(residuals,p0,args=(np.array(f_data),np.array(amplitude_sqr))) 87 | A2a, A4a, Qla = p_final[0] 88 | 89 | def residuals2(p,x,y): 90 | A1, A2, A3, A4, fr, Ql = p 91 | err = y -(A1+A2*(x-fr)+(A3+A4*(x-fr))/(1.+4.*Ql**2*((x-fr)/fr)**2)) 92 | return err 93 | def fitfunc(x,A1, A2, A3, A4, fr, Ql): 94 | return A1+A2*(x-fr)+(A3+A4*(x-fr))/(1.+4.*Ql**2*((x-fr)/fr)**2) 95 | 96 | p0 = [A1a, A2a , A3a, A4a, fra, Qla] 97 | #p_final = spopt.leastsq(residuals2,p0,args=(np.array(f_data),np.array(amplitude_sqr))) 98 | try: 99 | popt, pcov = spopt.curve_fit(fitfunc, np.array(f_data), np.array(amplitude_sqr),p0=p0) 100 | #A1, A2, A3, A4, fr, Ql = p_final[0] 101 | #print(p_final[0][5]) 102 | if pcov is not None: 103 | self.df_error = np.sqrt(pcov[4][4]) 104 | self.dQl_error = np.sqrt(pcov[5][5]) 105 | else: 106 | self.df_error = np.inf 107 | self.dQl_error = np.inf 108 | except: 109 | popt = p0 110 | self.df_error = np.inf 111 | self.dQl_error = np.inf 112 | #return p_final[0] 113 | return popt 114 | 115 | def _fit_circle(self,z_data, refine_results=False): 116 | def calc_moments(z_data): 117 | xi = z_data.real 118 | xi_sqr = xi*xi 119 | yi = z_data.imag 120 | yi_sqr = yi*yi 121 | zi = xi_sqr+yi_sqr 122 | Nd = float(len(xi)) 123 | xi_sum = xi.sum() 124 | yi_sum = yi.sum() 125 | zi_sum = zi.sum() 126 | xiyi_sum = (xi*yi).sum() 127 | xizi_sum = (xi*zi).sum() 128 | yizi_sum = (yi*zi).sum() 129 | return np.array([ [(zi*zi).sum(), xizi_sum, yizi_sum, zi_sum], \ 130 | [xizi_sum, xi_sqr.sum(), xiyi_sum, xi_sum], \ 131 | [yizi_sum, xiyi_sum, yi_sqr.sum(), yi_sum], \ 132 | [zi_sum, xi_sum, yi_sum, Nd] ]) 133 | 134 | M = calc_moments(z_data) 135 | 136 | a0 = ((M[2][0]*M[3][2]-M[2][2]*M[3][0])*M[1][1]-M[1][2]*M[2][0]*M[3][1]-M[1][0]*M[2][1]*M[3][2]+M[1][0]*M[2][2]*M[3][1]+M[1][2]*M[2][1]*M[3][0])*M[0][3]+(M[0][2]*M[2][3]*M[3][0]-M[0][2]*M[2][0]*M[3][3]+M[0][0]*M[2][2]*M[3][3]-M[0][0]*M[2][3]*M[3][2])*M[1][1]+(M[0][1]*M[1][3]*M[3][0]-M[0][1]*M[1][0]*M[3][3]-M[0][0]*M[1][3]*M[3][1])*M[2][2]+(-M[0][1]*M[1][2]*M[2][3]-M[0][2]*M[1][3]*M[2][1])*M[3][0]+((M[2][3]*M[3][1]-M[2][1]*M[3][3])*M[1][2]+M[2][1]*M[3][2]*M[1][3])*M[0][0]+(M[1][0]*M[2][3]*M[3][2]+M[2][0]*(M[1][2]*M[3][3]-M[1][3]*M[3][2]))*M[0][1]+((M[2][1]*M[3][3]-M[2][3]*M[3][1])*M[1][0]+M[1][3]*M[2][0]*M[3][1])*M[0][2] 137 | a1 = (((M[3][0]-2.*M[2][2])*M[1][1]-M[1][0]*M[3][1]+M[2][2]*M[3][0]+2.*M[1][2]*M[2][1]-M[2][0]*M[3][2])*M[0][3]+(2.*M[2][0]*M[3][2]-M[0][0]*M[3][3]-2.*M[2][2]*M[3][0]+2.*M[0][2]*M[2][3])*M[1][1]+(-M[0][0]*M[3][3]+2.*M[0][1]*M[1][3]+2.*M[1][0]*M[3][1])*M[2][2]+(-M[0][1]*M[1][3]+2.*M[1][2]*M[2][1]-M[0][2]*M[2][3])*M[3][0]+(M[1][3]*M[3][1]+M[2][3]*M[3][2])*M[0][0]+(M[1][0]*M[3][3]-2.*M[1][2]*M[2][3])*M[0][1]+(M[2][0]*M[3][3]-2.*M[1][3]*M[2][1])*M[0][2]-2.*M[1][2]*M[2][0]*M[3][1]-2.*M[1][0]*M[2][1]*M[3][2]) 138 | a2 = ((2.*M[1][1]-M[3][0]+2.*M[2][2])*M[0][3]+(2.*M[3][0]-4.*M[2][2])*M[1][1]-2.*M[2][0]*M[3][2]+2.*M[2][2]*M[3][0]+M[0][0]*M[3][3]+4.*M[1][2]*M[2][1]-2.*M[0][1]*M[1][3]-2.*M[1][0]*M[3][1]-2.*M[0][2]*M[2][3]) 139 | a3 = (-2.*M[3][0]+4.*M[1][1]+4.*M[2][2]-2.*M[0][3]) 140 | a4 = -4. 141 | 142 | def func(x): 143 | return a0+a1*x+a2*x*x+a3*x*x*x+a4*x*x*x*x 144 | 145 | def d_func(x): 146 | return a1+2*a2*x+3*a3*x*x+4*a4*x*x*x 147 | 148 | x0 = spopt.fsolve(func, 0., fprime=d_func) 149 | 150 | def solve_eq_sys(val,M): 151 | #prepare 152 | M[3][0] = M[3][0]+2*val 153 | M[0][3] = M[0][3]+2*val 154 | M[1][1] = M[1][1]-val 155 | M[2][2] = M[2][2]-val 156 | return np.linalg.svd(M) 157 | 158 | U,s,Vt = solve_eq_sys(x0[0],M) 159 | 160 | A_vec = Vt[np.argmin(s),:] 161 | 162 | xc = -A_vec[1]/(2.*A_vec[0]) 163 | yc = -A_vec[2]/(2.*A_vec[0]) 164 | # the term *sqrt term corrects for the constraint, because it may be altered due to numerical inaccuracies during calculation 165 | r0 = 1./(2.*np.absolute(A_vec[0]))*np.sqrt(A_vec[1]*A_vec[1]+A_vec[2]*A_vec[2]-4.*A_vec[0]*A_vec[3]) 166 | if refine_results: 167 | print("agebraic r0: " + str(r0)) 168 | xc,yc,r0 = self._fit_circle_iter(z_data, xc, yc, r0) 169 | r0 = self._fit_circle_iter_radialweight(z_data, xc, yc, r0) 170 | print("iterative r0: " + str(r0)) 171 | return xc, yc, r0 172 | 173 | def _guess_delay(self,f_data,z_data): 174 | phase2 = np.unwrap(np.angle(z_data)) 175 | gradient, intercept, r_value, p_value, std_err = stats.linregress(f_data,phase2) 176 | return gradient*(-1.)/(np.pi*2.) 177 | 178 | 179 | def _fit_delay(self,f_data,z_data,delay=0.,maxiter=0): 180 | def residuals(p,x,y): 181 | phasedelay = p 182 | z_data_temp = y*np.exp(1j*(2.*np.pi*phasedelay*x)) 183 | xc,yc,r0 = self._fit_circle(z_data_temp) 184 | err = np.sqrt((z_data_temp.real-xc)**2+(z_data_temp.imag-yc)**2)-r0 185 | return err 186 | p_final = spopt.leastsq(residuals,delay,args=(f_data,z_data),maxfev=maxiter,ftol=1e-12,xtol=1e-12) 187 | return p_final[0][0] 188 | 189 | def _fit_delay_alt_bigdata(self,f_data,z_data,delay=0.,maxiter=0): 190 | def residuals(p,x,y): 191 | phasedelay = p 192 | z_data_temp = 1j*2.*np.pi*phasedelay*x 193 | np.exp(z_data_temp,out=z_data_temp) 194 | np.multiply(y,z_data_temp,out=z_data_temp) 195 | #z_data_temp = y*np.exp(1j*(2.*np.pi*phasedelay*x)) 196 | xc,yc,r0 = self._fit_circle(z_data_temp) 197 | err = np.sqrt((z_data_temp.real-xc)**2+(z_data_temp.imag-yc)**2)-r0 198 | return err 199 | p_final = spopt.leastsq(residuals,delay,args=(f_data,z_data),maxfev=maxiter,ftol=1e-12,xtol=1e-12) 200 | return p_final[0][0] 201 | 202 | def _fit_entire_model(self,f_data,z_data,fr,absQc,Ql,phi0,delay,a=1.,alpha=0.,maxiter=0): 203 | ''' 204 | fits the whole model: a*exp(i*alpha)*exp(-2*pi*i*f*delay) * [ 1 - {Ql/Qc*exp(i*phi0)} / {1+2*i*Ql*(f-fr)/fr} ] 205 | ''' 206 | def funcsqr(p,x): 207 | fr,absQc,Ql,phi0,delay,a,alpha = p 208 | return np.array([np.absolute( ( a*np.exp(complex(0,alpha))*np.exp(complex(0,-2.*np.pi*delay*x[i])) * ( 1 - (Ql/absQc*np.exp(complex(0,phi0)))/(complex(1,2*Ql*(x[i]-fr)/fr)) ) ) )**2 for i in range(len(x))]) 209 | def residuals(p,x,y): 210 | fr,absQc,Ql,phi0,delay,a,alpha = p 211 | err = [np.absolute( y[i] - ( a*np.exp(complex(0,alpha))*np.exp(complex(0,-2.*np.pi*delay*x[i])) * ( 1 - (Ql/absQc*np.exp(complex(0,phi0)))/(complex(1,2*Ql*(x[i]-fr)/fr)) ) ) ) for i in range(len(x))] 212 | return err 213 | p0 = [fr,absQc,Ql,phi0,delay,a,alpha] 214 | (popt, params_cov, infodict, errmsg, ier) = spopt.leastsq(residuals,p0,args=(np.array(f_data),np.array(z_data)),full_output=True,maxfev=maxiter) 215 | len_ydata = len(np.array(f_data)) 216 | if (len_ydata > len(p0)) and params_cov is not None: #p_final[1] is cov_x data #this caculation is from scipy curve_fit routine - no idea if this works correctly... 217 | s_sq = (funcsqr(popt, np.array(f_data))).sum()/(len_ydata-len(p0)) 218 | params_cov = params_cov * s_sq 219 | else: 220 | params_cov = np.inf 221 | return popt, params_cov, infodict, errmsg, ier 222 | 223 | # 224 | 225 | def _optimizedelay(self,f_data,z_data,Ql,fr,maxiter=4): 226 | xc,yc,r0 = self._fit_circle(z_data) 227 | z_data = self._center(z_data,complex(xc,yc)) 228 | theta, Ql, fr, slope = self._phase_fit_wslope(f_data,z_data,0.,Ql,fr,0.) 229 | delay = 0. 230 | for i in range(maxiter-1): #interate to get besser phase delay term 231 | delay = delay - slope/(2.*2.*np.pi) 232 | z_data_corr = self._remove_cable_delay(f_data,z_data,delay) 233 | xc, yc, r0 = self._fit_circle(z_data_corr) 234 | z_data_corr2 = self._center(z_data_corr,complex(xc,yc)) 235 | theta0, Ql, fr, slope = self._phase_fit_wslope(f_data,z_data_corr2,0.,Ql,fr,0.) 236 | delay = delay - slope/(2.*2.*np.pi) #start final interation 237 | return delay 238 | 239 | def _fit_circle_iter(self,z_data, xc, yc, rc): 240 | ''' 241 | this is the radial weighting procedure 242 | it improves your fitting value for the radius = Ql/Qc 243 | use this to improve your fit in presence of heavy noise 244 | after having used the standard algebraic fir_circle() function 245 | the weight here is: W=1/sqrt((xc-xi)^2+(yc-yi)^2) 246 | this works, because the center of the circle is usually much less 247 | corrupted by noise than the radius 248 | ''' 249 | xdat = z_data.real 250 | ydat = z_data.imag 251 | def fitfunc(x,y,xc,yc): 252 | return np.sqrt((x-xc)**2+(y-yc)**2) 253 | def residuals(p,x,y): 254 | xc,yc,r = p 255 | temp = (r-fitfunc(x,y,xc,yc)) 256 | return temp 257 | p0 = [xc,yc,rc] 258 | p_final = spopt.leastsq(residuals,p0,args=(xdat,ydat)) 259 | xc,yc,rc = p_final[0] 260 | return xc,yc,rc 261 | 262 | def _fit_circle_iter_radialweight(self,z_data, xc, yc, rc): 263 | ''' 264 | this is the radial weighting procedure 265 | it improves your fitting value for the radius = Ql/Qc 266 | use this to improve your fit in presence of heavy noise 267 | after having used the standard algebraic fir_circle() function 268 | the weight here is: W=1/sqrt((xc-xi)^2+(yc-yi)^2) 269 | this works, because the center of the circle is usually much less 270 | corrupted by noise than the radius 271 | ''' 272 | xdat = z_data.real 273 | ydat = z_data.imag 274 | def fitfunc(x,y): 275 | return np.sqrt((x-xc)**2+(y-yc)**2) 276 | def weight(x,y): 277 | try: 278 | res = 1./np.sqrt((xc-x)**2+(yc-y)**2) 279 | except: 280 | res = 1. 281 | return res 282 | def residuals(p,x,y): 283 | r = p[0] 284 | temp = (r-fitfunc(x,y))*weight(x,y) 285 | return temp 286 | p0 = [rc] 287 | p_final = spopt.leastsq(residuals,p0,args=(xdat,ydat)) 288 | return p_final[0][0] 289 | 290 | def _get_errors(self,residual,xdata,ydata,fitparams): 291 | ''' 292 | wrapper for get_cov, only gives the errors and chisquare 293 | ''' 294 | chisqr, cov = self._get_cov(residual,xdata,ydata,fitparams) 295 | if cov is not None: 296 | errors = np.sqrt(np.diagonal(cov)) 297 | else: 298 | errors = None 299 | return chisqr, errors 300 | 301 | def _residuals_notch_full(self,p,x,y): 302 | fr,absQc,Ql,phi0,delay,a,alpha = p 303 | err = np.absolute( y - ( a*np.exp(complex(0,alpha))*np.exp(complex(0,-2.*np.pi*delay*x)) * ( 1 - (Ql/absQc*np.exp(complex(0,phi0)))/(complex(1,2*Ql*(x-fr)/float(fr))) ) ) ) 304 | return err 305 | 306 | def _residuals_notch_ideal(self,p,x,y): 307 | fr,absQc,Ql,phi0 = p 308 | #if fr == 0: print(p) 309 | err = np.absolute( y - ( ( 1. - (Ql/float(absQc)*np.exp(1j*phi0))/(1+2j*Ql*(x-fr)/float(fr)) ) ) ) 310 | #if np.isinf((complex(1,2*Ql*(x-fr)/float(fr))).imag): 311 | # print(complex(1,2*Ql*(x-fr)/float(fr))) 312 | # print("x: " + str(x)) 313 | # print("Ql: " +str(Ql)) 314 | #print("fr: " +str(fr)) 315 | return err 316 | 317 | def _residuals_notch_ideal_complex(self,p,x,y): 318 | fr,absQc,Ql,phi0 = p 319 | #if fr == 0: print(p) 320 | err = y - ( ( 1. - (Ql/float(absQc)*np.exp(1j*phi0))/(1+2j*Ql*(x-fr)/float(fr)) ) ) 321 | #if np.isinf((complex(1,2*Ql*(x-fr)/float(fr))).imag): 322 | # print(complex(1,2*Ql*(x-fr)/float(fr))) 323 | # print("x: " + str(x)) 324 | # print("Ql: " +str(Ql)) 325 | #print("fr: " +str(fr)) 326 | return err 327 | 328 | def _residuals_directrefl(self,p,x,y): 329 | fr,Qc,Ql = p 330 | #if fr == 0: print(p) 331 | err = y - ( 2.*Ql/Qc - 1. + 2j*Ql*(fr-x)/fr ) / ( 1. - 2j*Ql*(fr-x)/fr ) 332 | #if np.isinf((complex(1,2*Ql*(x-fr)/float(fr))).imag): 333 | # print(complex(1,2*Ql*(x-fr)/float(fr))) 334 | # print("x: " + str(x)) 335 | # print("Ql: " +str(Ql)) 336 | #print("fr: " +str(fr)) 337 | return err 338 | 339 | def _residuals_transm_ideal(self,p,x,y): 340 | fr,Ql = p 341 | err = np.absolute( y - ( 1./(complex(1,2*Ql*(x-fr)/float(fr))) ) ) 342 | return err 343 | 344 | 345 | def _get_cov_fast_notch(self,xdata,ydata,fitparams): #enhanced by analytical derivatives 346 | #derivatives of notch_ideal model with respect to parameters 347 | def dS21_dQl(p,f): 348 | fr,absQc,Ql,phi0 = p 349 | return - (np.exp(1j*phi0) * fr**2) / (absQc * (fr+2j*Ql*f-2j*Ql*fr)**2 ) 350 | 351 | def dS21_dQc(p,f): 352 | fr,absQc,Ql,phi0 = p 353 | return (np.exp(1j*phi0) * Ql*fr) / (2j*(f-fr)*absQc**2*Ql+absQc**2*fr ) 354 | 355 | def dS21_dphi0(p,f): 356 | fr,absQc,Ql,phi0 = p 357 | return - (1j*Ql*fr*np.exp(1j*phi0) ) / (2j*(f-fr)*absQc*Ql+absQc*fr ) 358 | 359 | def dS21_dfr(p,f): 360 | fr,absQc,Ql,phi0 = p 361 | return - (2j*Ql**2*f*np.exp(1j*phi0) ) / (absQc * (fr+2j*Ql*f-2j*Ql*fr)**2 ) 362 | 363 | u = self._residuals_notch_ideal_complex(fitparams,xdata,ydata) 364 | chi = np.absolute(u) 365 | u = u/chi # unit vector pointing in the correct direction for the derivative 366 | 367 | aa = dS21_dfr(fitparams,xdata) 368 | bb = dS21_dQc(fitparams,xdata) 369 | cc = dS21_dQl(fitparams,xdata) 370 | dd = dS21_dphi0(fitparams,xdata) 371 | 372 | Jt = np.array([aa.real*u.real+aa.imag*u.imag, bb.real*u.real+bb.imag*u.imag\ 373 | , cc.real*u.real+cc.imag*u.imag, dd.real*u.real+dd.imag*u.imag ]) 374 | A = np.dot(Jt,np.transpose(Jt)) 375 | chisqr = 1./float(len(xdata)-len(fitparams)) * (chi**2).sum() 376 | try: 377 | cov = np.linalg.inv(A)*chisqr 378 | except: 379 | cov = None 380 | return chisqr, cov 381 | 382 | def _get_cov_fast_directrefl(self,xdata,ydata,fitparams): #enhanced by analytical derivatives 383 | #derivatives of notch_ideal model with respect to parameters 384 | def dS21_dQl(p,f): 385 | fr,Qc,Ql = p 386 | return 2.*fr**2/( Qc*(2j*Ql*fr-2j*Ql*f+fr)**2 ) 387 | 388 | def dS21_dQc(p,f): 389 | fr,Qc,Ql = p 390 | return 2.*Ql*fr/(2j*Qc**2*(f-fr)*Ql-Qc**2*fr) 391 | 392 | def dS21_dfr(p,f): 393 | fr,Qc,Ql = p 394 | return - 4j*Ql**2*f/(Qc*(2j*Ql*fr-2j*Ql*f+fr)**2) 395 | 396 | u = self._residuals_directrefl(fitparams,xdata,ydata) 397 | chi = np.absolute(u) 398 | u = u/chi # unit vector pointing in the correct direction for the derivative 399 | 400 | aa = dS21_dfr(fitparams,xdata) 401 | bb = dS21_dQc(fitparams,xdata) 402 | cc = dS21_dQl(fitparams,xdata) 403 | 404 | Jt = np.array([aa.real*u.real+aa.imag*u.imag, bb.real*u.real+bb.imag*u.imag\ 405 | , cc.real*u.real+cc.imag*u.imag ]) 406 | A = np.dot(Jt,np.transpose(Jt)) 407 | chisqr = 1./float(len(xdata)-len(fitparams)) * (chi**2).sum() 408 | try: 409 | cov = np.linalg.inv(A)*chisqr 410 | except: 411 | cov = None 412 | return chisqr, cov -------------------------------------------------------------------------------- /resonator_tools/circuit.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import numpy as np 3 | import scipy.optimize as spopt 4 | from scipy.constants import hbar 5 | from scipy.interpolate import splrep, splev 6 | 7 | from resonator_tools.utilities import plotting, save_load, Watt2dBm, dBm2Watt 8 | from resonator_tools.circlefit import circlefit 9 | from resonator_tools.calibration import calibration 10 | 11 | ## 12 | ## z_data_raw denotes the raw data 13 | ## z_data denotes the normalized data 14 | ## 15 | 16 | class reflection_port(circlefit, save_load, plotting, calibration): 17 | ''' 18 | normal direct port probed in reflection 19 | ''' 20 | def __init__(self, f_data=None, z_data_raw=None): 21 | self.porttype = 'direct' 22 | self.fitresults = {} 23 | self.z_data = None 24 | if f_data is not None: 25 | self.f_data = np.array(f_data) 26 | else: 27 | self.f_data=None 28 | if z_data_raw is not None: 29 | self.z_data_raw = np.array(z_data_raw) 30 | else: 31 | self.z_data=None 32 | self.phasefitsmooth = 3 33 | 34 | def _S11(self,f,fr,k_c,k_i): 35 | ''' 36 | use either frequency or angular frequency units 37 | for all quantities 38 | k_l=k_c+k_i: total (loaded) coupling rate 39 | k_c: coupling rate 40 | k_i: internal loss rate 41 | ''' 42 | return ((k_c-k_i)+2j*(f-fr))/((k_c+k_i)-2j*(f-fr)) 43 | 44 | def get_delay(self,f_data,z_data,delay=None,ignoreslope=True,guess=True): 45 | ''' 46 | ignoreslope option not used here 47 | retrieves the cable delay assuming the ideal resonance has a circular shape 48 | modifies the cable delay until the shape Im(S21) vs Re(S21) is circular 49 | see "do_calibration" 50 | ''' 51 | maxval = np.max(np.absolute(z_data)) 52 | z_data = z_data/maxval 53 | A1, A2, A3, A4, fr, Ql = self._fit_skewed_lorentzian(f_data,z_data) 54 | if self.df_error/fr > 0.0001 or self.dQl_error/Ql>0.1: 55 | #print("WARNING: Calibration using Lorentz fit failed, trying phase fit...") 56 | A1 = np.mean(np.absolute(z_data)) 57 | A2 = 0. 58 | A3 = 0. 59 | A4 = 0. 60 | #fr = np.mean(f_data) 61 | f = splrep(f_data,np.unwrap(np.angle(z_data)),k=5,s=self.phasefitsmooth) 62 | fr = f_data[np.argmax(np.absolute(splev(f_data,f,der=1)))] 63 | Ql = 1e4 64 | if ignoreslope==True: 65 | A2 = 0. 66 | else: 67 | A2 = 0. 68 | print("WARNING: The ignoreslope option is ignored! Corrections to the baseline should be done manually prior to fitting.") 69 | print("see also: resonator_tools.calibration.fit_baseline_amp() etc. for help on fitting the baseline.") 70 | print("There is also an example ipython notebook for using this function.") 71 | print("However, make sure to understand the impact of the baseline (parasitic coupled resonances etc.) on your system.") 72 | #z_data = (np.absolute(z_data)-A2*(f_data-fr)) * np.exp(np.angle(z_data)*1j) #usually not necessary 73 | if delay is None: 74 | if guess==True: 75 | delay = self._guess_delay(f_data,z_data) 76 | else: 77 | delay=0. 78 | delay = self._fit_delay(f_data,z_data,delay,maxiter=200) 79 | params = [A1, A2, A3, A4, fr, Ql] 80 | return delay, params 81 | 82 | def do_calibration(self,f_data,z_data,ignoreslope=True,guessdelay=True,fixed_delay=None): 83 | ''' 84 | calculating parameters for normalization 85 | ''' 86 | delay, params = self.get_delay(f_data,z_data,ignoreslope=ignoreslope,guess=guessdelay,delay=fixed_delay) 87 | z_data = (z_data-params[1]*(f_data-params[4]))*np.exp(2.*1j*np.pi*delay*f_data) 88 | xc, yc, r0 = self._fit_circle(z_data) 89 | zc = complex(xc,yc) 90 | fitparams = self._phase_fit(f_data,self._center(z_data,zc),0.,np.absolute(params[5]),params[4]) 91 | theta, Ql, fr = fitparams 92 | beta = self._periodic_boundary(theta+np.pi,np.pi) ### 93 | offrespoint = complex((xc+r0*np.cos(beta)),(yc+r0*np.sin(beta))) 94 | alpha = self._periodic_boundary(np.angle(offrespoint)+np.pi,np.pi) 95 | #a = np.absolute(offrespoint) 96 | #alpha = np.angle(zc) 97 | a = r0 + np.absolute(zc) 98 | return delay, a, alpha, fr, Ql, params[1], params[4] 99 | 100 | def do_normalization(self,f_data,z_data,delay,amp_norm,alpha,A2,frcal): 101 | ''' 102 | transforming resonator into canonical position 103 | ''' 104 | return (z_data-A2*(f_data-frcal))/amp_norm*np.exp(1j*(-alpha+2.*np.pi*delay*f_data)) 105 | 106 | def circlefit(self,f_data,z_data,fr=None,Ql=None,refine_results=False,calc_errors=True): 107 | ''' 108 | S11 version of the circlefit 109 | ''' 110 | 111 | if fr is None: fr=f_data[np.argmin(np.absolute(z_data))] 112 | if Ql is None: Ql=1e6 113 | xc, yc, r0 = self._fit_circle(z_data,refine_results=refine_results) 114 | phi0 = -np.arcsin(yc/r0) 115 | theta0 = self._periodic_boundary(phi0+np.pi,np.pi) 116 | z_data_corr = self._center(z_data,complex(xc,yc)) 117 | theta0, Ql, fr = self._phase_fit(f_data,z_data_corr,theta0,Ql,fr) 118 | #print("Ql from phasefit is: " + str(Ql)) 119 | Qi = Ql/(1.-r0) 120 | Qc = 1./(1./Ql-1./Qi) 121 | 122 | results = {"Qi":Qi,"Qc":Qc,"Ql":Ql,"fr":fr,"theta0":theta0} 123 | 124 | #calculation of the error 125 | p = [fr,Qc,Ql] 126 | #chi_square, errors = rt.get_errors(rt.residuals_notch_ideal,f_data,z_data,p) 127 | if calc_errors==True: 128 | chi_square, cov = self._get_cov_fast_directrefl(f_data,z_data,p) 129 | #chi_square, cov = rt.get_cov(rt.residuals_notch_ideal,f_data,z_data,p) 130 | 131 | if cov is not None: 132 | errors = np.sqrt(np.diagonal(cov)) 133 | fr_err,Qc_err,Ql_err = errors 134 | #calc Qi with error prop (sum the squares of the variances and covariaces) 135 | dQl = 1./((1./Ql-1./Qc)**2*Ql**2) 136 | dQc = - 1./((1./Ql-1./Qc)**2*Qc**2) 137 | Qi_err = np.sqrt((dQl**2*cov[2][2]) + (dQc**2*cov[1][1])+(2*dQl*dQc*cov[2][1])) #with correlations 138 | errors = {"Ql_err":Ql_err, "Qc_err":Qc_err, "fr_err":fr_err,"chi_square":chi_square,"Qi_err":Qi_err} 139 | results.update( errors ) 140 | else: 141 | print("WARNING: Error calculation failed!") 142 | else: 143 | #just calc chisquared: 144 | fun2 = lambda x: self._residuals_notch_ideal(x,f_data,z_data)**2 145 | chi_square = 1./float(len(f_data)-len(p)) * (fun2(p)).sum() 146 | errors = {"chi_square":chi_square} 147 | results.update(errors) 148 | 149 | return results 150 | 151 | 152 | def autofit(self,electric_delay=None,fcrop=None): 153 | ''' 154 | automatic calibration and fitting 155 | electric_delay: set the electric delay manually 156 | fcrop = (f1,f2) : crop the frequency range used for fitting 157 | ''' 158 | if fcrop is None: 159 | self._fid = np.ones(self.f_data.size,dtype=bool) 160 | else: 161 | f1, f2 = fcrop 162 | self._fid = np.logical_and(self.f_data>=f1,self.f_data<=f2) 163 | delay, amp_norm, alpha, fr, Ql, A2, frcal =\ 164 | self.do_calibration(self.f_data[self._fid],self.z_data_raw[self._fid],ignoreslope=True,guessdelay=False,fixed_delay=electric_delay) 165 | self.z_data = self.do_normalization(self.f_data,self.z_data_raw,delay,amp_norm,alpha,A2,frcal) 166 | self.fitresults = self.circlefit(self.f_data[self._fid],self.z_data[self._fid],fr,Ql,refine_results=False,calc_errors=True) 167 | self.z_data_sim = A2*(self.f_data-frcal)+self._S11_directrefl(self.f_data,fr=self.fitresults["fr"],Ql=self.fitresults["Ql"],Qc=self.fitresults["Qc"],a=amp_norm,alpha=alpha,delay=delay) 168 | self.z_data_sim_norm = self._S11_directrefl(self.f_data,fr=self.fitresults["fr"],Ql=self.fitresults["Ql"],Qc=self.fitresults["Qc"],a=1.,alpha=0.,delay=0.) 169 | self._delay = delay 170 | 171 | def GUIfit(self): 172 | ''' 173 | automatic fit with possible user interaction to crop the data and modify the electric delay 174 | f1,f2,delay are determined in the GUI. Then, data is cropped and autofit with delay is performed 175 | ''' 176 | #copy data 177 | fmin, fmax = self.f_data.min(), self.f_data.max() 178 | self.autofit() 179 | self.__delay = self._delay 180 | #prepare plot and slider 181 | import matplotlib.pyplot as plt 182 | from matplotlib.widgets import Slider, Button 183 | fig, ((ax2,ax0),(ax1,ax3)) = plt.subplots(nrows=2,ncols=2) 184 | plt.suptitle('Normalized data. Use the silders to improve the fitting if necessary.') 185 | plt.subplots_adjust(left=0.25, bottom=0.25) 186 | l0, = ax0.plot(self.f_data*1e-9,np.absolute(self.z_data)) 187 | l1, = ax1.plot(self.f_data*1e-9,np.angle(self.z_data)) 188 | l2, = ax2.plot(np.real(self.z_data),np.imag(self.z_data)) 189 | l0s, = ax0.plot(self.f_data*1e-9,np.absolute(self.z_data_sim_norm)) 190 | l1s, = ax1.plot(self.f_data*1e-9,np.angle(self.z_data_sim_norm)) 191 | l2s, = ax2.plot(np.real(self.z_data_sim_norm),np.imag(self.z_data_sim_norm)) 192 | ax0.set_xlabel('f (GHz)') 193 | ax1.set_xlabel('f (GHz)') 194 | ax2.set_xlabel('real') 195 | ax0.set_ylabel('amp') 196 | ax1.set_ylabel('phase (rad)') 197 | ax2.set_ylabel('imagl') 198 | fr_ann = ax3.annotate('fr = %e Hz +- %e Hz' % (self.fitresults['fr'],self.fitresults['fr_err']),xy=(0.1, 0.8), xycoords='axes fraction') 199 | Ql_ann = ax3.annotate('Ql = %e +- %e' % (self.fitresults['Ql'],self.fitresults['Ql_err']),xy=(0.1, 0.6), xycoords='axes fraction') 200 | Qc_ann = ax3.annotate('Qc = %e +- %e' % (self.fitresults['Qc'],self.fitresults['Qc_err']),xy=(0.1, 0.4), xycoords='axes fraction') 201 | Qi_ann = ax3.annotate('Qi = %e +- %e' % (self.fitresults['Qi'],self.fitresults['Qi_err']),xy=(0.1, 0.2), xycoords='axes fraction') 202 | axcolor = 'lightgoldenrodyellow' 203 | axdelay = plt.axes([0.25, 0.05, 0.65, 0.03], axisbg=axcolor) 204 | axf2 = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor) 205 | axf1 = plt.axes([0.25, 0.15, 0.65, 0.03], axisbg=axcolor) 206 | sscale = 10. 207 | sdelay = Slider(axdelay, 'delay', -1., 1., valinit=self.__delay/(sscale*self.__delay),valfmt='%f') 208 | df = (fmax-fmin)*0.05 209 | sf2 = Slider(axf2, 'f2', (fmin-df)*1e-9, (fmax+df)*1e-9, valinit=fmax*1e-9,valfmt='%.10f GHz') 210 | sf1 = Slider(axf1, 'f1', (fmin-df)*1e-9, (fmax+df)*1e-9, valinit=fmin*1e-9,valfmt='%.10f GHz') 211 | def update(val): 212 | self.autofit(electric_delay=sdelay.val*sscale*self.__delay,fcrop=(sf1.val*1e9,sf2.val*1e9)) 213 | l0.set_data(self.f_data*1e-9,np.absolute(self.z_data)) 214 | l1.set_data(self.f_data*1e-9,np.angle(self.z_data)) 215 | l2.set_data(np.real(self.z_data),np.imag(self.z_data)) 216 | l0s.set_data(self.f_data[self._fid]*1e-9,np.absolute(self.z_data_sim_norm[self._fid])) 217 | l1s.set_data(self.f_data[self._fid]*1e-9,np.angle(self.z_data_sim_norm[self._fid])) 218 | l2s.set_data(np.real(self.z_data_sim_norm[self._fid]),np.imag(self.z_data_sim_norm[self._fid])) 219 | fr_ann.set_text('fr = %e Hz +- %e Hz' % (self.fitresults['fr'],self.fitresults['fr_err'])) 220 | Ql_ann.set_text('Ql = %e +- %e' % (self.fitresults['Ql'],self.fitresults['Ql_err'])) 221 | Qc_ann.set_text('Qc = %e +- %e' % (self.fitresults['Qc'],self.fitresults['Qc_err'])) 222 | Qi_ann.set_text('Qi = %e +- %e' % (self.fitresults['Qi'],self.fitresults['Qi_err'])) 223 | fig.canvas.draw_idle() 224 | def btnclicked(event): 225 | self.autofit(electric_delay=None,fcrop=(sf1.val*1e9,sf2.val*1e9)) 226 | self.__delay = self._delay 227 | sdelay.reset() 228 | update(event) 229 | sf1.on_changed(update) 230 | sf2.on_changed(update) 231 | sdelay.on_changed(update) 232 | btnax = plt.axes([0.05, 0.1, 0.1, 0.04]) 233 | button = Button(btnax, 'auto-delay', color=axcolor, hovercolor='0.975') 234 | button.on_clicked(btnclicked) 235 | plt.show() 236 | plt.close() 237 | 238 | def _S11_directrefl(self,f,fr=10e9,Ql=900,Qc=1000.,a=1.,alpha=0.,delay=.0): 239 | ''' 240 | full model for notch type resonances 241 | ''' 242 | return a*np.exp(complex(0,alpha))*np.exp(-2j*np.pi*f*delay) * ( 2.*Ql/Qc - 1. + 2j*Ql*(fr-f)/fr ) / ( 1. - 2j*Ql*(fr-f)/fr ) 243 | 244 | def get_single_photon_limit(self,unit='dBm'): 245 | ''' 246 | returns the amout of power in units of W necessary 247 | to maintain one photon on average in the cavity 248 | unit can be 'dbm' or 'watt' 249 | ''' 250 | if self.fitresults!={}: 251 | fr = self.fitresults['fr'] 252 | k_c = 2*np.pi*fr/self.fitresults['Qc'] 253 | k_i = 2*np.pi*fr/self.fitresults['Qi'] 254 | if unit=='dBm': 255 | return Watt2dBm(1./(4.*k_c/(2.*np.pi*hbar*fr*(k_c+k_i)**2))) 256 | elif unit=='watt': 257 | return 1./(4.*k_c/(2.*np.pi*hbar*fr*(k_c+k_i)**2)) 258 | 259 | else: 260 | warnings.warn('Please perform the fit first',UserWarning) 261 | return None 262 | 263 | def get_photons_in_resonator(self,power,unit='dBm'): 264 | ''' 265 | returns the average number of photons 266 | for a given power (defaul unit is 'dbm') 267 | unit can be 'dBm' or 'watt' 268 | ''' 269 | if self.fitresults!={}: 270 | if unit=='dBm': 271 | power = dBm2Watt(power) 272 | fr = self.fitresults['fr'] 273 | k_c = 2*np.pi*fr/self.fitresults['Qc'] 274 | k_i = 2*np.pi*fr/self.fitresults['Qi'] 275 | return 4.*k_c/(2.*np.pi*hbar*fr*(k_c+k_i)**2) * power 276 | else: 277 | warnings.warn('Please perform the fit first',UserWarning) 278 | return None 279 | 280 | class notch_port(circlefit, save_load, plotting, calibration): 281 | ''' 282 | notch type port probed in transmission 283 | ''' 284 | def __init__(self, f_data=None, z_data_raw=None): 285 | self.porttype = 'notch' 286 | self.fitresults = {} 287 | self.z_data = None 288 | if f_data is not None: 289 | self.f_data = np.array(f_data) 290 | else: 291 | self.f_data=None 292 | if z_data_raw is not None: 293 | self.z_data_raw = np.array(z_data_raw) 294 | else: 295 | self.z_data_raw=None 296 | 297 | def get_delay(self,f_data,z_data,delay=None,ignoreslope=True,guess=True): 298 | ''' 299 | retrieves the cable delay assuming the ideal resonance has a circular shape 300 | modifies the cable delay until the shape Im(S21) vs Re(S21) is circular 301 | see "do_calibration" 302 | ''' 303 | maxval = np.max(np.absolute(z_data)) 304 | z_data = z_data/maxval 305 | A1, A2, A3, A4, fr, Ql = self._fit_skewed_lorentzian(f_data,z_data) 306 | if ignoreslope==True: 307 | A2 = 0. 308 | else: 309 | A2 = 0. 310 | print("WARNING: The ignoreslope option is ignored! Corrections to the baseline should be done manually prior to fitting.") 311 | print("see also: resonator_tools.calibration.fit_baseline_amp() etc. for help on fitting the baseline.") 312 | print("There is also an example ipython notebook for using this function.") 313 | print("However, make sure to understand the impact of the baseline (parasitic coupled resonances etc.) on your system.") 314 | #z_data = (np.absolute(z_data)-A2*(f_data-fr)) * np.exp(np.angle(z_data)*1j) #usually not necessary 315 | if delay is None: 316 | if guess==True: 317 | delay = self._guess_delay(f_data,z_data) 318 | else: 319 | delay=0. 320 | delay = self._fit_delay(f_data,z_data,delay,maxiter=200) 321 | params = [A1, A2, A3, A4, fr, Ql] 322 | return delay, params 323 | 324 | def do_calibration(self,f_data,z_data,ignoreslope=True,guessdelay=True,fixed_delay=None, Ql_guess=None, fr_guess=None): 325 | ''' 326 | performs an automated calibration and tries to determine the prefactors a, alpha, delay 327 | fr, Ql, and a possible slope are extra information, which can be used as start parameters for subsequent fits 328 | see also "do_normalization" 329 | the calibration procedure works for transmission line resonators as well 330 | ''' 331 | delay, params = self.get_delay(f_data,z_data,ignoreslope=ignoreslope,guess=guessdelay,delay=fixed_delay) 332 | z_data = (z_data-params[1]*(f_data-params[4]))*np.exp(2.*1j*np.pi*delay*f_data) 333 | xc, yc, r0 = self._fit_circle(z_data) 334 | zc = complex(xc,yc) 335 | if Ql_guess is None: Ql_guess=np.absolute(params[5]) 336 | if fr_guess is None: fr_guess=params[4] 337 | fitparams = self._phase_fit(f_data,self._center(z_data,zc),0.,Ql_guess,fr_guess) 338 | theta, Ql, fr = fitparams 339 | beta = self._periodic_boundary(theta+np.pi,np.pi) 340 | offrespoint = complex((xc+r0*np.cos(beta)),(yc+r0*np.sin(beta))) 341 | alpha = np.angle(offrespoint) 342 | a = np.absolute(offrespoint) 343 | return delay, a, alpha, fr, Ql, params[1], params[4] 344 | 345 | def do_normalization(self,f_data,z_data,delay,amp_norm,alpha,A2,frcal): 346 | ''' 347 | removes the prefactors a, alpha, delay and returns the calibrated data, see also "do_calibration" 348 | works also for transmission line resonators 349 | ''' 350 | return (z_data-A2*(f_data-frcal))/amp_norm*np.exp(1j*(-alpha+2.*np.pi*delay*f_data)) 351 | 352 | def circlefit(self,f_data,z_data,fr=None,Ql=None,refine_results=False,calc_errors=True): 353 | ''' 354 | performs a circle fit on a frequency vs. complex resonator scattering data set 355 | Data has to be normalized!! 356 | INPUT: 357 | f_data,z_data: input data (frequency, complex S21 data) 358 | OUTPUT: 359 | outpus a dictionary {key:value} consisting of the fit values, errors and status information about the fit 360 | values: {"phi0":phi0, "Ql":Ql, "absolute(Qc)":absQc, "Qi": Qi, "electronic_delay":delay, "complexQc":complQc, "resonance_freq":fr, "prefactor_a":a, "prefactor_alpha":alpha} 361 | errors: {"phi0_err":phi0_err, "Ql_err":Ql_err, "absolute(Qc)_err":absQc_err, "Qi_err": Qi_err, "electronic_delay_err":delay_err, "resonance_freq_err":fr_err, "prefactor_a_err":a_err, "prefactor_alpha_err":alpha_err} 362 | for details, see: 363 | [1] (not diameter corrected) Jiansong Gao, "The Physics of Superconducting Microwave Resonators" (PhD Thesis), Appendix E, California Institute of Technology, (2008) 364 | [2] (diameter corrected) M. S. Khalil, et. al., J. Appl. Phys. 111, 054510 (2012) 365 | [3] (fitting techniques) N. CHERNOV AND C. LESORT, "Least Squares Fitting of Circles", Journal of Mathematical Imaging and Vision 23, 239, (2005) 366 | [4] (further fitting techniques) P. J. Petersan, S. M. Anlage, J. Appl. Phys, 84, 3392 (1998) 367 | the program fits the circle with the algebraic technique described in [3], the rest of the fitting is done with the scipy.optimize least square fitting toolbox 368 | also, check out [5] S. Probst et al. "Efficient and reliable analysis of noisy complex scatterung resonator data for superconducting quantum circuits" (in preparation) 369 | ''' 370 | 371 | if fr is None: fr=f_data[np.argmin(np.absolute(z_data))] 372 | if Ql is None: Ql=1e6 373 | xc, yc, r0 = self._fit_circle(z_data,refine_results=refine_results) 374 | phi0 = -np.arcsin(yc/r0) 375 | theta0 = self._periodic_boundary(phi0+np.pi,np.pi) 376 | z_data_corr = self._center(z_data,complex(xc,yc)) 377 | theta0, Ql, fr = self._phase_fit(f_data,z_data_corr,theta0,Ql,fr) 378 | #print("Ql from phasefit is: " + str(Ql)) 379 | absQc = Ql/(2.*r0) 380 | complQc = absQc*np.exp(1j*((-1.)*phi0)) 381 | Qc = 1./(1./complQc).real # here, taking the real part of (1/complQc) from diameter correction method 382 | Qi_dia_corr = 1./(1./Ql-1./Qc) 383 | Qi_no_corr = 1./(1./Ql-1./absQc) 384 | 385 | results = {"Qi_dia_corr":Qi_dia_corr,"Qi_no_corr":Qi_no_corr,"absQc":absQc,"Qc_dia_corr":Qc,"Ql":Ql,"fr":fr,"theta0":theta0,"phi0":phi0} 386 | 387 | #calculation of the error 388 | p = [fr,absQc,Ql,phi0] 389 | #chi_square, errors = rt.get_errors(rt.residuals_notch_ideal,f_data,z_data,p) 390 | if calc_errors==True: 391 | chi_square, cov = self._get_cov_fast_notch(f_data,z_data,p) 392 | #chi_square, cov = rt.get_cov(rt.residuals_notch_ideal,f_data,z_data,p) 393 | 394 | if cov is not None: 395 | errors = np.sqrt(np.diagonal(cov)) 396 | fr_err,absQc_err,Ql_err,phi0_err = errors 397 | #calc Qi with error prop (sum the squares of the variances and covariaces) 398 | dQl = 1./((1./Ql-1./absQc)**2*Ql**2) 399 | dabsQc = - 1./((1./Ql-1./absQc)**2*absQc**2) 400 | Qi_no_corr_err = np.sqrt((dQl**2*cov[2][2]) + (dabsQc**2*cov[1][1])+(2*dQl*dabsQc*cov[2][1])) #with correlations 401 | #calc Qi dia corr with error prop 402 | dQl = 1/((1/Ql-np.cos(phi0)/absQc)**2 *Ql**2) 403 | dabsQc = -np.cos(phi0)/((1/Ql-np.cos(phi0)/absQc)**2 *absQc**2) 404 | dphi0 = -np.sin(phi0)/((1/Ql-np.cos(phi0)/absQc)**2 *absQc) 405 | ##err1 = ( (dQl*cov[2][2])**2 + (dabsQc*cov[1][1])**2 + (dphi0*cov[3][3])**2 ) 406 | err1 = ( (dQl**2*cov[2][2]) + (dabsQc**2*cov[1][1]) + (dphi0**2*cov[3][3]) ) 407 | err2 = ( dQl*dabsQc*cov[2][1] + dQl*dphi0*cov[2][3] + dabsQc*dphi0*cov[1][3] ) 408 | Qi_dia_corr_err = np.sqrt(err1+2*err2) # including correlations 409 | errors = {"phi0_err":phi0_err, "Ql_err":Ql_err, "absQc_err":absQc_err, "fr_err":fr_err,"chi_square":chi_square,"Qi_no_corr_err":Qi_no_corr_err,"Qi_dia_corr_err": Qi_dia_corr_err} 410 | results.update( errors ) 411 | else: 412 | print("WARNING: Error calculation failed!") 413 | else: 414 | #just calc chisquared: 415 | fun2 = lambda x: self._residuals_notch_ideal(x,f_data,z_data)**2 416 | chi_square = 1./float(len(f_data)-len(p)) * (fun2(p)).sum() 417 | errors = {"chi_square":chi_square} 418 | results.update(errors) 419 | 420 | return results 421 | 422 | def autofit(self,electric_delay=None,fcrop=None,Ql_guess=None, fr_guess=None): 423 | ''' 424 | automatic calibration and fitting 425 | electric_delay: set the electric delay manually 426 | fcrop = (f1,f2) : crop the frequency range used for fitting 427 | ''' 428 | if fcrop is None: 429 | self._fid = np.ones(self.f_data.size,dtype=bool) 430 | else: 431 | f1, f2 = fcrop 432 | self._fid = np.logical_and(self.f_data>=f1,self.f_data<=f2) 433 | delay, amp_norm, alpha, fr, Ql, A2, frcal =\ 434 | self.do_calibration(self.f_data[self._fid],self.z_data_raw[self._fid],ignoreslope=True,guessdelay=True,fixed_delay=electric_delay,Ql_guess=Ql_guess, fr_guess=fr_guess) 435 | self.z_data = self.do_normalization(self.f_data,self.z_data_raw,delay,amp_norm,alpha,A2,frcal) 436 | self.fitresults = self.circlefit(self.f_data[self._fid],self.z_data[self._fid],fr,Ql,refine_results=False,calc_errors=True) 437 | self.z_data_sim = A2*(self.f_data-frcal)+self._S21_notch(self.f_data,fr=self.fitresults["fr"],Ql=self.fitresults["Ql"],Qc=self.fitresults["absQc"],phi=self.fitresults["phi0"],a=amp_norm,alpha=alpha,delay=delay) 438 | self.z_data_sim_norm = self._S21_notch(self.f_data,fr=self.fitresults["fr"],Ql=self.fitresults["Ql"],Qc=self.fitresults["absQc"],phi=self.fitresults["phi0"],a=1.0,alpha=0.,delay=0.) 439 | self._delay = delay 440 | 441 | def GUIfit(self): 442 | ''' 443 | automatic fit with possible user interaction to crop the data and modify the electric delay 444 | f1,f2,delay are determined in the GUI. Then, data is cropped and autofit with delay is performed 445 | ''' 446 | #copy data 447 | fmin, fmax = self.f_data.min(), self.f_data.max() 448 | self.autofit() 449 | self.__delay = self._delay 450 | #prepare plot and slider 451 | import matplotlib.pyplot as plt 452 | from matplotlib.widgets import Slider, Button 453 | fig, ((ax2,ax0),(ax1,ax3)) = plt.subplots(nrows=2,ncols=2) 454 | plt.suptitle('Normalized data. Use the silders to improve the fitting if necessary.') 455 | plt.subplots_adjust(left=0.25, bottom=0.25) 456 | l0, = ax0.plot(self.f_data*1e-9,np.absolute(self.z_data)) 457 | l1, = ax1.plot(self.f_data*1e-9,np.angle(self.z_data)) 458 | l2, = ax2.plot(np.real(self.z_data),np.imag(self.z_data)) 459 | l0s, = ax0.plot(self.f_data*1e-9,np.absolute(self.z_data_sim_norm)) 460 | l1s, = ax1.plot(self.f_data*1e-9,np.angle(self.z_data_sim_norm)) 461 | l2s, = ax2.plot(np.real(self.z_data_sim_norm),np.imag(self.z_data_sim_norm)) 462 | ax0.set_xlabel('f (GHz)') 463 | ax1.set_xlabel('f (GHz)') 464 | ax2.set_xlabel('real') 465 | ax0.set_ylabel('amp') 466 | ax1.set_ylabel('phase (rad)') 467 | ax2.set_ylabel('imagl') 468 | fr_ann = ax3.annotate('fr = %e Hz +- %e Hz' % (self.fitresults['fr'],self.fitresults['fr_err']),xy=(0.1, 0.8), xycoords='axes fraction') 469 | Ql_ann = ax3.annotate('Ql = %e +- %e' % (self.fitresults['Ql'],self.fitresults['Ql_err']),xy=(0.1, 0.6), xycoords='axes fraction') 470 | Qc_ann = ax3.annotate('Qc = %e +- %e' % (self.fitresults['absQc'],self.fitresults['absQc_err']),xy=(0.1, 0.4), xycoords='axes fraction') 471 | Qi_ann = ax3.annotate('Qi = %e +- %e' % (self.fitresults['Qi_dia_corr'],self.fitresults['Qi_dia_corr_err']),xy=(0.1, 0.2), xycoords='axes fraction') 472 | axcolor = 'lightgoldenrodyellow' 473 | axdelay = plt.axes([0.25, 0.05, 0.65, 0.03], facecolor=axcolor) 474 | axf2 = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor) 475 | axf1 = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor) 476 | sscale = 10. 477 | sdelay = Slider(axdelay, 'delay', -1., 1., valinit=self.__delay/(sscale*self.__delay),valfmt='%f') 478 | df = (fmax-fmin)*0.05 479 | sf2 = Slider(axf2, 'f2', (fmin-df)*1e-9, (fmax+df)*1e-9, valinit=fmax*1e-9,valfmt='%.10f GHz') 480 | sf1 = Slider(axf1, 'f1', (fmin-df)*1e-9, (fmax+df)*1e-9, valinit=fmin*1e-9,valfmt='%.10f GHz') 481 | def update(val): 482 | self.autofit(electric_delay=sdelay.val*sscale*self.__delay,fcrop=(sf1.val*1e9,sf2.val*1e9)) 483 | l0.set_data(self.f_data*1e-9,np.absolute(self.z_data)) 484 | l1.set_data(self.f_data*1e-9,np.angle(self.z_data)) 485 | l2.set_data(np.real(self.z_data),np.imag(self.z_data)) 486 | l0s.set_data(self.f_data[self._fid]*1e-9,np.absolute(self.z_data_sim_norm[self._fid])) 487 | l1s.set_data(self.f_data[self._fid]*1e-9,np.angle(self.z_data_sim_norm[self._fid])) 488 | l2s.set_data(np.real(self.z_data_sim_norm[self._fid]),np.imag(self.z_data_sim_norm[self._fid])) 489 | fr_ann.set_text('fr = %e Hz +- %e Hz' % (self.fitresults['fr'],self.fitresults['fr_err'])) 490 | Ql_ann.set_text('Ql = %e +- %e' % (self.fitresults['Ql'],self.fitresults['Ql_err'])) 491 | Qc_ann.set_text('|Qc| = %e +- %e' % (self.fitresults['absQc'],self.fitresults['absQc_err'])) 492 | Qi_ann.set_text('Qi_dia_corr = %e +- %e' % (self.fitresults['Qi_dia_corr'],self.fitresults['Qi_dia_corr_err'])) 493 | fig.canvas.draw_idle() 494 | def btnclicked(event): 495 | self.autofit(electric_delay=None,fcrop=(sf1.val*1e9,sf2.val*1e9)) 496 | self.__delay = self._delay 497 | sdelay.reset() 498 | update(event) 499 | sf1.on_changed(update) 500 | sf2.on_changed(update) 501 | sdelay.on_changed(update) 502 | btnax = plt.axes([0.05, 0.1, 0.1, 0.04]) 503 | button = Button(btnax, 'auto-delay', color=axcolor, hovercolor='0.975') 504 | button.on_clicked(btnclicked) 505 | plt.show() 506 | plt.close() 507 | 508 | def _S21_notch(self,f,fr=10e9,Ql=900,Qc=1000.,phi=0.,a=1.,alpha=0.,delay=.0): 509 | ''' 510 | full model for notch type resonances 511 | ''' 512 | return a*np.exp(complex(0,alpha))*np.exp(-2j*np.pi*f*delay)*(1.-Ql/Qc*np.exp(1j*phi)/(1.+2j*Ql*(f-fr)/fr)) 513 | 514 | def get_single_photon_limit(self,unit='dBm',diacorr=True): 515 | ''' 516 | returns the amout of power in units of W necessary 517 | to maintain one photon on average in the cavity 518 | unit can be 'dBm' or 'watt' 519 | ''' 520 | if self.fitresults!={}: 521 | fr = self.fitresults['fr'] 522 | if diacorr: 523 | k_c = 2*np.pi*fr/self.fitresults['Qc_dia_corr'] 524 | k_i = 2*np.pi*fr/self.fitresults['Qi_dia_corr'] 525 | else: 526 | k_c = 2*np.pi*fr/self.fitresults['absQc'] 527 | k_i = 2*np.pi*fr/self.fitresults['Qi_no_corr'] 528 | if unit=='dBm': 529 | return Watt2dBm(1./(4.*k_c/(2.*np.pi*hbar*fr*(k_c+k_i)**2))) 530 | elif unit=='watt': 531 | return 1./(4.*k_c/(2.*np.pi*hbar*fr*(k_c+k_i)**2)) 532 | else: 533 | warnings.warn('Please perform the fit first',UserWarning) 534 | return None 535 | 536 | def get_photons_in_resonator(self,power,unit='dBm',diacorr=True): 537 | ''' 538 | returns the average number of photons 539 | for a given power in units of W 540 | unit can be 'dBm' or 'watt' 541 | ''' 542 | if self.fitresults!={}: 543 | if unit=='dBm': 544 | power = dBm2Watt(power) 545 | fr = self.fitresults['fr'] 546 | if diacorr: 547 | k_c = 2*np.pi*fr/self.fitresults['Qc_dia_corr'] 548 | k_i = 2*np.pi*fr/self.fitresults['Qi_dia_corr'] 549 | else: 550 | k_c = 2*np.pi*fr/self.fitresults['absQc'] 551 | k_i = 2*np.pi*fr/self.fitresults['Qi_no_corr'] 552 | return 4.*k_c/(2.*np.pi*hbar*fr*(k_c+k_i)**2) * power 553 | else: 554 | warnings.warn('Please perform the fit first',UserWarning) 555 | return None 556 | 557 | class transmission_port(circlefit,save_load,plotting): 558 | ''' 559 | a class for handling transmission measurements 560 | ''' 561 | 562 | def __init__(self,f_data=None,z_data_raw=None): 563 | self.porttype = 'transm' 564 | self.fitresults = {} 565 | if f_data is not None: 566 | self.f_data = np.array(f_data) 567 | else: 568 | self.f_data=None 569 | if z_data_raw is not None: 570 | self.z_data_raw = np.array(z_data_raw) 571 | else: 572 | self.z_data=None 573 | 574 | def _S21(self,f,fr,Ql,A): 575 | return A**2/(1.+4.*Ql**2*((f-fr)/fr)**2) 576 | 577 | def fit(self): 578 | self.ampsqr = (np.absolute(self.z_data_raw))**2 579 | p = [self.f_data[np.argmax(self.ampsqr)],1000.,np.amax(self.ampsqr)] 580 | popt, pcov = spopt.curve_fit(self._S21, self.f_data, self.ampsqr,p) 581 | errors = np.sqrt(np.diag(pcov)) 582 | self.fitresults = {'fr':popt[0],'fr_err':errors[0],'Ql':popt[1],'Ql_err':errors[1],'Ampsqr':popt[2],'Ampsqr_err':errors[2]} 583 | 584 | class resonator(object): 585 | ''' 586 | Universal resonator analysis class 587 | It can handle different kinds of ports and assymetric resonators. 588 | ''' 589 | def __init__(self, ports = {}, comment = None): 590 | ''' 591 | initializes the resonator class object 592 | ports (dictionary {key:value}): specify the name and properties of the coupling ports 593 | e.g. ports = {'1':'direct', '2':'notch'} 594 | comment: add a comment 595 | ''' 596 | self.comment = comment 597 | self.port = {} 598 | self.transm = {} 599 | if len(ports) > 0: 600 | for key, pname in iter(ports.items()): 601 | if pname=='direct': 602 | self.port.update({key:reflection_port()}) 603 | elif pname=='notch': 604 | self.port.update({key:notch_port()}) 605 | else: 606 | warnings.warn("Undefined input type! Use 'direct' or 'notch'.", SyntaxWarning) 607 | if len(self.port) == 0: warnings.warn("Resonator has no coupling ports!", UserWarning) 608 | 609 | def add_port(self,key,pname): 610 | if pname=='direct': 611 | self.port.update({key:reflection_port()}) 612 | elif pname=='notch': 613 | self.port.update({key:notch_port()}) 614 | else: 615 | warnings.warn("Undefined input type! Use 'direct' or 'notch'.", SyntaxWarning) 616 | if len(self.port) == 0: warnings.warn("Resonator has no coupling ports!", UserWarning) 617 | 618 | def delete_port(self,key): 619 | del self.port[key] 620 | if len(self.port) == 0: warnings.warn("Resonator has no coupling ports!", UserWarning) 621 | 622 | def get_Qi(self): 623 | ''' 624 | based on the number of ports and the corresponding measurements 625 | it calculates the internal losses 626 | ''' 627 | pass 628 | 629 | def get_single_photon_limit(self,port): 630 | ''' 631 | returns the amout of power necessary to maintain one photon 632 | on average in the cavity 633 | ''' 634 | pass 635 | 636 | def get_photons_in_resonator(self,power,port): 637 | ''' 638 | returns the average number of photons 639 | for a given power 640 | ''' 641 | pass 642 | 643 | def add_transm_meas(self,port1, port2): 644 | ''' 645 | input: port1 646 | output: port2 647 | adds a transmission measurement 648 | connecting two direct ports S21 649 | ''' 650 | key = port1 + " -> " + port2 651 | self.port.update({key:transm()}) 652 | pass 653 | 654 | 655 | class batch_processing(object): 656 | ''' 657 | A class for batch processing of resonator data as a function of another variable 658 | Typical applications are power scans, magnetic field scans etc. 659 | ''' 660 | 661 | def __init__(self,porttype): 662 | ''' 663 | porttype = 'notch', 'direct', 'transm' 664 | results is an array of dictionaries containing the fitresults 665 | ''' 666 | self.porttype = porttype 667 | self.results = [] 668 | 669 | def autofit(self,cal_dataslice = 0): 670 | ''' 671 | fits all data 672 | cal_dataslice: choose scatteringdata which should be used for calibration 673 | of the amplitude and phase, default = 0 (first) 674 | ''' 675 | pass 676 | 677 | class coupled_resonators(batch_processing): 678 | ''' 679 | A class for fitting a resonator coupled to a second one 680 | ''' 681 | 682 | def __init__(self,porttype): 683 | self.porttype = porttype 684 | self.results = [] 685 | 686 | #def GUIfit(porttype,f_data,z_data_raw): 687 | # ''' 688 | # GUI based fitting process enabeling cutting the data and manually setting the delay 689 | # It employs the Matplotlib widgets 690 | # return f1, f2 and delay, which should be employed for the real fitting 691 | # ''' 692 | # if porttype=='direct': 693 | # p = reflection_port(f_data=f_data,z_data_raw=z_data_raw) 694 | # elif porttype =='notch': 695 | # p = notch_port(f_data=f_data,z_data_raw=z_data_raw) 696 | # else: 697 | # warnings.warn('Not supported!') 698 | # return None 699 | # import matplotlib.pyplot as plt 700 | # from matplotlib.widgets import Slider, Button, RadioButtons 701 | # #plt.style.use('ggplot') 702 | # fig, axes = plt.subplots(nrows=2,ncols=2) 703 | # 704 | # return f1,f2,delay -------------------------------------------------------------------------------- /resonator_tools/noise.py: -------------------------------------------------------------------------------- 1 | 2 | ###### 3 | ## Functions to evaluate noise data 4 | ###### 5 | 6 | import warnings 7 | import numpy as np 8 | from scipy.signal import periodogram 9 | 10 | 11 | class noisedata(object): 12 | 13 | def __init__(self,IQ,IQref,fr,Ql,fs,gain_corr=[1.,1.],Z=50): 14 | ''' 15 | units are assumed to be in volts 16 | -> IQ = I+1j*Q ; with amplitude signal on Q and phase on I 17 | this signal is measured on resonance 18 | -> IQref = Iref+1j*Qref ; with amplitude signal on Qref and phase on Iref 19 | this signal is measured far off resonance 20 | IMPORTANT: IQ and IQref describe signals on opposite sides of the resonance circle 21 | Therefore, take care that Q and Qref have the correct signs in order that 22 | the program can determine the diameter of the resonance circle. 23 | -> fr: resonance frequency 24 | -> Ql: loaded Q of the resonator 25 | -> fs: sampling rate 26 | -> gain_corr = [1.,1.] ; enter here if the gain of IQ and IQref signals 27 | are different 28 | -> Z: impedance 29 | The signals will be normalized to the reference such that IQref = 1. 30 | ''' 31 | self.Z = Z 32 | self.fr = fr 33 | self.Ql = Ql 34 | self.offrespoint = np.mean(np.imag(IQref)) 35 | self.respoint = np.mean(np.imag(IQref)) 36 | self.radius = (self.offrespoint - self.respoint)/self.offrespoint 37 | self.P_I = periodogram(self._demean(np.real(IQ)),fs=fs) 38 | self.P_Q = periodogram(self._demean(np.imag(IQ)),fs=fs) 39 | self.P_Iref = periodogram(self._demean(np.real(IQref)),fs=fs) 40 | self.P_Qref = periodogram(self._demean(np.imag(IQref)),fs=fs) 41 | 42 | ################################# 43 | #functions to evalate multiple things 44 | def P_I_eval_all(self): 45 | ''' 46 | returns a 2D numpy array with all the results 47 | and a 1D list with the description 48 | ''' 49 | comment = ['P_I','P_Inorm','P_Ipower','P_dtheta','P_dphi','P_df','P_'] 50 | return np.vstack((self.P_I,self.P_Inorm(),self.P_Ipower(),self.P_dtheta(),self.P_dphi(),self.P_df(),self.P_())), comment 51 | 52 | def P_Iref_eval_all(self): 53 | ''' 54 | returns a 2D numpy array with all the results 55 | and a 1D list with the description 56 | ''' 57 | comment = ['P_Iref','P_Irefnorm','P_Irefpower','P_refdtheta','P_refdphi','P_refdf','P_ref'] 58 | return np.vstack((self.P_Iref,self.P_Irefnorm(),self.P_Irefpower(),self.P_refdtheta(),self.P_refdphi(),self.P_refdf(),self.P_ref())), comment 59 | 60 | def P_Q_eval_all(self): 61 | ''' 62 | returns a 2D numpy array with all the results 63 | and a 1D list with the description 64 | ''' 65 | comment = ['P_Q','P_Qnorm','P_Qpower'] 66 | return np.vstack((self.P_Q,self.P_Qnorm(),self.P_Qpower())), comment 67 | 68 | def P_Qref_eval_all(self): 69 | ''' 70 | returns a 2D numpy array with all the results 71 | and a 1D list with the description 72 | ''' 73 | comment = ['P_Qref','P_Qrefnorm','P_Qrefpower'] 74 | return np.vstack((self.P_Qref,self.P_Qrefnorm(),self.P_Qrefpower())), comment 75 | 76 | ################################# 77 | #helpers 78 | def _demean(self,x): 79 | ''' 80 | removes the mean value from x 81 | ''' 82 | return x - x.mean() 83 | 84 | 85 | ################################# 86 | #noise on I 87 | 88 | def P_Inorm(self): 89 | ''' 90 | V^2/Hz 91 | ''' 92 | return self.P_I/(self.offrespoint**2) 93 | 94 | def P_Ipower(self): 95 | ''' 96 | W/Hz 97 | ''' 98 | return self.P_I/self.Z 99 | 100 | def P_dtheta(self): 101 | ''' 102 | rad^2/Hz 103 | phase noise on the resonator circle phase 104 | (this is not the real measured phase) 105 | ''' 106 | return self.P_Inorm()/self.r**2 107 | 108 | def P_dphi(self): 109 | ''' 110 | rad^2/Hz 111 | phase noise on the phase measured with the VNA 112 | ''' 113 | return self.P_Inorm()/np.absolute(self.respoint**2) 114 | 115 | def P_df(self): 116 | ''' 117 | Hz^2/Hz 118 | frequency noise 119 | ''' 120 | return self.P_theta() * self.fr**2 / (16.*self.Ql**2) 121 | 122 | def P_(self): 123 | ''' 124 | 1/Hz 125 | fractional frequency noise 126 | ''' 127 | return self.P_theta() / (16.*self.Ql**2) 128 | 129 | ################################# 130 | #noise on Iref 131 | 132 | def P_Irefnorm(self): 133 | ''' 134 | V^2/Hz 135 | ''' 136 | return self.P_Iref/(self.offrespoint**2) 137 | 138 | def P_Irefpower(self): 139 | ''' 140 | W/Hz 141 | ''' 142 | return self.P_Iref/self.Z 143 | 144 | def P_refdtheta(self): 145 | ''' 146 | rad^2/Hz 147 | phase noise on the resonator circle phase 148 | (this is not the real measured phase) 149 | ''' 150 | return self.P_Irefnorm()/self.r**2 151 | 152 | def P_refdphi(self): 153 | ''' 154 | rad^2/Hz 155 | phase noise on the phase measured with the VNA 156 | ''' 157 | return self.P_Irefnorm()/np.absolute(self.respoint**2) 158 | 159 | def P_refdf(self): 160 | ''' 161 | Hz^2/Hz 162 | frequency noise 163 | ''' 164 | return self.P_reftheta() * self.fr**2 / (16.*self.Ql**2) 165 | 166 | def P_ref(self): 167 | ''' 168 | 1/Hz 169 | fractional frequency noise 170 | ''' 171 | return self.P_reftheta() / (16.*self.Ql**2) 172 | 173 | ################################# 174 | #noise on Q 175 | 176 | def P_Qnorm(self): 177 | ''' 178 | V^2/Hz 179 | ''' 180 | return self.P_Q/(self.offrespoint**2) 181 | 182 | def P_Qpower(self): 183 | ''' 184 | W/Hz 185 | ''' 186 | return self.P_Q/self.Z 187 | 188 | ################################# 189 | #noise on Qref 190 | 191 | def P_Qrefnorm(self): 192 | ''' 193 | V^2/Hz 194 | ''' 195 | return self.P_Qref/(self.offrespoint**2) 196 | 197 | def P_Qrefpower(self): 198 | ''' 199 | W/Hz 200 | ''' 201 | return self.P_Qref/self.Z 202 | 203 | -------------------------------------------------------------------------------- /resonator_tools/utilities.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | def Watt2dBm(x): 6 | ''' 7 | converts from units of watts to dBm 8 | ''' 9 | return 10.*np.log10(x*1000.) 10 | 11 | def dBm2Watt(x): 12 | ''' 13 | converts from units of watts to dBm 14 | ''' 15 | return 10**(x/10.) /1000. 16 | 17 | class plotting(object): 18 | ''' 19 | some helper functions for plotting 20 | ''' 21 | def plotall(self): 22 | real = self.z_data_raw.real 23 | imag = self.z_data_raw.imag 24 | real2 = self.z_data_sim.real 25 | imag2 = self.z_data_sim.imag 26 | plt.subplot(221) 27 | plt.plot(real,imag,label='rawdata') 28 | plt.plot(real2,imag2,label='fit') 29 | plt.xlabel('Re(S21)') 30 | plt.ylabel('Im(S21)') 31 | plt.legend() 32 | plt.subplot(222) 33 | plt.plot(self.f_data*1e-9,np.absolute(self.z_data_raw),label='rawdata') 34 | plt.plot(self.f_data*1e-9,np.absolute(self.z_data_sim),label='fit') 35 | plt.xlabel('f (GHz)') 36 | plt.ylabel('|S21|') 37 | plt.legend() 38 | plt.subplot(223) 39 | plt.plot(self.f_data*1e-9,np.angle(self.z_data_raw),label='rawdata') 40 | plt.plot(self.f_data*1e-9,np.angle(self.z_data_sim),label='fit') 41 | plt.xlabel('f (GHz)') 42 | plt.ylabel('arg(|S21|)') 43 | plt.legend() 44 | plt.show() 45 | 46 | def plotcalibrateddata(self): 47 | real = self.z_data.real 48 | imag = self.z_data.imag 49 | plt.subplot(221) 50 | plt.plot(real,imag,label='rawdata') 51 | plt.xlabel('Re(S21)') 52 | plt.ylabel('Im(S21)') 53 | plt.legend() 54 | plt.subplot(222) 55 | plt.plot(self.f_data*1e-9,np.absolute(self.z_data),label='rawdata') 56 | plt.xlabel('f (GHz)') 57 | plt.ylabel('|S21|') 58 | plt.legend() 59 | plt.subplot(223) 60 | plt.plot(self.f_data*1e-9,np.angle(self.z_data),label='rawdata') 61 | plt.xlabel('f (GHz)') 62 | plt.ylabel('arg(|S21|)') 63 | plt.legend() 64 | plt.show() 65 | 66 | def plotrawdata(self): 67 | real = self.z_data_raw.real 68 | imag = self.z_data_raw.imag 69 | plt.subplot(221) 70 | plt.plot(real,imag,label='rawdata') 71 | plt.xlabel('Re(S21)') 72 | plt.ylabel('Im(S21)') 73 | plt.legend() 74 | plt.subplot(222) 75 | plt.plot(self.f_data*1e-9,np.absolute(self.z_data_raw),label='rawdata') 76 | plt.xlabel('f (GHz)') 77 | plt.ylabel('|S21|') 78 | plt.legend() 79 | plt.subplot(223) 80 | plt.plot(self.f_data*1e-9,np.angle(self.z_data_raw),label='rawdata') 81 | plt.xlabel('f (GHz)') 82 | plt.ylabel('arg(|S21|)') 83 | plt.legend() 84 | plt.show() 85 | 86 | class save_load(object): 87 | ''' 88 | procedures for loading and saving data used by other classes 89 | ''' 90 | def _ConvToCompl(self,x,y,dtype): 91 | ''' 92 | dtype = 'realimag', 'dBmagphaserad', 'linmagphaserad', 'dBmagphasedeg', 'linmagphasedeg' 93 | ''' 94 | if dtype=='realimag': 95 | return x+1j*y 96 | elif dtype=='linmagphaserad': 97 | return x*np.exp(1j*y) 98 | elif dtype=='dBmagphaserad': 99 | return 10**(x/20.)*np.exp(1j*y) 100 | elif dtype=='linmagphasedeg': 101 | return x*np.exp(1j*y/180.*np.pi) 102 | elif dtype=='dBmagphasedeg': 103 | return 10**(x/20.)*np.exp(1j*y/180.*np.pi) 104 | else: warnings.warn("Undefined input type! Use 'realimag', 'dBmagphaserad', 'linmagphaserad', 'dBmagphasedeg' or 'linmagphasedeg'.", SyntaxWarning) 105 | 106 | def add_data(self,f_data,z_data): 107 | self.f_data = np.array(f_data) 108 | self.z_data_raw = np.array(z_data) 109 | 110 | def cut_data(self,f1,f2): 111 | def findpos(f_data,val): 112 | pos = 0 113 | for i in range(len(f_data)): 114 | if f_data[i]