Beispiel #1
0
def inverterScMosfet(inputVoltage, numSolutions="all"):
    epsilon = 1e-14
    start = time.time()
    Vdd = 1.0
    sn = 3
    sp = 2 * sn

    outputVolt = Variable("outputVolt")
    iP = Variable("iP")
    iN = Variable("iN")

    allConstraints = []
    allConstraints.append(outputVolt >= 0.0)
    allConstraints.append(outputVolt <= Vdd)
    allConstraints.append(-iP - iN == 0)
    allConstraints += mvs_id("n", 0.0, inputVoltage, outputVolt, iN, sn)
    allConstraints += mvs_id("p", Vdd, inputVoltage, outputVolt, iP, sp)

    allSolutions = []
    while True:
        if numSolutions != "all" and len(allSolutions) == numSolutions:
            break

        # Store constraints pruning search space so that
        # old hyperrectangles are not considered
        excludingConstraints = []
        for solution in allSolutions:
            singleExcludingConstraints = []
            singleExcludingConstraints.append(outputVolt < solution[0][0])
            singleExcludingConstraints.append(outputVolt > solution[0][1])
            excludingConstraints.append(singleExcludingConstraints)

        #print ("allConstraints")
        #print (allConstraints)
        f_sat = logical_and(*allConstraints)
        if len(excludingConstraints) > 0:
            for constraints in excludingConstraints:
                f_sat = logical_and(f_sat, logical_or(*constraints))

        #print ("f_sat")
        #print (f_sat)
        result = CheckSatisfiability(f_sat, epsilon)
        #print (result)
        if result is None:
            break
        hyper = np.zeros((1, 2))
        hyper[0, :] = [
            result[outputVolt].lb() - 2 * epsilon,
            result[outputVolt].ub() + 2 * epsilon
        ]

        #print ("hyper", hyper)
        allSolutions.append(hyper)

        print("num solutions found", len(allSolutions))

    end = time.time()
    print("time taken", end - start)
    return allSolutions
Beispiel #2
0
 def test_type(self):
     real_var = Variable("x", Variable.Real)
     self.assertEqual(real_var.get_type(), Variable.Real)
     int_var = Variable("x", Variable.Int)
     self.assertEqual(int_var.get_type(), Variable.Int)
     bool_var = Variable("x", Variable.Bool)
     self.assertEqual(bool_var.get_type(), Variable.Bool)
Beispiel #3
0
def inverterLoopTanh(numInverters, numSolutions="all", a=-5.0):
    epsilon = 1e-14
    start = time.time()
    vs = []
    for i in range(numInverters):
        vs.append(Variable("v" + str(i)))

    allConstraints = []

    # Store rambus oscillator constraints
    for i in range(numInverters):
        allConstraints.append(vs[i] >= -1)
        allConstraints.append(vs[i] <= 1)
        inputInd = i
        outputInd = (i + 1) % numInverters
        allConstraints.append(tanh(a * vs[inputInd]) - vs[outputInd] == 0.0)

    allSolutions = []
    while True:
        if numSolutions != "all" and len(allSolutions) == numSolutions:
            break

        # Store constraints pruning search space so that
        # old hyperrectangles are not considered
        excludingConstraints = []
        for solution in allSolutions:
            singleExcludingConstraints = []
            for i in range(numInverters):
                singleExcludingConstraints.append(vs[i] < solution[i][0])
                singleExcludingConstraints.append(vs[i] > solution[i][1])
            excludingConstraints.append(singleExcludingConstraints)

        # Add all the rambus oscillator constraints
        f_sat = logical_and(*allConstraints)
        # Add constraints so that old hyperrectangles are not considered
        if len(excludingConstraints) > 0:
            for constraints in excludingConstraints:
                f_sat = logical_and(f_sat, logical_or(*constraints))

        #print ("f_sat")
        #print (f_sat)
        result = CheckSatisfiability(f_sat, epsilon)
        #print (result)
        if result is None:
            break
        hyper = np.zeros((numInverters, 2))
        for i in range(numInverters):
            hyper[i, :] = [
                result[vs[i]].lb() - 2 * epsilon,
                result[vs[i]].ub() + 2 * epsilon
            ]

        #print ("hyper", hyper)
        allSolutions.append(hyper)

        print("num solutions found", len(allSolutions))

    end = time.time()
    print("time taken", end - start)
    return allSolutions
Beispiel #4
0
    def test_if_then_else(self):
        b = Variable("b", Variable.Bool)
        e = if_then_else(b, 5.0, 3.0)
        self.assertEqual(str(e), "(if b then 5 else 3)")

        with self.assertRaises(Exception) as context:
            if_then_else(x, 5.0, 3.0)
        print(context.exception)
        self.assertTrue("not a Boolean variable but used as a conditional" in
                        str(context.exception))
Beispiel #5
0
def exampleFun(numSolutions="all"):
    start = time.time()
    epsilon = 1e-14
    lenV = 1
    x = Variable('x')

    allSolutions = []
    while True:
        if numSolutions != "all" and len(allSolutions) == numSolutions:
            break
        allConstraints = []
        allConstraints.append(2 * x * asin(cos(0.797) * sin(math.pi / x)) -
                              0.0331 * x >= 2 * math.pi - 2.097)
        allConstraints.append(x >= 3)
        allConstraints.append(x <= 64)
        excludingConstraints = []
        for solution in allSolutions:
            singleExcludingConstraints = []
            singleExcludingConstraints.append(x <= solution[0][0])
            singleExcludingConstraints.append(x >= solution[0][1])
            excludingConstraints.append(singleExcludingConstraints)

        #print ("allConstraints")
        #print (allConstraints)
        #print ("numConstraints", len(allConstraints))
        f_sat = logical_and(*allConstraints)
        if len(excludingConstraints) > 0:
            for constraints in excludingConstraints:
                f_sat = logical_and(f_sat, logical_or(*constraints))

        #print ("f_sat")
        #print (f_sat)
        result = CheckSatisfiability(f_sat, epsilon)
        #print (result)
        if result is None:
            break
        hyper = np.zeros((1, 2))
        hyper[0, :] = [
            result[x].lb() - 2 * epsilon, result[x].ub() + 2 * epsilon
        ]

        print("hyper", hyper)
        allSolutions.append(hyper)

        print("num solutions found", len(allSolutions))
    '''constraints = []
	x = Variable('x')
	#constraints.append(x >= 0.0)
	#constraints.append(x <= 64.0)
	constraints.append(2*math.pi - 2*x*asin(cos(0.797)*sin(math.pi/x)) == 2.097 - 0.0331*x)
	f_sat = logical_and(*constraints)
	result = CheckSatisfiability(f_sat, epsilon)
	print (result)'''
    end = time.time()
    print("time taken", end - start)
Beispiel #6
0
def inverterTanh(inputVoltage, a=-5.0, numSolutions="all"):
    epsilon = 1e-14
    start = time.time()

    outputVolt = Variable("outputVolt")

    allConstraints = []
    allConstraints.append(outputVolt >= -1.0)
    allConstraints.append(outputVolt <= 1.0)
    allConstraints.append(tanh(a * inputVoltage) - outputVolt == 0)

    allSolutions = []
    while True:
        if numSolutions != "all" and len(allSolutions) == numSolutions:
            break

        # Store constraints pruning search space so that
        # old hyperrectangles are not considered
        excludingConstraints = []
        for solution in allSolutions:
            singleExcludingConstraints = []
            singleExcludingConstraints.append(outputVolt < solution[0][0])
            singleExcludingConstraints.append(outputVolt > solution[0][1])
            excludingConstraints.append(singleExcludingConstraints)

        #print ("allConstraints")
        #print (allConstraints)
        f_sat = logical_and(*allConstraints)
        if len(excludingConstraints) > 0:
            for constraints in excludingConstraints:
                f_sat = logical_and(f_sat, logical_or(*constraints))

        #print ("f_sat")
        #print (f_sat)
        result = CheckSatisfiability(f_sat, epsilon)
        #print (result)
        if result is None:
            break
        hyper = np.zeros((1, 2))
        hyper[0, :] = [
            result[outputVolt].lb() - 2 * epsilon,
            result[outputVolt].ub() + 2 * epsilon
        ]

        #print ("hyper", hyper)
        allSolutions.append(hyper)

        print("num solutions found", len(allSolutions))

    end = time.time()
    print("time taken", end - start)
    return allSolutions
