app: stacked_cores: plot what happens when one cascades an inverter into a buffer

This commit is contained in:
colin 2022-10-15 23:26:10 -07:00
parent d03818b58e
commit 33b0b76278
3 changed files with 55 additions and 12 deletions

View File

@ -1,9 +1,11 @@
import plotly.express as px
from pandas import DataFrame
# df = pd.DataFrame(data=dict(x=[1, 2, 3, 4], y=[1, 4, 9, 16]))
# fig = px.line(df, x="x", y="y", title="test")
# fig.show()
from math import floor
def extrema(arr):
arr = list(arr)
return min(arr), max(arr)
class Line:
def __init__(self, from_, to):
@ -74,6 +76,14 @@ class Piecewise:
(x, min(max_, max(min_, y))) for (x, y) in self.xy
])
def cascaded(self, second: 'Piecewise') -> 'Piecewise':
""" return a function equivalent to y = second(self(x)) """
b_min, b_max = extrema(x for (x, y) in self.xy)
bounds = [ int(floor(b_min * 100)), 1 + int(floor(b_max * 100)) ]
return Piecewise([
(0.01 * x, second.get(self.get(0.01 * x))) for x in range(*bounds)
])
def line_for(self, x: float) -> Line:
for first_lower in self.xy[:-1][::-1]:
if first_lower[0] < x: break
@ -113,6 +123,9 @@ class Piecewise:
def get_integral(self, x: float, n: int = 100) -> float:
return self.get_mean(x) * x
def get_range(self, xmin: float = 0.0, xmax: float = 1.0) -> float:
return abs(self.get(xmax) - self.get(xmin))
def df_for(self, from_: float, to: float, points: int, f) -> DataFrame:
x_step = (to - from_) / (points - 1)
x = [from_ + x_step*x for x in range(points)]

View File

@ -3,6 +3,20 @@
from fake_cores_db import *
from stacked_cores_40xx_db import *
class SimParamsCascaded(SimParams):
def __init__(self, p1: SimParams, p2: SimParams):
super().__init__(p1.couplings, p1.wrappings_spec, p1.um, p1.drive_str)
self.p1 = p1
self.p2 = p2
@property
def is_inverter(self) -> bool:
return self.p1.is_inverter ^ self.p2.is_inverter
@property
def human_name(self) -> str:
return f"Cascade: {self.p1.human_name} -> {self.p2.human_name}"
# plot pre-40xx sims
for (name, curve) in [
@ -118,18 +132,25 @@ of_interest += [(p, get_meas(p)) for p in
# plot all viable inverters
of_interest += filter_meas(run="41", viable_inverter=True)
# of_interest += filter_meas(run="41", viable_inverter=True)
# of_interest += filter_meas(run="42", rad_um=400, couplings=4)
# of_interest += filter_meas(run="42", rad_um=400, couplings=9)
# of_interest += filter_meas(run="42", rad_um=400, couplings=2)
# of_interest += filter_meas(run="42", rad_um=400, couplings=6)
# of_interest += filter_meas(run="43")
of_interest.sort(key=lambda i: -i[1].max_abs_slope())
for (inv_p, inv_curve) in filter_meas(is_inverter=True):
for (fwd_p, fwd_curve) in filter_meas(rad_um=400, is_inverter=False):
of_interest += [ (SimParamsCascaded(inv_p, fwd_p), inv_curve.cascaded(fwd_curve)) ]
for (params, curve) in of_interest:
# of_interest.sort(key = lambda i: -i[1].max_abs_slope())
# of_interest.sort(key = lambda i: -i[1].get_range())
of_interest.sort(key = lambda i: i[1].get_repeated(1.0) - i[1].get_repeated(0.0)) # inverter strength
for (params, curve) in of_interest[:5]:
curve.plot(title = f"{params.human_name} mapping")
curve.plot_slope(title = f"{params.human_name} slope")
if not params.is_inverter:
curve = curve.logically_inverted()
curve.plot(title = f"{params.human_name} mapping")
curve.logically_inverted().plot_slope(title = f"{params.human_name} slope")
curve.plot_equilibrium(title = f"{params.human_name} equilibrium")

View File

@ -57,9 +57,9 @@ class SimParams:
@property
def tuple(self):
return (self.run, self.um, self.couplings, self.wrappings, self.drive)
return (self.run, self.um, self.couplings, self.wrappings, self.is_inverter, self.drive)
def matches(self, run: str, rad_um: int, drive: float, couplings: int, wrappings: int) -> bool:
def matches(self, run: str, rad_um: int, drive: float, couplings: int, wrappings: int, is_inverter: bool) -> bool:
"""
all parameters are optional
"""
@ -68,6 +68,7 @@ class SimParams:
self.um if rad_um is None else rad_um,
self.couplings if couplings is None else couplings,
self.wrappings if wrappings is None else wrappings,
self.is_inverter if is_inverter is None else is_inverter,
self.drive if drive is None else int(drive),
)
return self.tuple == match_tuple
@ -253,7 +254,15 @@ measurements = { real.machine_name: [human, norm, None, None] for (real, human,
def matches(actual, expected) -> bool:
return expected == None or actual == expected
def filter_meas(run: str = None, rad_um: int = None, drive: float = None, couplings: int = None, wrappings: int = None, viable_inverter = None) -> list:
def filter_meas(
run: str = None,
rad_um: int = None,
drive: float = None,
couplings: int = None,
wrappings: int = None,
viable_inverter = None,
is_inverter = None,
) -> list:
"""
return only that set of (SimParams, Piecewise) which matches the given criteria.
leave any option as `None` to not filter on it.
@ -262,7 +271,7 @@ def filter_meas(run: str = None, rad_um: int = None, drive: float = None, coupli
return sorted(
[
(p, get_meas(p)) for (p, _, _) in sims
if p.matches(run, rad_um, drive, couplings, wrappings) \
if p.matches(run, rad_um, drive, couplings, wrappings, is_inverter) \
and get_meas(p) \
and get_meas(p).num_pieces > 0 \
and matches(p.ensure_inverter(get_meas(p)).is_viable_inverter(), viable_inverter)