...
 
Commits (9)
......@@ -34,9 +34,13 @@ Noise spectral densities
Further modifying plots generated by |Zero|
-------------------------------------------
If you wish to apply further styling to a plot generated by |Zero|, you can do so using
`Matplotlib <https://matplotlib.org/>`__ method calls on the :class:`~matplotlib.figure.Figure`
produced by or provided to the :ref:`plotting methods <plotting/index:Plotting in analysis scripts>`.
If you wish to apply further styling to a plot generated by |Zero|, you can do so using `Matplotlib
<https://matplotlib.org/>`__ method calls on the :attr:`~.MatplotlibPlotter.figure` property of the
:class:`plotter <.MatplotlibPlotter>` object.
You can also set the :class:`.Solution`'s :attr:`~.Solution.response_plotter` and
:attr:`~.Solution.noise_plotter` attributes to custom :class:`.BaseGroupPlotter` implementations to
provide full control over plotting capabilities.
Plotting from the command line
------------------------------
......
......@@ -269,8 +269,8 @@ of an RF summing box with two inputs and one output:
.. hint::
The above example makes a call to :meth:`.plot`. This relies on :ref:`default functions
<solution/index:Default functions>` having been set, in this case by the :ref:`LISO
The above example makes a call to :meth:`~.Solution.plot`. This relies on :ref:`default
functions <solution/index:Default functions>` having been set, in this case by the :ref:`LISO
compatibility module <liso/index:LISO compatibility>`, which is normally not the case when a
circuit is constructed and simulated natively. In such cases, calls to :meth:`.plot_responses`
and :meth:`.plot_noise` with filter parameters are usually required.
......
......@@ -28,5 +28,5 @@ if __name__ == "__main__":
solution = analysis.calculate(frequencies=frequencies, input_type="voltage", node="n1")
# Plot.
solution.plot_responses(sinks=["nm", "nout", "op1"])
solution.show()
plot = solution.plot_responses(sinks=["nm", "nout", "op1"])
plot.show()
......@@ -35,5 +35,5 @@ if __name__ == "__main__":
solution.add_response_reference(frequencies, np.ones_like(frequencies), label="Unity gain")
# Plot, scaling in absolute units.
solution.plot_responses(sink="nout", scale_db=False)
solution.show()
plot = solution.plot_responses(sink="nout", scale_db=False)
plot.show()
......@@ -33,5 +33,5 @@ if __name__ == "__main__":
noise_sum.label = "Total noise"
# Plot.
solution.plot_noise(sink="nout")
solution.show()
plot = solution.plot_noise(sink="nout")
plot.show()
......@@ -31,5 +31,5 @@ if __name__ == "__main__":
"op-amps": "allop"})
# Plot.
solution.plot_noise(sink="nout")
solution.show()
plot = solution.plot_noise(sink="nout")
plot.show()
......@@ -38,5 +38,5 @@ if __name__ == "__main__":
solution.add_noise_reference(frequencies, shot_noise(frequencies, 1e-3), label="Shot noise")
# Plot. Note that the sink is now the input, since we projected the noise there.
solution.plot_noise(sink="input", ylim=(1e-14, 1e-9), title="Photodetector noise")
solution.show()
plot = solution.plot_noise(sink="input", ylim=(1e-14, 1e-9), title="Photodetector noise")
plot.show()
......@@ -38,5 +38,6 @@ if __name__ == "__main__":
solution.scale_noise(pd_to_displacement, sink="input")
# Plot. Note that the sink is now the input, since we projected the noise there.
solution.plot_noise(sink="displacement", ylim=(1e-23, 1e-18), title="Photodetector noise")
solution.show()
plot = solution.plot_noise(sink="displacement", ylim=(1e-23, 1e-18),
title="Photodetector noise")
plot.show()
......@@ -47,5 +47,5 @@ if __name__ == "__main__":
solution = solution1.combine(solution2, solution3)
# Plot
solution.plot_responses(sink="nout", groups="all")
solution.show()
plot = solution.plot_responses(sink="nout", groups="all")
plot.show()
......@@ -12,6 +12,8 @@ from . import __version__, PROGRAM, DESCRIPTION, set_log_verbosity
from .solution import Solution
from .liso import LisoInputParser, LisoOutputParser, LisoRunner, LisoParserError
from .datasheet import PartRequest
from .components import OpAmp
from .display import OpAmpGainPlotter
from .config import (ZeroConfig, OpAmpLibrary, ConfigDoesntExistException,
ConfigAlreadyExistsException, LibraryQueryEngine)
......@@ -206,18 +208,18 @@ def liso(ctx, files, liso, liso_path, resp_scale_db, compare, diff, plot, save_f
if generate_plot:
if solution.has_responses:
figure = solution.plot_responses(scale_db=resp_scale_db)
plotter = solution.plot_responses(scale_db=resp_scale_db)
else:
figure = solution.plot_noise()
plotter = solution.plot_noise()
if save_figure:
for save_path in save_figure:
# NOTE: use figure file's name so that Matplotlib can identify the file type
# appropriately.
solution.save_figure(figure, save_path.name)
plotter.save(save_path.name)
if plot:
solution.show()
plotter.show()
@cli.group()
def library():
......@@ -360,6 +362,39 @@ def library_search(query, sort_a0, sort_gbw, sort_delay, sort_vnoise, sort_vcorn
writer.writerow(engine.parameters)
writer.writerows(rows)
@library.command("opamp")
@click.argument("models", type=str, nargs=-1, metavar="[MODEL]...")
@click.option("--show/--no-show", is_flag=True, default=True, show_default=True,
help="Show op-amp data.")
@click.option("--plot/--no-plot", is_flag=True, default=False,
help="Display open loop gain as figure.")
@click.option("--fstart", type=float, default=1e0, show_default=True, help="Plot start frequency.")
@click.option("--fstop", type=float, default=1e9, show_default=True, help="Plot stop frequency.")
@click.option("--npoints", type=int, default=1000, show_default=True, help="Plot number of points.")
@click.option("--save-figure", type=click.File("wb", lazy=False), multiple=True,
help="Save image of figure to file. Can be specified multiple times.")
def opamp_tools(models, show, plot, fstart, fstop, npoints, save_figure):
opamps = []
for model in models:
library_opamp = LIBRARY.get_opamp(model)
if show:
print(repr(library_opamp))
opamp = OpAmp(model=OpAmpLibrary.format_name(model), node1="input", node2="gnd",
node3="output", **LIBRARY.get_data(model))
opamps.append(opamp)
# Determine whether to generate plot.
generate_plot = plot or save_figure
if generate_plot:
plotter = OpAmpGainPlotter(fstart=fstart, fstop=fstop, npoints=npoints)
plotter.plot(opamps)
if save_figure:
for save_path in save_figure:
# NOTE: use figure file's name so that Matplotlib can identify the file type
# appropriately.
plotter.save(save_path.name)
if plot:
plotter.show()
@cli.group()
def config():
"""Zero configuration functions."""
......
......@@ -55,6 +55,24 @@ class OpAmpLibrary(BaseConfig):
"""
return str(name).upper()
def get_opamp(self, model):
"""Get op-amp by model.
Parameters
----------
model : :class:`str`
The op-amp model.
Returns
-------
:class:`.LibraryOpAmp`
The op-amp.
"""
for opamp in self.opamps:
if opamp.model.upper() == model.upper():
return opamp
raise ValueError(f"op-amp model '{model}' not found in library.")
def get_data(self, name):
"""Get op-amp data.
......@@ -448,3 +466,38 @@ class LibraryOpAmp:
def __str__(self):
return f"{self.model}(a0={self.a0}, gbw={self.gbw}, delay={self.delay})"
def __repr__(self):
def format_poles(poles):
formatted_poles = []
for mag, q in poles:
frequency = Quantity(mag, units="Hz")
if q == 0.5:
q = "real"
else:
q = f"q={q}"
formatted_poles.append(f"{frequency} ({q})")
return ", ".join(formatted_poles)
if self.poles:
poles = format_poles(self.poles_mag_q)
else:
poles = "--"
if self.zeros:
zeros = format_poles(self.zeros_mag_q)
else:
zeros = "--"
return f"""{self.model}
a0: {self.a0}
gbw: {self.gbw}
delay: {self.delay}
vnoise: {self.vnoise}
vcorner: {self.vcorner}
inoise: {self.inoise}
icorner: {self.icorner}
vmax: {self.vmax}
imax: {self.imax}
sr: {self.sr}
poles: {poles}
zeros: {zeros}
"""
......@@ -86,9 +86,10 @@ class LibraryQueryParser:
self._filters = None
# Order in which parameters have been queried.
self.parameter_query_order = []
# Create lexer and parser handlers.
self.lexer = lex.lex(module=self)
self.parser = yacc.yacc(module=self)
# Create lexer and parser handlers. Set lex and yacc to not generate grammar files, for
# packaging simplicity, at the cost of a slight speed penalty.
self.lexer = lex.lex(module=self, optimize=False, debug=False)
self.parser = yacc.yacc(module=self, write_tables=False, debug=False)
def parse(self, text):
# clear existing filters
......
This diff is collapsed.
......@@ -4,11 +4,9 @@ import sys
import os
import abc
import tempfile
import colorsys
import numpy as np
import requests
import progressbar
from matplotlib import colors
class Singleton(abc.ABCMeta):
......@@ -145,29 +143,6 @@ class Downloadable:
return filename, request
def lighten_colours(colour_cycle, factor):
"""Lightens the given color by multiplying (1 - luminosity) by the given factor.
https://stackoverflow.com/a/49601444/2251982
"""
cycle = []
for this_colour in colour_cycle:
try:
# get RGB values from hex string or name
c = colors.cnames[this_colour]
except KeyError:
c = this_colour
c = colorsys.rgb_to_hls(*colors.to_rgb(c))
new = colorsys.hls_to_rgb(c[0], 1 - factor * (1 - c[1]), c[2])
newints = tuple([int(value * 255) for value in new])
hexcode = "#%02x%02x%02x" % newints
cycle.append(hexcode)
return cycle
def db_to_mag(quantity):
return 10 ** (quantity / 20)
......
This diff is collapsed.