Beispiel #7
0
from dreal.util import Box
from dreal.util import Interval
from dreal.util import (exp, log, sqr, sqrt, pow, sin, cos, tan, asin, acos,
                        atan, atan2, sinh, cosh, tanh, root, abs, max, min,
                        sign, integer)
from dreal.symbolic import Variable
import unittest
import math

x = Variable("x")
y = Variable("y")
z = Variable("z")

inf = float("inf")


class IntervalTest(unittest.TestCase):
    def test_constructor(self):
        i1 = Interval()
        self.assertEqual(i1.lb(), -inf)
        self.assertEqual(i1.ub(), +inf)
        i2 = Interval(3, 4)
        self.assertEqual(i2.lb(), 3)
        self.assertEqual(i2.ub(), 4)
        i3 = Interval(5)
        self.assertEqual(i3.lb(), 5)
        self.assertEqual(i3.ub(), 5)

    def test_addition(self):
        i1 = Interval(3, 4)
        i2 = Interval(4, 5)
Beispiel #8
0
def schmittTriggerScMosfet(inputVoltage, numSolutions="all"):
    epsilon = 1e-14
    start = time.time()

    lenV = 3
    Vdd = 1.0
    sn = 3
    sp = 2 * sn

    vs = []
    tIs = []
    nIs = []

    for i in range(lenV):
        vs.append(Variable("v" + str(i)))
        nIs.append(Variable("nI" + str(i)))

    for i in range(lenV * 2):
        tIs.append(Variable("tI" + str(i)))

    allConstraints = []
    for i in range(lenV):
        allConstraints.append(vs[i] >= 0.0)
        allConstraints.append(vs[i] <= Vdd)
    allConstraints += mvs_id('n', 0.0, inputVoltage, vs[1], tIs[0], sn)
    allConstraints += mvs_id('n', vs[1], inputVoltage, vs[0], tIs[1], sn)
    allConstraints += mvs_id('n', vs[1], vs[0], Vdd, tIs[2], sn)
    allConstraints += mvs_id('p', Vdd, inputVoltage, vs[2], tIs[3], sp)
    allConstraints += mvs_id('p', vs[2], inputVoltage, vs[0], tIs[4], sp)
    allConstraints += mvs_id('p', vs[2], vs[0], 0.0, tIs[5], sp)
    allConstraints.append(nIs[0] == -tIs[4] - tIs[1])
    allConstraints.append(nIs[1] == -tIs[0] + tIs[1] + tIs[2])
    allConstraints.append(nIs[2] == -tIs[3] + tIs[5] + tIs[4])
    for i in range(lenV):
        allConstraints.append(nIs[i] == 0.0)

    allSolutions = []
    while True:
        if numSolutions != "all" and len(allSolutions) == numSolutions:
            break

        # Store constraints pruning search space so that
        # old hyperrectangles are not considered
        excludingConstraints = []
        for solution in allSolutions:
            singleExcludingConstraints = []
            for i in range(lenV):
                singleExcludingConstraints.append(vs[i] < solution[i][0])
                singleExcludingConstraints.append(vs[i] > solution[i][1])
            excludingConstraints.append(singleExcludingConstraints)

        #print ("allConstraints")
        #print (allConstraints)
        f_sat = logical_and(*allConstraints)
        if len(excludingConstraints) > 0:
            for constraints in excludingConstraints:
                f_sat = logical_and(f_sat, logical_or(*constraints))

        #print ("f_sat")
        #print (f_sat)
        result = CheckSatisfiability(f_sat, epsilon)
        #print (result)
        if result is None:
            break
        hyper = np.zeros((lenV, 2))
        for i in range(lenV):
            hyper[i, :] = [
                result[vs[i]].lb() - 2 * epsilon,
                result[vs[i]].ub() + 2 * epsilon
            ]

        #print ("hyper", hyper)
        allSolutions.append(hyper)

        print("num solutions found", len(allSolutions))

    end = time.time()
    print("time taken", end - start)
    return allSolutions
Beispiel #9
0
def mvs_id(fetType, Vs, Vg, Vd, I, shape):
    params = model_params(fetType)
    version = params['version']
    mType = params['mType']
    W = params['W']
    Lgdr = params['Lgdr']
    dLg = params['dLg']
    Cg = params['Cg']
    etov = params['etov']
    delta = params['delta']
    n0 = params['n0']
    Rs0 = params['Rs0']
    Rd0 = params['Rd0']
    Cif = params['Cif']
    Cof = params['Cof']
    vxo = params['vxo'] * 1e7
    mu = params['mu']
    beta = params['beta']
    Tjun = params['Tjun']
    phib = params['phib']
    gamma = params['gamma']
    Vt0 = params['Vt0']
    alpha = params['alpha']
    mc = params['mc']
    CTM_select = params['CTM_select']
    CC = params['CC']
    nd = params['nd']
    zeta = params['zeta']

    # SMALL_VALUE
    SMALL_VALUE = 1e-10
    # LARGE_VALUE
    LARGE_VALUE = 40

    if mType == 1.0:
        Vb = 0.0
        Vdsi = Variable("Vdsin")
        Vgsi = Variable("Vgsin")
        Vbsi = Variable("Vbsin")
        n = Variable("nn")
        nphit = Variable("nphitn")
        phibVbs = Variable("phibVbsn")
        Vtpcorr = Variable("Vtpcorrn")
        eVgpre = Variable("eVgpren")
        FFpre = Variable("FFpren")
        ab = Variable("abn")
        Vcorr = Variable("Vcorrn")
        Vgscorr = Variable("Vgscorrn")
        Vbscorr = Variable("Vbscorrn")
        phibVbscorr = Variable("phibVbscorrn")
        Vt0bs = Variable("Vt0bsn")
        phibVbsi = Variable("phibVbsin")
        Vt0bs0 = Variable("Vt0bs0n")
        Vtp = Variable("Vtpn")
        Vtp0 = Variable("Vtp0n")
        eVg = Variable("eVgn")
        FF = Variable("FFn")
        eVg0 = Variable("eVg0n")
        FF0 = Variable("FF0n")
        Qref = Variable("Qrefn")
        eta = Variable("etan")
        Qinv_corr = Variable("Qinv_corrn")
        Vdsat = Variable("Vdsatn")
        VdsiVdsat = Variable("VdsiVdsatn")
        powVal = Variable("powValn")
        Fsat = Variable("Fsatn")

    else:
        Vb = 1.8
        Vdsi = Variable("Vdsip")
        Vgsi = Variable("Vgsip")
        Vbsi = Variable("Vbsip")
        n = Variable("np")
        nphit = Variable("nphitp")
        phibVbs = Variable("phibVbsp")
        Vtpcorr = Variable("Vtpcorrp")
        eVgpre = Variable("eVgprep")
        FFpre = Variable("FFprep")
        ab = Variable("abp")
        Vcorr = Variable("Vcorrp")
        Vgscorr = Variable("Vgscorrp")
        Vbscorr = Variable("Vbscorrp")
        phibVbscorr = Variable("phibVbscorrp")
        Vt0bs = Variable("Vt0bsp")
        phibVbsi = Variable("phibVbsip")
        Vt0bs0 = Variable("Vt0bs0p")
        Vtp = Variable("Vtpp")
        Vtp0 = Variable("Vtp0p")
        eVg = Variable("eVgp")
        FF = Variable("FFp")
        eVg0 = Variable("eVg0p")
        FF0 = Variable("FF0p")
        Qref = Variable("Qrefp")
        eta = Variable("etap")
        Qinv_corr = Variable("Qinv_corrp")
        Vdsat = Variable("Vdsatp")
        VdsiVdsat = Variable("VdsiVdsatp")
        powVal = Variable("powValp")
        Fsat = Variable("Fsatp")

    constraints = []

    if mType == 1:
        constraints.append(
            logical_or(logical_and(Vs <= Vd, Vdsi == mType * (Vd - Vs)),
                       logical_and(Vs > Vd, Vdsi == mType * (Vs - Vd))))
        constraints.append(
            logical_or(logical_and(Vs <= Vd, Vgsi == mType * (Vg - Vs)),
                       logical_and(Vs > Vd, Vgsi == mType * (Vg - Vd))))
        constraints.append(
            logical_or(logical_and(Vs <= Vd, Vbsi == mType * (Vb - Vs)),
                       logical_and(Vs > Vd, Vbsi == mType * (Vb - Vd))))

    else:
        constraints.append(
            logical_or(logical_and(Vd <= Vs, Vdsi == mType * (Vd - Vs)),
                       logical_and(Vd > Vs, Vdsi == mType * (Vs - Vd))))
        constraints.append(
            logical_or(logical_and(Vd <= Vs, Vgsi == mType * (Vg - Vs)),
                       logical_and(Vd > Vs, Vgsi == mType * (Vg - Vd))))
        constraints.append(
            logical_or(logical_and(Vd <= Vs, Vbsi == mType * (Vb - Vs)),
                       logical_and(Vd > Vs, Vbsi == mType * (Vb - Vd))))

    Cofs = 0 * (0.345e-12 / etov) * dLg / 2.0 + Cof
    # s-terminal outer fringing cap [F/cm]
    Cofd = 0 * (0.345e-12 / etov) * dLg / 2.0 + Cof
    # d-terminal outer fringing cap [F/cm]
    Leff = Lgdr - dLg
    # Effective channel length [cm]. After subtracting overlap lengths on s and d side
    kB = 8.617e-5
    # Boltzmann constant [eV/K]
    phit = kB * Tjun
    # Thermal voltage, kT/q [V]
    me = (9.1e-31) * mc
    # Carrier mass [Kg]

    constraints.append(n == n0 + nd * Vdsi)
    constraints.append(nphit == n * phit)
    aphit = alpha * phit

    constraints.append(phibVbs == dabs(phib - Vbsi))
    constraints.append(Vtpcorr == Vt0 + gamma * (sqrt(phibVbs) - sqrt(phib)) -
                       Vdsi * delta)
    constraints.append(eVgpre == exp((Vgsi - Vtpcorr) / (aphit * 1.5)))
    constraints.append(FFpre == 1.0 / (1.0 + eVgpre))
    constraints.append(ab == 2 * (1 - 0.99 * FFpre) * phit)
    constraints.append(Vcorr == (1.0 + 2.0 * delta) * (ab / 2.0) *
                       (exp(-Vdsi / ab)))
    constraints.append(Vgscorr == Vgsi + Vcorr)
    constraints.append(Vbscorr == Vbsi + Vcorr)
    constraints.append(phibVbscorr == dabs(phib - Vbscorr))
    constraints.append(Vt0bs == Vt0 + gamma * (sqrt(phibVbscorr) - sqrt(phib)))
    constraints.append(phibVbsi == dabs(phib - Vbsi))
    constraints.append(Vt0bs0 == Vt0 + gamma * (sqrt(phibVbsi) - sqrt(phib)))
    constraints.append(Vtp == Vt0bs - Vdsi * delta - 0.5 * aphit)
    constraints.append(Vtp0 == Vt0bs0 - Vdsi * delta - 0.5 * aphit)
    constraints.append(eVg == exp((Vgscorr - Vtp) / (aphit)))
    constraints.append(FF == 1.0 / (1.0 + eVg))
    constraints.append(eVg0 == exp((Vgsi - Vtp0) / (aphit)))
    constraints.append(Qref == Cg * nphit)
    constraints.append(eta == (Vgscorr - (Vt0bs - Vdsi * delta - FF * aphit)) /
                       (nphit))
    constraints.append(Qinv_corr == Qref * log(1.0 + exp(eta)))
    vx0 = vxo
    Vdsats = vx0 * Leff / mu
    constraints.append(Vdsat == Vdsats * (1.0 - FF) + phit * FF)
    constraints.append(VdsiVdsat == Vdsi / Vdsat)
    constraints.append(powVal == pow(VdsiVdsat, beta))
    constraints.append(Fsat == VdsiVdsat / (pow((1 + powVal), (1.0 / beta))))
    if mType == 1:
        constraints.append(
            logical_or(
                logical_and(Vs <= Vd,
                            I == Qinv_corr * vx0 * Fsat * W * mType * shape),
                logical_and(
                    Vs > Vd,
                    I == Qinv_corr * vx0 * Fsat * W * mType * -1.0 * shape)))
    else:
        constraints.append(
            logical_or(
                logical_and(Vd <= Vs,
                            I == Qinv_corr * vx0 * Fsat * W * mType * shape),
                logical_and(
                    Vd > Vs,
                    I == Qinv_corr * vx0 * Fsat * W * mType * -1.0 * shape)))

    return constraints
Beispiel #10
0
    def elec_port(self,
                  name,
                  kind,
                  min_pressure=False,
                  min_flow_rate=False,
                  x=False,
                  y=False,
                  voltage=False,
                  current=False,
                  fluid_name='default'):
        """Create new electrical port where fluids and voltages can enter or exit the circuit, any
        optional tag left empty will be converted to a variable for the SMT
        solver to solve for a given value, units in brackets

        :param str name: The name of the port to use when defining channels
        :param str kind: Define if this is an 'input' or 'output' port
        :param float density: Density of fluid (kg/m^3)
        :param float min_viscosity: Viscosity of the fluid (Pa*s)
        :param float min_pressure: Pressure of the input fluid, (Pa)
        :param float min_flow_rate - flow rate of input fluid, (m^3/s)
        :param float X: x-position of port on chip schematic (m)
        :param float Y: y-position of port on chip schematic (m)
        :param float voltage: Voltage value passing through the port (V)
        :param float current: Current value passing through the port (A)
        :returns: None -- no issues with creating this port
        :raises: TypeError if an input parameter is wrong type
                 ValueError if an input parameter has an invalid value
        """
        user_provided_params = {
            name: 'string',
            min_pressure: 'positive number',
            min_flow_rate: 'positive number',
            x: 'positive number',
            y: 'positive number',
            voltage: 'number',
            current: 'positive number',
            kind: 'string',
            fluid_name: 'string'
        }
        # Checking that arguments are valid
        self.validate_params(user_provided_params, 'electrical port', name)

        if name in self.dg.nodes:
            raise ValueError("Must provide a unique name")
        if "translate_" + kind.lower() not in self.translation_strats:
            raise ValueError(
                "kind %s must be either %s" %
                ("translate_" + kind.lower(), self.translation_strats))

        # Initialize fluid properties
        fluid_properties = Fluid(fluid_name)

        # Ports are stored with nodes because ports are just a specific type of
        # node that has a constant flow rate
        # only accept ports of the right kind (input or output)
        attributes = {
            'kind': kind.lower(),
            'viscosity': Variable(name + '_viscosity'),
            'min_viscosity': fluid_properties.min_viscosity,
            'pressure': Variable(name + '_pressure'),
            'min_pressure': min_pressure,
            'flow_rate': Variable(name + '_flow_rate'),
            'min_flow_rate': min_flow_rate,
            'density': Variable(name + '_density'),
            'min_density': fluid_properties.min_density,
            'x': Variable(name + '_X'),
            'y': Variable(name + '_Y'),
            'min_x': x,
            'min_y': y,
            'voltage': voltage,
            'current': current,
        }

        # Create this node in the graph
        self.dg.add_node(name)
        # Add argument to attributes within NetworkX
        for key, attr in attributes.items():
            self.dg.nodes[name][key] = attr
        return
Beispiel #11
0
from dreal.symbolic import Variable, Variables, Expression, Formula
from dreal.symbolic import logical_not, logical_and, logical_or
from dreal.symbolic import logical_imply, logical_iff, forall
from dreal.symbolic import abs, sin, cos, tan, exp, log, sqrt
from dreal.symbolic import asin, acos, atan, sinh, cosh, tanh, atan2
from dreal.symbolic import min, max, if_then_else
from dreal.symbolic import intersect
import unittest
import math

x = Variable("x")
y = Variable("y")
z = Variable("z")
w = Variable("w")
a = Variable("a")
b = Variable("b")
c = Variable("c")
e_x = Expression(x)
e_y = Expression(y)


class SymbolicVariableTest(unittest.TestCase):
    def test_type(self):
        real_var = Variable("x", Variable.Real)
        self.assertEqual(real_var.get_type(), Variable.Real)
        int_var = Variable("x", Variable.Int)
        self.assertEqual(int_var.get_type(), Variable.Int)
        bool_var = Variable("x", Variable.Bool)
        self.assertEqual(bool_var.get_type(), Variable.Bool)

    def test_addition(self):
Beispiel #12
0
    def channel(self,
                port_from,
                port_to,
                min_length=False,
                min_width=False,
                min_height=False,
                kind='rectangle',
                phase='None',
                min_sampling_rate=1):
        """Create new connection between two nodes/ports with attributes
        consisting of the dimensions of the channel to be used to create the
        SMT equations to calculate solvability of the circuit
        Units are in brackets

        :param str port_from: Port where fluid comes into the channel from
        :param str port_to: Port at the end of the channel where fluid exits
        :param float min_length: Constrain channel to be this long (m)
        :param float width: Constrain channel to be this wide (m)
        :param float height: Constrain channel to be this wide (m)
        :param str kind: Kind of cross section of the channel (rectangle)
        :param str phase: For channels connecting to a T-junction this must be
            either continuous, dispersed or output
        :returns: None -- no issues with creating this channel
        :raises: TypeError if an input parameter is wrong type
                 ValueError if an input parameter has an invalid value
        """
        # Collection of the kinds for which there are methods to calculate their
        # channel resistance
        valid_kinds = ("rectangle")

        name = (port_from, port_to)

        user_provided_params = {
            port_from: 'string',
            port_to: 'string',
            min_length: 'positive number',
            min_width: 'positive number',
            min_height: 'positive number',
            kind: 'string',
            phase: 'string',
            min_sampling_rate: 'positive number'
        }
        # Checking that arguments are valid
        # TODO: Modify this to make it work for other channel shapes
        if kind not in valid_kinds:
            raise ValueError("Valid channel kinds are: %s" % valid_kinds)
        if kind == "rectangle":
            kind = "channel"

        self.validate_params(user_provided_params, 'Channel', name)

        if (port_from, port_to) in self.dg.edges:
            raise ValueError("Channel already exists between these nodes %s" %
                             (port_from, port_to))
        if 'translate_' + kind.lower() not in self.translation_strats:
            raise ValueError(
                "kind %s must be either %s" %
                ("translate_" + kind.lower(), self.translation_strats))

        # Add the information about that connection to another dict
        # There's extra parameters in here than in the arguments because they
        # are values calculated by later methods when creating the SMT eqns
        # Channels do not have pressure though, since it decreases linearly
        # across the channel
        attributes = {
            'kind': kind,
            'length': Variable('_'.join([*name, 'length'])),
            'min_length': min_length,
            'width': Variable('_'.join([*name, 'width'])),
            'min_width': min_width,
            'height': Variable('_'.join([*name, 'height'])),
            'min_height': min_height,
            'flow_rate': Variable('_'.join([*name, 'flow_rate'])),
            'droplet_volume': Variable('_'.join([*name, 'droplet_volume'])),
            'viscosity': Variable('_'.join([*name, 'viscosity'])),
            'resistance': Variable('_'.join([*name, 'resistance'])),
            'phase': phase.lower(),
            'port_from': port_from,
            'port_to': port_to,
            'x_detector': Variable('_'.join([*name, 'x_detector'])),
            'min_sampling_rate': min_sampling_rate
        }

        # If user provides values, put them into the attributes dictionary
        if not min_width:
            attributes['min_width'] = min_width
        if not min_length:
            attributes['min_length'] = min_length
        if not min_height:
            attributes['min_height'] = min_height

        # Create this edge in the graph
        self.dg.add_edge(*name)

        # Add argument to attributes within NetworkX
        for key, attr in attributes.items():
            self.dg.edges[port_from, port_to][key] = attr
        return
Beispiel #13
0
    def node(self, name, x=False, y=False, kind='node', c=0.4, p=0.5, qf=0.9):
        # TODO: Add ability to add features when this is  tjunc, same for channel
        """Create new node where fluids merge or split, kind of node (T-junction,
        Y-junction, cross, etc.) can be specified if not then a basic node
        connecting multiple channels will be created, units in brackets

        :param str name: Name of the node to use when connecting to a channel
        :param float x:  Set the X position of this node (m)
        :param float y:  Set the Y position of this node (m)
        :param str kind: The type of node this is, default is node, other
            option is t-junction
        :returns: None -- no issues with creating this node
        :raises: TypeError if an input parameter is wrong type
                 ValueError if an input parameter has an invalid value
        """
        user_provided_params = {
            name: 'string',
            x: 'positive number',
            y: 'positive number',
            kind: 'string',
            c: 'positive number',
            p: 'positive number',
            qf: 'positive number'
        }
        # Checking that arguments are valid
        self.validate_params(user_provided_params, 'node', name)

        if name in self.dg.nodes:
            raise ValueError("Must provide a unique name")
        if 'translate_' + kind.lower() not in self.translation_strats:
            raise ValueError(
                "kind %s must be either %s" %
                ("translate_" + kind.lower(), self.translation_strats))

        # Ports are stored with nodes because ports are just a specific type of
        # node that has a constant flow rate only accept ports of the right
        # kind (input or output)
        # While the user can't define most parameters for a node because it
        # doesnt take an input from outside the chip, they're still added
        # and set to zero so checks to each node to see if there is a min
        # value for each node doesn't raise a KeyError
        attributes = {
            'kind': kind.lower(),
            'pressure': Variable(name + '_pressure'),
            'min_pressure': None,
            'flow_rate': Variable(name + '_flow_rate'),
            'min_flow_rate': None,
            'viscosity': Variable(name + '_viscosity'),
            'min_viscosity': None,
            'density': Variable(name + '_density'),
            'min_density': None,
            'x': Variable(name + '_x'),
            'min_x': None,
            'y': Variable(name + '_y'),
            'min_y': None,
            'c': c,
            'p': p,
            'qf': qf
        }

        # If user provides values, put them into the attributes dictionary
        if x:
            attributes['min_x'] = x
        if y:
            attributes['min_y'] = y

        # Create this node in the graph
        self.dg.add_node(name)
        # Add argument to attributes within NetworkX
        for key, attr in attributes.items():
            self.dg.nodes[name][key] = attr
        return
Beispiel #14
0
# -*- coding: utf-8 -*-
from dreal.symbolic import Variable, logical_and, sin, cos
from dreal.symbolic import logical_imply, forall
from dreal.api import CheckSatisfiability, Minimize
from dreal.util import Box
from dreal.solver import Config
import math
import unittest

x = Variable("x")
y = Variable("y")
z = Variable("z")
p = Variable("p")
x0 = Variable("x0")
x1 = Variable("x1")
x2 = Variable("x2")

f_sat = logical_and(0 <= x, x <= 10, 0 <= y, y <= 10, 0 <= z, z <= 10,
                    sin(x) + cos(y) == z)

f_unsat = logical_and(3 <= x, x <= 4, 4 <= y, y <= 5, 5 <= z, z <= 6,
                      sin(x) + cos(y) == z)

objective = 2 * x * x + 6 * x + 5
constraint = logical_and(-10 <= x, x <= 10)


class ApiTest(unittest.TestCase):
    def test_delta_sat(self):
        result = CheckSatisfiability(f_sat, 0.001)
        self.assertEqual(type(result), Box)
Beispiel #15
0
def translate_ep_cross(dg, name, fluid_name='default'):
    """Create SMT expressions for an electrophoretic cross
    :param str name: the name of the junction node in the electrophoretic cross
    :returns: None -- no issues with translating channel parameters to SMT
    :raises: ValueError if the analyte_properties are not defined properly
             TypeError if the analyte_properties are not floats or ints
    """

    # work in progress
    exprs = []

    # Validate input
    if dg.size(name) != 4:
        raise ValueError("Electrophoretic Cross %s must have 4 connections" %
                         name)

    # Electrophoretic Cross is a type of node, so call translate node
    [exprs.append(val) for val in translate_node(dg, name)]

    # Because it's done in translate_tjunc
    ep_cross_node_name = name

    # figure out which nodes are for sample injection and which are for separation channel
    # assume single input node, 3 output nodes, one junction node
    # assume separation and tail channels are specified by user
    phases = nx.get_edge_attributes(dg, 'phase')
    for edge, phase in phases.items():
        # assuming only one separation channel, and only 1 tail channel
        if phase == 'separation':
            separation_channel_name = edge
            anode_node_name = edge[1]
        elif phase == 'tail':
            tail_channel_name = edge
            cathode_node_name = edge[
                edge[0] ==
                ep_cross_node_name]  # returns whichever tuple element is NOT the ep_cross node

    # is there a better way to do this?
    node_kinds = nx.get_node_attributes(dg, 'kind')
    for node, kind in node_kinds.items():
        if node not in separation_channel_name and node not in tail_channel_name:
            if kind == 'input':
                injection_channel_name = (node, ep_cross_node_name)
                injection_node_name = node  # necessary?
            elif kind == 'output':
                waste_channel_name = (ep_cross_node_name, node)
                waste_node_name = node  # necessary?

    # assert dimensions:
    # assert width and height of tail channel to be equal to separation channel
    exprs.append(
        algorithms.retrieve(dg, tail_channel_name, 'width') ==
        algorithms.retrieve(dg, separation_channel_name, 'width'))
    exprs.append(
        algorithms.retrieve(dg, tail_channel_name, 'height') ==
        algorithms.retrieve(dg, separation_channel_name, 'height'))

    # assert width and height of injection channel to be equal to waste channel
    exprs.append(
        algorithms.retrieve(dg, injection_channel_name, 'width') ==
        algorithms.retrieve(dg, waste_channel_name, 'width'))
    exprs.append(
        algorithms.retrieve(dg, injection_channel_name, 'height') ==
        algorithms.retrieve(dg, waste_channel_name, 'height'))

    # assert height of separation channel and injection channel are same
    exprs.append(
        algorithms.retrieve(dg, injection_channel_name, 'height') ==
        algorithms.retrieve(dg, separation_channel_name, 'height'))

    # electric field
    E = Variable('E')
    exprs.append(E < 1000000)
    exprs.append(E > 0)
    exprs.append(E == algorithms.calculate_electric_field(
        dg, anode_node_name, cathode_node_name))
    # only works if cathode is an input?  only works for paths that are true in directed graph

    # assume that the analyte parameters were included in the injection port
    # need to validate that the data exists?

    D = algorithms.retrieve(dg, injection_node_name, 'analyte_diffusivities')
    C0 = algorithms.retrieve(dg, injection_node_name,
                             'analyte_initial_concentrations')
    q = algorithms.retrieve(dg, injection_node_name, 'analyte_charges')
    r = algorithms.retrieve(dg, injection_node_name, 'analyte_radii')

    analyte_properties = {
        'analyte_diffusivities': D,
        'analyte_initial_concentrations': C0,
        'analyte_charges': q,
        'analyte_radii': r
    }

    for property_name, values in analyte_properties.items():
        # check if something is defined, otherwise should be set to false
        if not values:
            raise ValueError(
                'No values defined for %s in electrophoretic cross node %s' %
                (property_name, ep_cross_node_name))

        # make sure all the values are either ints or floats
        if not all(isinstance(x, (int, float)) for x in values):
            raise TypeError(
                "%s values in electrophoretic cross node '%s' must be numbers"
                % (property_name, ep_cross_node_name))

    # n = number of analytes
    n = len(D)
    for property_name, values in analyte_properties.items():
        # check that they all have the same number of values
        n_to_check = len(values)

        if not (n_to_check == n):
            raise ValueError(
                "Expecting %s values, and found %s for %s in node: '%s'" %
                (n, n_to_check, property_name, ep_cross_node_name))

    delta = algorithms.retrieve(dg, separation_channel_name,
                                'min_sampling_rate')
    x_detector = algorithms.retrieve(dg, separation_channel_name, 'x_detector')

    # These are currently set as parameters to the node function
    # all are constants, numbers between 0 and 1
    # brief descriptions:
    # lower c, more discernable concentration peaks
    # higher p, any given conc. peak must be higher (closer to max conc.)
    # qf is arbitrary, rule of thumb qf = 0.9 (called q in Stephen Chou's paper)
    c = algorithms.retrieve(dg, ep_cross_node_name, 'c')
    p = algorithms.retrieve(dg, ep_cross_node_name, 'p')
    qf = algorithms.retrieve(dg, ep_cross_node_name, 'qf')

    mu = []
    v = []
    t_peak = []
    t_min = []
    W = algorithms.retrieve(dg, injection_channel_name, 'width')

    # for each analyte
    for i in range(0, n):
        # calculate mobility
        mu.append(Variable('mu_' + str(i)))
        exprs.append(mu[i] < 10000000000)
        exprs.append(mu[i] > 0)
        exprs.append(mu[i] == algorithms.calculate_mobility(
            dg, separation_channel_name, q[i], r[i]))

        # calculate velocity
        v.append(Variable('v_' + str(i)))
        #  exprs.append(v[i] < 1)
        exprs.append(v[i] > 0)
        exprs.append(v[i] == algorithms.calculate_charged_particle_velocity(
            dg, mu[i], E))

        # calculate t_peak, initialize variables for t_min
        t_peak.append(Variable('t_peak_' + str(i)))
        t_min.append(Variable('t_min_' + str(i)))
        exprs.append(t_peak[i] < 1000000)
        exprs.append(t_peak[i] > 0)
        exprs.append(t_min[i] < 1000000)
        exprs.append(t_min[i] > 0)
        exprs.append(t_peak[i] == x_detector / v[i])

    # detector position is somewhere along the separation channel
    # assume x_detector ranges from 0 to length of channel
    # to get absolute position of detector, add x_detector to ep_cross_node position
    exprs.append(x_detector <= algorithms.retrieve(dg, separation_channel_name,
                                                   'length'))

    # C_negligible is the minimum concentration level
    # i.e. smallest concentration peak should be > C_negligible
    C_negligible = Variable('C_negligible')
    C_floor = Variable('C_floor')
    sigma0 = Variable('sigma0')

    # TODO: This equation for sigma0 is for round, should add rectangular as well
    # definition of sigma0 for round channels (sigma0 ~ r_channel/2.355)
    exprs.append(sigma0 == W / (2 * 2.355))
    exprs.append(C_floor == (min(C0) /
                             (sigma0 +
                              (2 * max(D) * x_detector / v[n - 1])**0.5)))
    exprs.append(C_negligible == p * C_floor)

    diff = []
    for i in range(0, n - 1):

        # constrain that time difference between peaks is large enough to be detected
        exprs.append(t_peak[i] + delta < t_min[i])
        exprs.append(t_peak[i] + delta < t_min[i + 1])

        # constrain t_min to be where derivative of concentration is 0
        # if two adjacent peaks are close enough in height, then instead of using
        # the differential eqn, can approximate Fi(tmin) = Fi+1(tmin)
        # where i is the current analyte, and i+1 is the next analyte
        # and F = C(x_detector), C is concentration
        # quantify closeness of heights of peaks using the variable diff
        diff.append(Variable('diff_' + str(i)))
        exprs.append(diff[i] == C0[i] / C0[i + 1] * (D[i + 1] * mu[i] /
                                                     (D[i] * mu[i + 1]))**0.5)

        # if 0.1 < diff < 10, then use expression Fi(tmin) = Fi+1(tmin)
        # otherwise use expression dFi/dt (tmin) + dFi+1/dt (tmin) = 0
        t_min_constraint_expression = if_then_else(
            logical_and(0.1 < diff[i], diff[i] < 10),
            algorithms.calculate_concentration(dg, C0[i], D[i], W, v[i],
                                               x_detector, t_min[i]) -
            algorithms.calculate_concentration(dg, C0[i + 1], D[i + 1], W,
                                               v[i + 1], x_detector, t_min[i]),
            (algorithms.calculate_concentration(
                dg, C0[i + 1], D[i + 1], W, v[i + 1], x_detector,
                t_min[i])).Differentiate(t_min[i]) +
            (algorithms.calculate_concentration(
                dg, C0[i + 1], D[i + 1], W, v[i + 1], x_detector,
                t_min[i])).Differentiate(t_min[i]))

        exprs.append(t_min_constraint_expression == 0)

        # an alternate way to define C_negligible is:
        # C_negligible < p * min(Fi(t_peaki))
        # this requires computing the concentration again, which is inefficient
        # Wrote this expression just in case; this is the more exact expression
        #  for C_negligible, in case the simpler one does not work for square
        #  channels
        # I don't know how to use the min function in dreal, so I figured an
        #  equivalent but less efficient way to do it is just to ensure it is
        #  less than Fi(t_peaki), for every i
        #  exprs.append(C_negligible < p * algorithms.calculate_concentration(dg, C0[i], D[i], W, v[i], x_detector, t_peak[i]))

        # F(tmin, i)/(F(tmax, i)) <= c
        # F(tmin, i)/F(tpeak, j) ~ ( Fi(tmin,i) + Fi+1(tmin, i) + (n-2)(1-q)/(n-3) ) / Fj(tpeak,j)
        exprs.append(
            (algorithms.calculate_concentration(dg, C0[i], D[i], W, v[i],
                                                x_detector, t_min[i]) +
             algorithms.calculate_concentration(dg, C0[i + 1], D[i + 1], W, v[
                 i + 1], x_detector, t_min[i]) + (n - 2) * (1 - qf) /
             (n - 3) * C_negligible) / (algorithms.calculate_concentration(
                 dg, C0[i], D[i], W, v[i], x_detector, t_peak[i])) <= c)

        # F(tmin, i)/(F(tmax, i+1)) <= c
        exprs.append(
            (algorithms.calculate_concentration(dg, C0[i], D[i], W, v[i],
                                                x_detector, t_min[i]) +
             algorithms.calculate_concentration(dg, C0[i + 1], D[i + 1], W, v[
                 i + 1], x_detector, t_min[i]) + (n - 2) * (1 - qf) /
             (n - 3) * C_negligible) /
            (algorithms.calculate_concentration(dg, C0[i + 1], D[i + 1], W, v[
                i + 1], x_detector, t_peak[i + 1])) <= c)

    # Call translate on output - waste node
    [
        exprs.append(val) for val in translation_strats[algorithms.retrieve(
            dg, waste_node_name, 'kind')](dg, waste_node_name)
    ]
    # Call translate on output - anode
    [
        exprs.append(val) for val in translation_strats[algorithms.retrieve(
            dg, anode_node_name, 'kind')](dg, anode_node_name)
    ]

    return exprs
Beispiel #16
0
def rambusOscillatorTanh(numStages, g_cc=0.5, numSolutions="all", a=-5.0):
    epsilon = 1e-14
    start = time.time()
    g_fwd = 1.0
    lenV = numStages * 2
    vs = []
    vfwds = []
    vccs = []
    for i in range(lenV):
        vs.append(Variable("v" + str(i)))
        vfwds.append(Variable("vfwd" + str(i)))
        vccs.append(Variable("vcc" + str(i)))

    allConstraints = []

    # Store rambus oscillator constraints
    for i in range(lenV):
        allConstraints.append(vs[i] >= -1)
        allConstraints.append(vs[i] <= 1)
        fwdInd = (i - 1) % lenV
        ccInd = (i + lenV // 2) % lenV
        allConstraints.append(vfwds[i] == tanh(a * vs[fwdInd]))
        allConstraints.append(vccs[i] == tanh(a * vs[ccInd]))
        allConstraints.append(g_fwd * vfwds[i] + (-g_fwd - g_cc) * vs[i] +
                              g_cc * vccs[i] == 0)

    allSolutions = []
    while True:
        if numSolutions != "all" and len(allSolutions) == numSolutions:
            break

        # Store constraints pruning search space so that
        # old hyperrectangles are not considered
        excludingConstraints = []
        for solution in allSolutions:
            singleExcludingConstraints = []
            for i in range(lenV):
                singleExcludingConstraints.append(vs[i] < solution[i][0])
                singleExcludingConstraints.append(vs[i] > solution[i][1])
            excludingConstraints.append(singleExcludingConstraints)

        # Add all the rambus oscillator constraints
        f_sat = logical_and(*allConstraints)
        # Add constraints so that old hyperrectangles are not considered
        if len(excludingConstraints) > 0:
            for constraints in excludingConstraints:
                f_sat = logical_and(f_sat, logical_or(*constraints))

        #print ("f_sat")
        #print (f_sat)
        result = CheckSatisfiability(f_sat, epsilon)
        #print (result)
        if result is None:
            break
        hyper = np.zeros((lenV, 2))
        for i in range(lenV):
            hyper[i, :] = [
                result[vs[i]].lb() - 2 * epsilon,
                result[vs[i]].ub() + 2 * epsilon
            ]

        #print ("hyper", hyper)
        allSolutions.append(hyper)

        print("num solutions found", len(allSolutions))

    end = time.time()
    print("time taken", end - start)
    return allSolutions
Beispiel #17
0
def rambusOscillatorLcMosfet(numStages, numSolutions = "all", g_cc = 0.5, Vtp = -0.4, Vtn = 0.4, Vdd = 1.8, Kn = 270*1e-6, Kp = -90*1e-6, Sn = 3.0):
	epsilon = 1e-14
	start = time.time()
	#print ("Vtp", Vtp, "Vtn", Vtn, "Vdd", Vdd, "Kn", Kn, "Kp", Kp, "Sn", Sn)
	g_fwd = 1.0
	lenV = numStages*2
	Sp = 2*Sn

	vs = []
	ifwdNs = []
	ifwdPs = []
	iccNs = []
	iccPs = []
	for i in range(lenV):
		vs.append(Variable("v" + str(i)))
		ifwdNs.append(Variable("ifwdN" + str(i)))
		ifwdPs.append(Variable("ifwdP" + str(i)))
		iccNs.append(Variable("iccN" + str(i)))
		iccPs.append(Variable("iccP" + str(i)))

	allConstraints = []	
	for i in range(lenV):
		allConstraints.append(vs[i] >= 0.0)
		allConstraints.append(vs[i] <= Vdd)
		allConstraints.append(g_fwd*(-ifwdNs[i]-ifwdPs[i]) + g_cc*(-iccNs[i]-iccPs[i]) == 0)
		fwdInd = (i-1)%lenV
		ccInd = (i+lenV//2)%lenV
		fwdConstraints = nFet(Vtn, Vdd, Kn, Sn, 0.0, vs[fwdInd], vs[i], ifwdNs[i])
		fwdConstraints += pFet(Vtp, Vdd, Kp, Sp, Vdd, vs[fwdInd], vs[i], ifwdPs[i])
		ccConstraints = nFet(Vtn, Vdd, Kn, Sn, 0.0, vs[ccInd], vs[i], iccNs[i])
		ccConstraints += pFet(Vtp, Vdd, Kp, Sp, Vdd, vs[ccInd], vs[i], iccPs[i])
		allConstraints += fwdConstraints + ccConstraints

	allSolutions = []
	while True:
		if numSolutions != "all" and len(allSolutions) == numSolutions:
			break
		
		# Store constraints pruning search space so that
		# old hyperrectangles are not considered
		excludingConstraints = []
		for solution in allSolutions:
			singleExcludingConstraints = []
			for i in range(lenV):
				singleExcludingConstraints.append(vs[i] < solution[i][0])
				singleExcludingConstraints.append(vs[i] > solution[i][1])
			excludingConstraints.append(singleExcludingConstraints)
		
		#print ("allConstraints")
		#print (allConstraints)
		f_sat = logical_and(*allConstraints)
		if len(excludingConstraints) > 0:
			for constraints in excludingConstraints:
				f_sat = logical_and(f_sat, logical_or(*constraints))
		
		#print ("f_sat")
		#print (f_sat)
		result = CheckSatisfiability(f_sat, epsilon)
		#print (result)
		if result is None:
			break
		hyper = np.zeros((lenV,2))
		for i in range(lenV):
			hyper[i,:] = [result[vs[i]].lb() - 1000*epsilon, result[vs[i]].ub() + 1000*epsilon]

		#print ("hyper", hyper)
		allSolutions.append(hyper)

		print ("num solutions found", len(allSolutions))


	end = time.time()
	print ("time taken", end - start)
	return allSolutions
Beispiel #18
0
    def port(self,
             name,
             kind,
             min_pressure=False,
             min_flow_rate=False,
             x=False,
             y=False,
             fluid_name='default'):
        """Create new port where fluids can enter or exit the circuit, any
        optional tag left empty will be converted to a variable for the SMT
        solver to solve for a give a value, units in brackets

        :param str name: The name of the port to use when defining channels
        :param str kind: Define if this is an 'input' or 'output' port
        :param float density: Density of fluid (kg/m^3)
        :param float min_viscosity: Viscosity of the fluid (Pa*s)
        :param float min_pressure: Pressure of the input fluid, (Pa)
        :param float min_flow_rate - flow rate of input fluid, (m^3/s)
        :param float X: x-position of port on chip schematic (m)
        :param float Y: y-position of port on chip schematic (m)
        :returns: None -- no issues with creating this port
        :raises: TypeError if an input parameter is wrong type
                 ValueError if an input parameter has an invalid value
        """
        user_provided_params = {
            name: 'string',
            min_pressure: 'positive number',
            min_flow_rate: 'positive number',
            x: 'positive number',
            y: 'positive number',
            kind: 'string',
            fluid_name: 'string'
        }
        # Checking that arguments are valid
        self.validate_params(user_provided_params, 'port', name)

        if name in self.dg.nodes:
            raise ValueError("Must provide a unique name")
        if 'translate_' + kind.lower() not in self.translation_strats:
            raise ValueError("kind must be either %s" %
                             self.translation_strats)

        # Initialize fluid properties
        fluid_properties = Fluid(fluid_name)

        # Ports are stored with nodes because ports are just a specific type of
        # node that has a constant flow rate
        # only accept ports of the right kind (input or output)
        attributes = {
            'kind': kind.lower(),
            'viscosity': Variable(name + '_viscosity'),
            'min_viscosity': fluid_properties.min_viscosity,
            'pressure': Variable(name + '_pressure'),
            'min_pressure': min_pressure,
            'flow_rate': Variable(name + '_flow_rate'),
            'min_flow_rate': min_flow_rate,
            'density': Variable(name + '_density'),
            'min_density': fluid_properties.min_density,
            'x': Variable(name + '_x'),
            'y': Variable(name + '_y'),
            'min_x': x,
            'min_y': y,
            'analyte_diffusivities': fluid_properties.analyte_diffusivities,
            'analyte_initial_concentrations':
            fluid_properties.analyte_initial_concentrations,
            'analyte_radii': fluid_properties.analyte_radii,
            'analyte_charges': fluid_properties.analyte_charges
        }

        # If user provides values, put them into the attributes dictionary
        if not x:
            attributes['min_x'] = x
        if not y:
            attributes['min_y'] = y
        if not min_flow_rate:
            attributes['min_flow_rate'] = min_flow_rate
        if not min_pressure:
            attributes['min_pressure'] = min_pressure

        # Create this node in the graph
        self.dg.add_node(name)
        # Add argument to attributes within NetworkX
        for key, attr in attributes.items():
            self.dg.nodes[name][key] = attr
        return
Beispiel #19
0
def inverterLoopLcMosfet(numInverters, numSolutions = "all", Vtp = -0.4, Vtn = 0.4, Vdd = 1.8, Kn = 270*1e-6, Kp = -90*1e-6, Sn = 3.0):
	epsilon = 1e-14
	start = time.time()
	#print ("Vtp", Vtp, "Vtn", Vtn, "Vdd", Vdd, "Kn", Kn, "Kp", Kp, "Sn", Sn)
	Sp = 2*Sn

	vs = []
	iNs = []
	iPs = []

	for i in range(numInverters):
		vs.append(Variable("v" + str(i)))
		iNs.append(Variable("iN" + str(i)))
		iPs.append(Variable("iP" + str(i)))

	allConstraints = []	
	for i in range(numInverters):
		allConstraints.append(vs[i] >= 0.0)
		allConstraints.append(vs[i] <= Vdd)
		allConstraints.append(-iNs[i]-iPs[i] == 0)
		inputInd = i
		outputInd = (i+1)%numInverters
		allConstraints += nFet(Vtn, Vdd, Kn, Sn, 0.0, vs[inputInd], vs[outputInd], iNs[i])
		allConstraints += pFet(Vtp, Vdd, Kp, Sp, Vdd, vs[inputInd], vs[outputInd], iPs[i])

	allSolutions = []
	while True:
		if numSolutions != "all" and len(allSolutions) == numSolutions:
			break
		
		# Store constraints pruning search space so that
		# old hyperrectangles are not considered
		excludingConstraints = []
		for solution in allSolutions:
			singleExcludingConstraints = []
			for i in range(numInverters):
				singleExcludingConstraints.append(vs[i] < solution[i][0])
				singleExcludingConstraints.append(vs[i] > solution[i][1])
			excludingConstraints.append(singleExcludingConstraints)
		
		#print ("allConstraints")
		#print (allConstraints)
		f_sat = logical_and(*allConstraints)
		if len(excludingConstraints) > 0:
			for constraints in excludingConstraints:
				f_sat = logical_and(f_sat, logical_or(*constraints))
		
		#print ("f_sat")
		#print (f_sat)
		result = CheckSatisfiability(f_sat, epsilon)
		#print (result)
		if result is None:
			break
		hyper = np.zeros((numInverters,2))
		for i in range(numInverters):
			hyper[i,:] = [result[vs[i]].lb() - 1000*epsilon, result[vs[i]].ub() + 1000*epsilon]

		#print ("hyper", hyper)
		allSolutions.append(hyper)

		print ("num solutions found", len(allSolutions))


	end = time.time()
	print ("time taken", end - start)
	return allSolutions
Beispiel #20
0
def schmittTriggerLcMosfet(inputVoltage, Vtp = -0.4, Vtn = 0.4, Vdd = 1.8, Kn = 270*1e-6, Kp = -90*1e-6, Sn = 3.0, numSolutions = "all"):
	epsilon = 1e-14
	start = time.time()
	#print ("Vtp", Vtp, "Vtn", Vtn, "Vdd", Vdd, "Kn", Kn, "Kp", Kp, "Sn", Sn)
	Sp = Sn *2.0

	lenV = 3

	vs = []
	tIs = []
	nIs = []

	for i in range(lenV):
		vs.append(Variable("v" + str(i)))
		nIs.append(Variable("nI" + str(i)))
	
	for i in range(lenV*2):
		tIs.append(Variable("tI" + str(i)))

	allConstraints = []	
	for i in range(lenV):
		allConstraints.append(vs[i] >= 0.0)
		allConstraints.append(vs[i] <= 1.8)
	allConstraints += nFetLeak(Vtn, Vdd, Kn, Sn, 0.0, inputVoltage, vs[1], tIs[0])
	allConstraints += nFetLeak(Vtn, Vdd, Kn, Sn, vs[1], inputVoltage, vs[0], tIs[1])
	allConstraints += nFetLeak(Vtn, Vdd, Kn, Sn, vs[1], vs[0], Vdd, tIs[2])
	allConstraints += pFetLeak(Vtp, Vdd, Kp, Sp, Vdd, inputVoltage, vs[2], tIs[3])
	allConstraints += pFetLeak(Vtp, Vdd, Kp, Sp, vs[2], inputVoltage, vs[0], tIs[4])
	allConstraints += pFetLeak(Vtp, Vdd, Kp, Sp, vs[2], vs[0], 0.0, tIs[5])
	allConstraints.append(nIs[0] == -tIs[4] - tIs[1])
	allConstraints.append(nIs[1] == -tIs[0] + tIs[1] + tIs[2])
	allConstraints.append(nIs[2] == -tIs[3] + tIs[5] + tIs[4])
	for i in range(lenV):
		allConstraints.append(nIs[i] == 0.0)

	allSolutions = []
	while True:
		if numSolutions != "all" and len(allSolutions) == numSolutions:
			break
		
		# Store constraints pruning search space so that
		# old hyperrectangles are not considered
		excludingConstraints = []
		for solution in allSolutions:
			singleExcludingConstraints = []
			for i in range(lenV):
				singleExcludingConstraints.append(vs[i] < solution[i][0])
				singleExcludingConstraints.append(vs[i] > solution[i][1])
			excludingConstraints.append(singleExcludingConstraints)
		
		#print ("allConstraints")
		#print (allConstraints)
		#print ("numConstraints", len(allConstraints))

		f_sat = logical_and(*allConstraints)
		if len(excludingConstraints) > 0:
			for constraints in excludingConstraints:
				f_sat = logical_and(f_sat, logical_or(*constraints))
		
		#print ("f_sat")
		#print (f_sat)
		result = CheckSatisfiability(f_sat, epsilon)
		#print (result)
		if result is None:
			break
		hyper = np.zeros((lenV,2))
		for i in range(lenV):
			hyper[i,:] = [result[vs[i]].lb() - 1000*epsilon, result[vs[i]].ub() + 1000*epsilon]

		#print ("hyper", hyper)
		allSolutions.append(hyper)

		print ("num solutions found", len(allSolutions))


	end = time.time()
	print ("time taken", end - start)
	return allSolutions
Beispiel #21
0
def translate_tjunc(dg, name, crit_crossing_angle=0.5):
    """Create SMT expressions for a t-junction node that generates droplets
    Must have 2 input channels (continuous and dispersed phases) and one
    output channel where the droplets leave the node. Continuous is usually
    oil and dispersed is usually water

    :param str name: The name of the channel to generate SMT equations for
    :param crit_crossing_angle: The angle of the dispersed channel to
        the continuous must be great than this to have droplet generation
    :returns: None -- no issues with translating channel parameters to SMT
    :raises: KeyError, if channel is not found in the list of defined edges
    """
    exprs = []
    # Validate input
    if dg.size(name) != 3:
        raise ValueError("T-junction %s must have 3 connections" % name)

    # Since T-junction is just a specialized node, call translate node
    [exprs.append(val) for val in translate_node(dg, name)]

    # Renaming for consistency with the other nodes
    junction_node_name = name
    # Since there should only be one output node, this can be found first
    # from the dict of successors
    try:
        output_node_name = list(dict(dg.succ[name]).keys())[0]
        output_channel_name = (junction_node_name, output_node_name)
    except KeyError as e:
        raise KeyError("T-junction must have only one output")
    # these will be found later from iterating through the dict of
    # predecessor nodes to the junction node
    continuous_node_name = ''
    continuous_channel_name = ''
    dispersed_node_name = ''
    dispersed_channel_name = ''

    # NetworkX allows for the creation of dicts that contain all of
    # the edges containing a certain attribute, in this case phase is
    # of interest
    phases = nx.get_edge_attributes(dg, 'phase')
    for pred_node, phase in phases.items():
        if phase == 'continuous':
            continuous_node_name = pred_node[0]
            continuous_channel_name = (continuous_node_name,
                                       junction_node_name)
            # assert width and height to be equal to output
            exprs.append(
                algorithms.retrieve(dg, continuous_channel_name, 'width') ==
                algorithms.retrieve(dg, output_channel_name, 'width'))
            exprs.append(
                algorithms.retrieve(dg, continuous_channel_name, 'height') ==
                algorithms.retrieve(dg, output_channel_name, 'height'))
        elif phase == 'dispersed':
            dispersed_node_name = pred_node[0]
            dispersed_channel_name = (dispersed_node_name, junction_node_name)
            # Assert that only the height of channel be equal
            exprs.append(
                algorithms.retrieve(dg, dispersed_channel_name, 'height') ==
                algorithms.retrieve(dg, output_channel_name, 'height'))
        elif phase == 'output':
            continue
        else:
            raise ValueError("Invalid phase for T-junction: %s" % name)

    # Epsilon, sharpness of T-junc, must be greater than 0
    # epsilon = 0.01*w for liquid droplets from Steijn et al.
    epsilon = Variable('epsilon')
    exprs.append(
        epsilon == algorithms.retrieve(dg, continuous_channel_name, 'width') *
        0.01)

    # TODO: Figure out why original had this cause it doesn't seem true
    #  # Pressure at each of the 4 nodes must be equal
    #  exprs.append(Equals(junction_node['pressure'],
    #                           continuous_node['pressure']
    #                           ))
    #  exprs.append(Equals(junction_node['pressure'],
    #                           dispersed_node['pressure']
    #                           ))
    #  exprs.append(Equals(junction_node['pressure'],
    #                           output_node['pressure']
    #                           ))

    # Viscosity in continous phase equals viscosity at output
    exprs.append(
        algorithms.retrieve(dg, continuous_node_name, 'viscosity') ==
        algorithms.retrieve(dg, output_node_name, 'viscosity'))

    # Flow rate into the t-junction equals the flow rate out
    exprs.append(
        algorithms.retrieve(dg, continuous_channel_name, 'flow_rate') +
        algorithms.retrieve(dg, dispersed_channel_name, 'flow_rate') ==
        algorithms.retrieve(dg, output_channel_name, 'flow_rate'))

    # Assert that continuous and output channels are in a straight line
    exprs.append(
        algorithms.channels_in_straight_line(dg, continuous_node_name,
                                             junction_node_name,
                                             output_node_name))

    # Droplet volume in channel equals calculated droplet volume
    # TODO: Manifold also has a table of constraints in the Schematic and
    # sets ChannelDropletVolume equal to dropletVolumeConstraint, however
    # the constraint is void (new instance of RealTypeValue) and I think
    # could conflict with calculated value, so ignoring it for now but
    # may be necessary to add at a later point if I'm misunderstand why
    # its needed
    exprs.append(
        algorithms.retrieve(dg, output_channel_name, 'droplet_volume') ==
        algorithms.calculate_droplet_volume(
            dg, algorithms.retrieve(dg, output_channel_name, 'height'),
            algorithms.retrieve(dg, output_channel_name, 'width'),
            algorithms.retrieve(dg, dispersed_channel_name, 'width'), epsilon,
            algorithms.retrieve(dg, dispersed_node_name, 'flow_rate'),
            algorithms.retrieve(dg, continuous_node_name, 'flow_rate')))

    # Assert critical angle is <= calculated angle
    cosine_squared_theta_crit = math.cos(math.radians(crit_crossing_angle))**2
    # Continuous to dispersed
    exprs.append(cosine_squared_theta_crit <= algorithms.cosine_law_crit_angle(
        dg, continuous_node_name, junction_node_name, dispersed_node_name))
    # Continuous to output
    exprs.append(cosine_squared_theta_crit <= algorithms.cosine_law_crit_angle(
        dg, continuous_node_name, junction_node_name, output_node_name))
    # Output to dispersed
    exprs.append(cosine_squared_theta_crit <= algorithms.cosine_law_crit_angle(
        dg, output_node_name, junction_node_name, dispersed_node_name))
    # Call translate on output
    [
        exprs.append(val) for val in translation_strats[algorithms.retrieve(
            dg, output_node_name, 'kind')](dg, output_node_name)
    ]
    return exprs
Beispiel #22
0
 def make_bound(name_of_var, lb, ub):
     v = Variable(name_of_var)
     return (v, logical_and(lb <= v, v <= ub))