From 96c6bcfb68d0816ff41bea1ca9a515a10e763484 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 1 Feb 2015 16:05:39 -0500 Subject: [PATCH 01/20] add strategy for rectangular channel resistance --- .../microfluidics/MicrofluidicsBackend.java | 44 +++++++++++++- .../microfluidics/PrimitiveTypeTable.java | 25 ++++++-- .../back/microfluidics/smt2/QFNRA.java | 8 +++ .../smt2/SymbolNameGenerator.java | 10 ++++ .../strategies/MultiPhaseStrategySet.java | 44 ++++++++++++++ .../strategies/PressureFlowStrategySet.java | 34 +++++++++++ .../multiphase/DropletConstraintStrategy.java | 58 +++++++++++++++++++ .../TJunctionDeviceStrategy.java | 6 +- .../ChannelResistanceStrategy.java | 56 ++++++++++++++++++ .../TestMicrofluidicsBackend.java | 29 ++++++++++ .../UtilSchematicConstruction.java | 25 +++++++- 11 files changed, 330 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/DropletConstraintStrategy.java rename src/main/java/org/manifold/compiler/back/microfluidics/strategies/{devices => multiphase}/TJunctionDeviceStrategy.java (97%) create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java index fd11577..476ce9b 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java @@ -1,6 +1,10 @@ package org.manifold.compiler.back.microfluidics; +import java.io.BufferedWriter; +import java.io.FileWriter; import java.io.IOException; +import java.util.LinkedList; +import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; @@ -9,6 +13,13 @@ import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.manifold.compiler.Backend; +import org.manifold.compiler.back.microfluidics.smt2.ParenList; +import org.manifold.compiler.back.microfluidics.smt2.QFNRA; +import org.manifold.compiler.back.microfluidics.smt2.SExpression; +import org.manifold.compiler.back.microfluidics.smt2.Symbol; +import org.manifold.compiler.back.microfluidics.strategies.MultiPhaseStrategySet; +import org.manifold.compiler.back.microfluidics.strategies.PlacementTranslationStrategySet; +import org.manifold.compiler.back.microfluidics.strategies.PressureFlowStrategySet; import org.manifold.compiler.middle.Schematic; public class MicrofluidicsBackend implements Backend { @@ -113,8 +124,39 @@ private static void checkTypeHierarchy(PrimitiveTypeTable typeTable) { } } - public void run(Schematic schematic) { + public void run(Schematic schematic) throws IOException { primitiveTypes = constructTypeTable(schematic); + // translation step + // for now: one pass + List exprs = new LinkedList<>(); + exprs.add(QFNRA.useQFNRA()); + + PlacementTranslationStrategySet placeSet = + new PlacementTranslationStrategySet(); + exprs.addAll(placeSet.translate( + schematic, processParams, primitiveTypes)); + MultiPhaseStrategySet multiPhase = new MultiPhaseStrategySet(); + exprs.addAll(multiPhase.translate( + schematic, processParams, primitiveTypes)); + PressureFlowStrategySet pressureFlow = new PressureFlowStrategySet(); + exprs.addAll(pressureFlow.translate( + schematic, processParams, primitiveTypes)); + + // (check-sat) (exit) + exprs.add(new ParenList(new SExpression[] { + new Symbol("check-sat") + })); + exprs.add(new ParenList(new SExpression[] { + new Symbol("exit") + })); + // write to "schematic-name.smt2" + String filename = schematic.getName() + ".smt2"; + try(BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { + for (SExpression expr : exprs) { + expr.write(writer); + writer.newLine(); + } + } } } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java b/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java index 6722de3..84720bb 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java @@ -15,7 +15,20 @@ public class PrimitiveTypeTable { public PortTypeValue getMicrofluidPortType() { return microfluidPortType; } + + // multi-phase stuff + + private NodeTypeValue fluidEntryNodeType = null; + public NodeTypeValue getFluidEntryNodeType() { + return fluidEntryNodeType; + } + + private NodeTypeValue tJunctionNodeType = null; + public NodeTypeValue getTJunctionNodeType() { + return tJunctionNodeType; + } + // single-phase stuff, probably out of date private NodeTypeValue controlPointNodeType = null; public NodeTypeValue getControlPointNodeType() { return controlPointNodeType; @@ -53,11 +66,6 @@ public void addDerivedVoltageControlPointNodeTypes( public NodeTypeValue getChannelCrossingNodeType() { return channelCrossingNodeType; } - - private NodeTypeValue tJunctionNodeType = null; - public NodeTypeValue getTJunctionNodetype() { - return tJunctionNodeType; - } public void retrieveBaseTypes(Schematic schematic) { try { @@ -100,12 +108,19 @@ public ConstraintType getChannelPlacementConstraintType() { return channelPlacementConstraintType; } + private ConstraintType channelDropletVolumeConstraintType = null; + public ConstraintType getchannelDropletVolumeConstraintType() { + return channelDropletVolumeConstraintType; + } + public void retrieveConstraintTypes(Schematic schematic) { try { controlPointPlacementConstraintType = schematic.getConstraintType("controlPointPlacementConstraint"); channelPlacementConstraintType = schematic.getConstraintType("channelPlacementConstraint"); + channelDropletVolumeConstraintType = + schematic.getConstraintType("channelDropletVolumeConstraint"); } catch (UndeclaredIdentifierException e) { throw new CodeGenerationError( "could not find required microfluidic schematic constraint type '" diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java index adee7ac..7e282e2 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java @@ -12,6 +12,14 @@ private static SExpression infix(SExpression e1, String op, SExpression e2) { return new ParenList(exprs); } + public static SExpression useQFNRA() { + SExpression exprs[] = new SExpression[] { + new Symbol("set-logic"), + new Symbol("QF_NRA") + }; + return new ParenList(exprs); + } + public static SExpression add(SExpression e1, SExpression e2) { return infix(e1, "+", e2); } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java index 4ee5faf..c8c9c6b 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java @@ -87,6 +87,16 @@ public static Symbol getsym_ChannelFlowRate(Schematic schematic, return new Symbol(chName.concat("_flowrate")); } + /** + * Retrieves the symbol that defines the viscosity of fluid + * present in a channel. + */ + public static Symbol getsym_ChannelViscosity(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_viscosity")); + } + /** * Retrieves the symbol that defines the hydrodynamic resistance * of a channel. diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java new file mode 100644 index 0000000..6d00508 --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java @@ -0,0 +1,44 @@ +package org.manifold.compiler.back.microfluidics.strategies; + +import java.util.LinkedList; +import java.util.List; + +import org.manifold.compiler.back.microfluidics.PrimitiveTypeTable; +import org.manifold.compiler.back.microfluidics.ProcessParameters; +import org.manifold.compiler.back.microfluidics.TranslationStrategy; +import org.manifold.compiler.back.microfluidics.smt2.SExpression; +import org.manifold.compiler.back.microfluidics.strategies.multiphase.DropletConstraintStrategy; +import org.manifold.compiler.back.microfluidics.strategies.multiphase.TJunctionDeviceStrategy; +import org.manifold.compiler.middle.Schematic; + +public class MultiPhaseStrategySet extends TranslationStrategy { + + private DropletConstraintStrategy dropletConstraintStrategy; + public void useDropletConstraintStrategy(DropletConstraintStrategy strat) { + this.dropletConstraintStrategy = strat; + } + + private TJunctionDeviceStrategy tjunctionDeviceStrategy; + public void useTJunctionDeviceStrategy(TJunctionDeviceStrategy strat) { + this.tjunctionDeviceStrategy = strat; + } + + public MultiPhaseStrategySet() { + // initialize default strategies + dropletConstraintStrategy = new DropletConstraintStrategy(); + tjunctionDeviceStrategy = new TJunctionDeviceStrategy(); + } + + @Override + protected List translationStep(Schematic schematic, + ProcessParameters processParams, + PrimitiveTypeTable typeTable) { + List exprs = new LinkedList<>(); + exprs.addAll(dropletConstraintStrategy.translate( + schematic, processParams, typeTable)); + exprs.addAll(tjunctionDeviceStrategy.translate( + schematic, processParams, typeTable)); + return exprs; + } + +} diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java new file mode 100644 index 0000000..7658ee5 --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java @@ -0,0 +1,34 @@ +package org.manifold.compiler.back.microfluidics.strategies; + +import java.util.LinkedList; +import java.util.List; + +import org.manifold.compiler.back.microfluidics.PrimitiveTypeTable; +import org.manifold.compiler.back.microfluidics.ProcessParameters; +import org.manifold.compiler.back.microfluidics.TranslationStrategy; +import org.manifold.compiler.back.microfluidics.smt2.SExpression; +import org.manifold.compiler.back.microfluidics.strategies.pressureflow.ChannelResistanceStrategy; +import org.manifold.compiler.middle.Schematic; + +public class PressureFlowStrategySet extends TranslationStrategy { + + private ChannelResistanceStrategy channelResistanceStrategy; + public void useChannelResistanceStrategy( + ChannelResistanceStrategy strat) { + this.channelResistanceStrategy = strat; + } + + public PressureFlowStrategySet() { + channelResistanceStrategy = new ChannelResistanceStrategy(); + } + + @Override + protected List translationStep(Schematic schematic, + ProcessParameters processParams, PrimitiveTypeTable typeTable) { + List exprs = new LinkedList<>(); + exprs.addAll(channelResistanceStrategy.translate( + schematic, processParams, typeTable)); + return exprs; + } + +} diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/DropletConstraintStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/DropletConstraintStrategy.java new file mode 100644 index 0000000..bada92b --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/DropletConstraintStrategy.java @@ -0,0 +1,58 @@ +package org.manifold.compiler.back.microfluidics.strategies.multiphase; + +import java.util.LinkedList; +import java.util.List; + +import org.manifold.compiler.ConnectionValue; +import org.manifold.compiler.ConstraintValue; +import org.manifold.compiler.RealValue; +import org.manifold.compiler.UndeclaredAttributeException; +import org.manifold.compiler.back.microfluidics.CodeGenerationError; +import org.manifold.compiler.back.microfluidics.PrimitiveTypeTable; +import org.manifold.compiler.back.microfluidics.ProcessParameters; +import org.manifold.compiler.back.microfluidics.TranslationStrategy; +import org.manifold.compiler.back.microfluidics.smt2.Decimal; +import org.manifold.compiler.back.microfluidics.smt2.QFNRA; +import org.manifold.compiler.back.microfluidics.smt2.SExpression; +import org.manifold.compiler.back.microfluidics.smt2.Symbol; +import org.manifold.compiler.back.microfluidics.smt2.SymbolNameGenerator; +import org.manifold.compiler.middle.Schematic; + +public class DropletConstraintStrategy extends TranslationStrategy { + + @Override + protected List translationStep(Schematic schematic, + ProcessParameters processParams, PrimitiveTypeTable typeTable) { + List exprs = new LinkedList<>(); + // get all droplet-related constraints + for (ConstraintValue cxt : schematic.getConstraints().values()) { + if (cxt.getType().isSubtypeOf( + typeTable.getchannelDropletVolumeConstraintType())) { + // droplet volume constraint + exprs.addAll(translateDropletVolumeConstraint( + schematic, typeTable, cxt)); + } + } + return exprs; + } + + private List translateDropletVolumeConstraint( + Schematic schematic, PrimitiveTypeTable typeTable, + ConstraintValue cxt) { + List exprs = new LinkedList<>(); + try { + ConnectionValue channel = (ConnectionValue) cxt.getAttribute("channel"); + Decimal volume = new Decimal(((RealValue) cxt.getAttribute("volume")) + .toDouble()); + Symbol vChannel = + SymbolNameGenerator.getsym_ChannelDropletVolume(schematic, channel); + exprs.add(QFNRA.assertEqual(vChannel, volume)); + } catch (ClassCastException|UndeclaredAttributeException e) { + throw new CodeGenerationError( + "instance of channelDropletVolumeConstraint" + + " has values with wrong types"); + } + return exprs; + } + +} diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/devices/TJunctionDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java similarity index 97% rename from src/main/java/org/manifold/compiler/back/microfluidics/strategies/devices/TJunctionDeviceStrategy.java rename to src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java index 9dfc505..93946a5 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/devices/TJunctionDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java @@ -1,4 +1,4 @@ -package org.manifold.compiler.back.microfluidics.strategies.devices; +package org.manifold.compiler.back.microfluidics.strategies.multiphase; import java.util.LinkedList; import java.util.List; @@ -39,7 +39,7 @@ protected List translationStep(Schematic schematic, ProcessParameters processParams, PrimitiveTypeTable typeTable) { List exprs = new LinkedList<>(); // look for all T-junctions - NodeTypeValue targetNode = typeTable.getTJunctionNodetype(); + NodeTypeValue targetNode = typeTable.getTJunctionNodeType(); for (NodeValue node : schematic.getNodes().values()) { if (!(node.getType().isSubtypeOf(targetNode))) { continue; @@ -127,6 +127,8 @@ private List translateTJunction(Schematic schematic, Symbol pi = SymbolNameGenerator.getsym_constant_pi(); // TODO constraint: all channels must be rectangular + // TODO (?) constraint: continuous and output channel must be parallel + // TODO (?) constraint: disperse and output channel must be perpendicular // constraint: flow rates must be positive into the junction at inputs PortValue pContinuous = junction.getPort("continuous"); exprs.add(constrainFlowDirection( diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java new file mode 100644 index 0000000..91751bf --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java @@ -0,0 +1,56 @@ +package org.manifold.compiler.back.microfluidics.strategies.pressureflow; + +import java.util.LinkedList; +import java.util.List; + +import org.manifold.compiler.ConnectionValue; +import org.manifold.compiler.back.microfluidics.PrimitiveTypeTable; +import org.manifold.compiler.back.microfluidics.ProcessParameters; +import org.manifold.compiler.back.microfluidics.TranslationStrategy; +import org.manifold.compiler.back.microfluidics.smt2.Decimal; +import org.manifold.compiler.back.microfluidics.smt2.QFNRA; +import org.manifold.compiler.back.microfluidics.smt2.SExpression; +import org.manifold.compiler.back.microfluidics.smt2.Symbol; +import org.manifold.compiler.back.microfluidics.smt2.SymbolNameGenerator; +import org.manifold.compiler.middle.Schematic; + +public class ChannelResistanceStrategy extends TranslationStrategy { + + @Override + protected List translationStep(Schematic schematic, + ProcessParameters processParams, PrimitiveTypeTable typeTable) { + List exprs = new LinkedList<>(); + for (ConnectionValue conn : schematic.getConnections().values()) { + // TODO check port types + // TODO it would be really cool to make the channel type + // part of the SMT2 equations so we could solve for that too + // TODO we are just assuming all channels are rectangular right now + exprs.addAll(translateRectangularChannel(schematic, conn)); + } + return exprs; + } + + private List translateRectangularChannel( + Schematic schematic, ConnectionValue channel) { + List exprs = new LinkedList<>(); + // R = (12 * mu * L) / (w * h^3 * (1 - 0.630 (h/w)) ) + // for channel width w, height h, h < w + // total length L + // viscosity of the solvent is mu + Symbol R = SymbolNameGenerator.getsym_ChannelResistance(schematic, channel); + Symbol w = SymbolNameGenerator.getsym_ChannelWidth(schematic, channel); + Symbol h = SymbolNameGenerator.getsym_ChannelHeight(schematic, channel); + Symbol mu = SymbolNameGenerator.getsym_ChannelViscosity(schematic, channel); + Symbol L = SymbolNameGenerator.getsym_ChannelLength(schematic, channel); + SExpression resistanceRectangular = QFNRA.assertEqual(R, + QFNRA.divide(QFNRA.multiply(new Decimal(12.0), QFNRA.multiply(mu, L)), + QFNRA.multiply(w, QFNRA.multiply(QFNRA.pow(h, new Decimal(3.0)), + QFNRA.subtract(new Decimal(1.0), + QFNRA.multiply(new Decimal(0.630), QFNRA.divide(h, w))))))); + exprs.add(resistanceRectangular); + SExpression heightLessThanWidth = QFNRA.assertLessThan(h, w); + exprs.add(heightLessThanWidth); + return exprs; + } + +} diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index 9258c85..ecb5066 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -1,10 +1,15 @@ package org.manifold.compiler.back.microfluidics; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.Options; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.PatternLayout; import org.junit.BeforeClass; +import org.junit.Test; +import org.manifold.compiler.middle.Schematic; public class TestMicrofluidicsBackend { @@ -15,7 +20,31 @@ public static void setUpClass() { "%-5p [%t]: %m%n"); LogManager.getRootLogger().addAppender( new ConsoleAppender(layout, ConsoleAppender.SYSTEM_ERR)); + + UtilSchematicConstruction.setupIntermediateTypes(); } + + @Test + public void testTJunctionSynthesis() throws Exception { + String[] args = { + "-bProcessMinimumNodeDistance", "0.0001", + "-bProcessMinimumChannelLength", "0.0001", + "-bProcessMaximumChipSizeX", "0.04", + "-bProcessMaximumChipSizeY", "0.04", + "-bProcessCriticalCrossingAngle", "0.0872664626" + }; + + Schematic schematic = UtilSchematicConstruction + .instantiateSchematic("test"); + + MicrofluidicsBackend backend = new MicrofluidicsBackend(); + Options options = new Options(); + backend.registerArguments(options); + CommandLineParser parser = new org.apache.commons.cli.BasicParser(); + CommandLine cmd = parser.parse(options, args); + backend.invokeBackend(schematic, cmd); + } + // TODO update test for new interface /* @Test diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java b/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java index b40e778..30f3d55 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java @@ -43,6 +43,7 @@ public class UtilSchematicConstruction { private static ConstraintType controlPointPlacementConstraintType; private static ConstraintType channelPlacementConstraintType; + private static ConstraintType channelDropletVolumeConstraintType; public static void setupIntermediateTypes() { @@ -81,13 +82,20 @@ public static void setupIntermediateTypes() { cxtCPPlaceAttrs.put("y", RealTypeValue.getInstance()); controlPointPlacementConstraintType = new ConstraintType(cxtCPPlaceAttrs); - // controlPointPlacementConstraint(microfluidChannel node, Real x, Real y) + // channelPlacementConstraint(Connection channel, Real x, Real y) Map cxtChanPlaceAttrs = new HashMap<>(); cxtChanPlaceAttrs.put("channel", ConnectionTypeValue.getInstance()); cxtChanPlaceAttrs.put("x", RealTypeValue.getInstance()); cxtChanPlaceAttrs.put("y", RealTypeValue.getInstance()); channelPlacementConstraintType = new ConstraintType(cxtChanPlaceAttrs); + // channelDropletVolumeConstraint(Connection channel, Real volume) + Map cxtChanDropVolAttrs = new HashMap<>(); + cxtChanDropVolAttrs.put("channel", ConnectionTypeValue.getInstance()); + cxtChanDropVolAttrs.put("volume", RealTypeValue.getInstance()); + channelDropletVolumeConstraintType = + new ConstraintType(cxtChanDropVolAttrs); + setUp = true; } @@ -112,6 +120,9 @@ public static Schematic instantiateSchematic(String name) s.addConstraintType( "channelPlacementConstraint", channelPlacementConstraintType); + s.addConstraintType( + "channelDropletVolumeConstraint", + channelDropletVolumeConstraintType); return s; } @@ -213,5 +224,17 @@ public static ConstraintValue instantiateChannelPlacementConstraint( channelPlacementConstraintType, attrs); return cxt; } + + public static ConstraintValue instantiateChannelDropletVolumeConstraint( + ConnectionValue channel, Double volume) + throws UndeclaredAttributeException, InvalidAttributeException, + TypeMismatchException { + Map attrs = new HashMap<>(); + attrs.put("channel", channel); + attrs.put("volume", new RealValue(volume)); + ConstraintValue cxt = new ConstraintValue( + channelDropletVolumeConstraintType, attrs); + return cxt; + } } From bbef2bc199ed79b9da56ce021a1a31e35dc3813d Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 1 Feb 2015 16:29:06 -0500 Subject: [PATCH 02/20] fix generated symbol name for channel length --- .../compiler/back/microfluidics/smt2/SymbolNameGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java index c8c9c6b..e4973be 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java @@ -73,7 +73,7 @@ public static Symbol getSym_PortPressure( public static Symbol getsym_ChannelLength(Schematic schematic, ConnectionValue ch) { String chName = schematic.getConnectionName(ch); - return new Symbol(chName.concat("_pos_x")); + return new Symbol(chName.concat("_length")); } /** From 7b282938d3e47fc5fd3a4b94db4ef650d8163fc9 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 1 Feb 2015 16:41:10 -0500 Subject: [PATCH 03/20] fix symbol name rules, because dReal doesn't correctly support || names (and they're harder to read anyways) --- .../back/microfluidics/smt2/Symbol.java | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Symbol.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Symbol.java index b205b1d..73481ac 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Symbol.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Symbol.java @@ -4,38 +4,40 @@ import java.io.Writer; public class Symbol extends SExpression { - // TODO symbol names do not always need to be escaped - // so only add |s if it is absolutely necessary - + private String name; public String getName() { return name; } + private static String otherLegalChars = "+-/*=%?!.%_!&^<>@"; + private void validateName(String name) { - // A symbol is a any sequence of printable ASCII characters (including - // space, tab, and line-breaking characters) except for the - // backslash character \, that starts and ends with | - // and does not otherwise contain |. - if (!name.startsWith("|")) { - throw new IllegalArgumentException("symbol must start with '|'"); + // A symbol is a non-empty sequence of letters, digits, and the characters + // + - / * = % ? ! . $ _ ~ & ^ < > @ + // that does not start with a digit. + if (name.isEmpty()) { + throw new IllegalArgumentException( + "symbol name cannot be empty"); } - if (!name.endsWith("|")) { - throw new IllegalArgumentException("symbol must end with '|'"); + if (Character.isDigit(name.charAt(0))) { + throw new IllegalArgumentException( + "symbol name cannot start with a digit"); } - if (name.indexOf('\\') != -1) { - throw new IllegalArgumentException("symbol cannot contain '\\'"); + for (int i = 0; i < name.length(); ++i) { + char c = name.charAt(i); + if (Character.isAlphabetic(c)) { + continue; + } else if (Character.isDigit(c)) { + continue; + } else if (otherLegalChars.indexOf(c) == -1) { + throw new IllegalArgumentException("character '" + c + "'" + + " cannot appear in a symbol name"); + } } } public Symbol(String name) { - // If the name isn't delimited by |s, add them. - if (!name.startsWith("|")) { - name = "|" + name; - } - if (!name.endsWith("|")) { - name = name + "|"; - } validateName(name); this.name = name; } From bf06cc59faae93715708cfc80ed9157e59931f6d Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 1 Feb 2015 17:45:50 -0500 Subject: [PATCH 04/20] generate declarations for entry/exit nodes --- .../microfluidics/PrimitiveTypeTable.java | 17 +++- .../back/microfluidics/smt2/QFNRA.java | 10 ++ .../smt2/SymbolNameGenerator.java | 2 +- .../strategies/PressureFlowStrategySet.java | 10 ++ .../FluidEntryExitDeviceStrategy.java | 97 +++++++++++++++++++ .../TestMicrofluidicsBackend.java | 15 +++ .../UtilSchematicConstruction.java | 86 ++++++++++++---- 7 files changed, 217 insertions(+), 20 deletions(-) create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java b/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java index 84720bb..dff5c57 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/PrimitiveTypeTable.java @@ -23,6 +23,11 @@ public NodeTypeValue getFluidEntryNodeType() { return fluidEntryNodeType; } + private NodeTypeValue fluidExitNodeType = null; + public NodeTypeValue getFluidExitNodeType() { + return fluidExitNodeType; + } + private NodeTypeValue tJunctionNodeType = null; public NodeTypeValue getTJunctionNodeType() { return tJunctionNodeType; @@ -70,6 +75,16 @@ public NodeTypeValue getChannelCrossingNodeType() { public void retrieveBaseTypes(Schematic schematic) { try { microfluidPortType = schematic.getPortType("microfluidPort"); + + // multi-phase + fluidEntryNodeType = + schematic.getNodeType("fluidEntry"); + fluidExitNodeType = + schematic.getNodeType("fluidExit"); + tJunctionNodeType = + schematic.getNodeType("tJunction"); + + // single-phase controlPointNodeType = schematic.getNodeType("controlPoint"); pressureControlPointNodeType = schematic.getNodeType("pressureControlPoint"); @@ -77,8 +92,6 @@ public void retrieveBaseTypes(Schematic schematic) { schematic.getNodeType("voltageControlPoint"); channelCrossingNodeType = schematic.getNodeType("channelCrossing"); - tJunctionNodeType = - schematic.getNodeType("tJunction"); } catch (UndeclaredIdentifierException e) { throw new CodeGenerationError( "could not find required microfluidic schematic type '" diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java index 7e282e2..74e2a99 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java @@ -20,6 +20,16 @@ public static SExpression useQFNRA() { return new ParenList(exprs); } + public static SExpression declareRealVariable(Symbol var) { + SExpression exprs[] = new SExpression[] { + new Symbol("declare-fun"), + var, + new ParenList(), + new Symbol("Real") + }; + return new ParenList(exprs); + } + public static SExpression add(SExpression e1, SExpression e2) { return infix(e1, "+", e2); } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java index e4973be..7061a2b 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java @@ -32,7 +32,7 @@ public static Symbol getsym_NodeX(Schematic schematic, NodeValue node) { */ public static Symbol getsym_NodeY(Schematic schematic, NodeValue node) { String nodeName = schematic.getNodeName(node); - return new Symbol(nodeName.concat("_pos_Y")); + return new Symbol(nodeName.concat("_pos_y")); } /** diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java index 7658ee5..3bfa3d8 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java @@ -8,6 +8,7 @@ import org.manifold.compiler.back.microfluidics.TranslationStrategy; import org.manifold.compiler.back.microfluidics.smt2.SExpression; import org.manifold.compiler.back.microfluidics.strategies.pressureflow.ChannelResistanceStrategy; +import org.manifold.compiler.back.microfluidics.strategies.pressureflow.FluidEntryExitDeviceStrategy; import org.manifold.compiler.middle.Schematic; public class PressureFlowStrategySet extends TranslationStrategy { @@ -18,8 +19,15 @@ public void useChannelResistanceStrategy( this.channelResistanceStrategy = strat; } + private FluidEntryExitDeviceStrategy entryExitStrategy; + public void useFluidEntryExitDeviceStrategy( + FluidEntryExitDeviceStrategy strat) { + this.entryExitStrategy = strat; + } + public PressureFlowStrategySet() { channelResistanceStrategy = new ChannelResistanceStrategy(); + entryExitStrategy = new FluidEntryExitDeviceStrategy(); } @Override @@ -28,6 +36,8 @@ protected List translationStep(Schematic schematic, List exprs = new LinkedList<>(); exprs.addAll(channelResistanceStrategy.translate( schematic, processParams, typeTable)); + exprs.addAll(entryExitStrategy.translate( + schematic, processParams, typeTable)); return exprs; } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java new file mode 100644 index 0000000..d5980e3 --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java @@ -0,0 +1,97 @@ +package org.manifold.compiler.back.microfluidics.strategies.pressureflow; + +import java.util.LinkedList; +import java.util.List; + +import org.manifold.compiler.ConnectionValue; +import org.manifold.compiler.NodeValue; +import org.manifold.compiler.PortValue; +import org.manifold.compiler.RealValue; +import org.manifold.compiler.UndeclaredAttributeException; +import org.manifold.compiler.UndeclaredIdentifierException; +import org.manifold.compiler.back.microfluidics.CodeGenerationError; +import org.manifold.compiler.back.microfluidics.PrimitiveTypeTable; +import org.manifold.compiler.back.microfluidics.ProcessParameters; +import org.manifold.compiler.back.microfluidics.TranslationStrategy; +import org.manifold.compiler.back.microfluidics.smt2.Decimal; +import org.manifold.compiler.back.microfluidics.smt2.QFNRA; +import org.manifold.compiler.back.microfluidics.smt2.SExpression; +import org.manifold.compiler.back.microfluidics.smt2.Symbol; +import org.manifold.compiler.back.microfluidics.smt2.SymbolNameGenerator; +import org.manifold.compiler.middle.Schematic; + +public class FluidEntryExitDeviceStrategy extends TranslationStrategy { + +//get the connection associated with this port + // TODO this is VERY EXPENSIVE, find an optimization + protected ConnectionValue getConnection( + Schematic schematic, PortValue port) { + for (ConnectionValue conn : schematic.getConnections().values()) { + if (conn.getFrom().equals(port) || conn.getTo().equals(port)) { + return conn; + } + } + return null; + } + + @Override + protected List translationStep(Schematic schematic, + ProcessParameters processParams, PrimitiveTypeTable typeTable) { + List exprs = new LinkedList<>(); + for (NodeValue node : schematic.getNodes().values()) { + try { + if (node.getType().isSubtypeOf(typeTable.getFluidEntryNodeType())) { + exprs.addAll(translateFluidEntryNode(schematic, node)); + } else if (node.getType().isSubtypeOf(typeTable.getFluidExitNodeType())) { + exprs.addAll(translateFluidExitNode(schematic, node)); + } + } catch (UndeclaredIdentifierException e) { + throw new CodeGenerationError("undeclared identifier '" + + e.getIdentifier() + "' when inspecting fluid entry/exit node '" + + schematic.getNodeName(node) + "'; " + + "possible schematic version mismatch"); + } catch (UndeclaredAttributeException e) { + throw new CodeGenerationError("undeclared attribute " + + " when inspecting fluid entry/exit node '" + + schematic.getNodeName(node) + "'; " + + "possible schematic version mismatch"); + } + } + return exprs; + } + + private List translateFluidEntryNode( + Schematic schematic, NodeValue node) + throws UndeclaredIdentifierException, UndeclaredAttributeException { + List exprs = new LinkedList<>(); + exprs.add(QFNRA.declareRealVariable( + SymbolNameGenerator.getsym_NodeX(schematic, node))); + exprs.add(QFNRA.declareRealVariable( + SymbolNameGenerator.getsym_NodeY(schematic, node))); + exprs.add(QFNRA.declareRealVariable( + SymbolNameGenerator.getSym_PortPressure(schematic, + node.getPort("output")))); + // the viscosity in the channel connected to output + // is the viscosity given at the entry + ConnectionValue ch = getConnection(schematic, node.getPort("output")); + Symbol mu = SymbolNameGenerator.getsym_ChannelViscosity(schematic, ch); + RealValue viscosity = (RealValue) node.getAttribute("viscosity"); + exprs.add(QFNRA.assertEqual(mu, new Decimal(viscosity.toDouble()))); + return exprs; + } + + private List translateFluidExitNode( + Schematic schematic, NodeValue node) + throws UndeclaredIdentifierException { + List exprs = new LinkedList<>(); + exprs.add(QFNRA.declareRealVariable( + SymbolNameGenerator.getsym_NodeX(schematic, node))); + exprs.add(QFNRA.declareRealVariable( + SymbolNameGenerator.getsym_NodeY(schematic, node))); + exprs.add(QFNRA.declareRealVariable( + SymbolNameGenerator.getSym_PortPressure(schematic, + node.getPort("input")))); + return exprs; + } + +} diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index ecb5066..35ed8da 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -9,6 +9,8 @@ import org.apache.log4j.PatternLayout; import org.junit.BeforeClass; import org.junit.Test; +import org.manifold.compiler.ConnectionValue; +import org.manifold.compiler.NodeValue; import org.manifold.compiler.middle.Schematic; public class TestMicrofluidicsBackend { @@ -37,6 +39,19 @@ public void testTJunctionSynthesis() throws Exception { Schematic schematic = UtilSchematicConstruction .instantiateSchematic("test"); + // Make a very simple schematic: + // (fluidEntry) ---> (fluidExit) + // Water has a viscosity of 0.001002 Pa*s + NodeValue entry = UtilSchematicConstruction.instantiateFluidEntry( + schematic, 0.001002); + schematic.addNode("n_entry", entry); + NodeValue exit = UtilSchematicConstruction.instantiateFluidExit(schematic); + schematic.addNode("n_exit", exit); + ConnectionValue entryToExit = UtilSchematicConstruction.instantiateChannel( + entry.getPort("output"), exit.getPort("input")); + schematic.addConnection("c_entry_to_exit", entryToExit); + // TODO constrain the pressure in the channel to be 0.001 Pa + MicrofluidicsBackend backend = new MicrofluidicsBackend(); Options options = new Options(); backend.registerArguments(options); diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java b/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java index 30f3d55..5957b0e 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java @@ -35,11 +35,16 @@ public class UtilSchematicConstruction { private static Map noPorts = new HashMap<>(); private static PortTypeValue microfluidPortType; + // multi-phase + private static NodeTypeValue fluidEntryNodeType; + private static NodeTypeValue fluidExitNodeType; + private static NodeTypeValue tJunctionNodeType; + // single-phase private static NodeTypeValue controlPointNodeType; private static NodeTypeValue voltageCPNodeType; private static NodeTypeValue pressureCPNodeType; private static NodeTypeValue channelCrossingNodeType; - private static NodeTypeValue tJunctionNodeType; + private static ConstraintType controlPointPlacementConstraintType; private static ConstraintType channelPlacementConstraintType; @@ -50,6 +55,28 @@ public static void setupIntermediateTypes() { // TODO which signal type do we want here? microfluidPortType = new PortTypeValue(NilTypeValue.getInstance(), noTypeAttributes); + + // multi-phase + Map fluidEntryPorts = new HashMap<>(); + fluidEntryPorts.put("output", microfluidPortType); + Map fluidEntryAttributes = new HashMap<>(); + fluidEntryAttributes.put("viscosity", RealTypeValue.getInstance()); + fluidEntryNodeType = new NodeTypeValue( + fluidEntryAttributes, fluidEntryPorts); + + Map fluidExitPorts = new HashMap<>(); + fluidExitPorts.put("input", microfluidPortType); + fluidExitNodeType = new NodeTypeValue( + noTypeAttributes, fluidExitPorts); + + Map tJunctionPorts = new HashMap<>(); + tJunctionPorts.put("continuous", microfluidPortType); + tJunctionPorts.put("dispersed", microfluidPortType); + tJunctionPorts.put("output", microfluidPortType); + // TODO attributes + tJunctionNodeType = new NodeTypeValue(noTypeAttributes, tJunctionPorts); + + // single-phase controlPointNodeType = new NodeTypeValue(noTypeAttributes, noPorts); pressureCPNodeType = new NodeTypeValue(noTypeAttributes, noPorts, controlPointNodeType); @@ -67,13 +94,6 @@ public static void setupIntermediateTypes() { channelCrossingPorts.put("channelB1", microfluidPortType); channelCrossingNodeType = new NodeTypeValue(noTypeAttributes, channelCrossingPorts); - - Map tJunctionPorts = new HashMap<>(); - tJunctionPorts.put("continuous", microfluidPortType); - tJunctionPorts.put("dispersed", microfluidPortType); - tJunctionPorts.put("output", microfluidPortType); - // TODO attributes - tJunctionNodeType = new NodeTypeValue(noTypeAttributes, tJunctionPorts); // controlPointPlacementConstraint(ControlPointNode node, Real x, Real y) Map cxtCPPlaceAttrs = new HashMap<>(); @@ -107,12 +127,16 @@ public static Schematic instantiateSchematic(String name) Schematic s = new Schematic(name); s.addPortType("microfluidPort", microfluidPortType); - + // multi-phase + s.addNodeType("fluidEntry", fluidEntryNodeType); + s.addNodeType("fluidExit", fluidExitNodeType); + s.addNodeType("tJunction", tJunctionNodeType); + + // single-phase s.addNodeType("controlPoint", controlPointNodeType); s.addNodeType("pressureControlPoint", pressureCPNodeType); s.addNodeType("voltageControlPoint", voltageCPNodeType); s.addNodeType("channelCrossing", channelCrossingNodeType); - s.addNodeType("tJunction", tJunctionNodeType); s.addConstraintType( "controlPointPlacementConstraint", @@ -127,6 +151,41 @@ public static Schematic instantiateSchematic(String name) return s; } + public static ConnectionValue instantiateChannel(PortValue from, PortValue to) + throws UndeclaredAttributeException, InvalidAttributeException, + TypeMismatchException { + ConnectionValue channel = new ConnectionValue(from, to, noAttributes); + return channel; + } + + public static NodeValue instantiateFluidEntry(Schematic schematic, + double viscosity) + throws SchematicException { + Map attrsMap = new HashMap<>(); + RealValue mu = new RealValue(viscosity); + attrsMap.put("viscosity", mu); + Map> portAttrsMap = new HashMap<>(); + portAttrsMap.put("output", noAttributes); + NodeValue exit = new NodeValue(schematic.getNodeType("fluidEntry"), + attrsMap, portAttrsMap); + return exit; + } + + public static NodeValue instantiateFluidExit(Schematic schematic) + throws SchematicException { + Map> portAttrsMap = new HashMap<>(); + portAttrsMap.put("input", noAttributes); + NodeValue exit = new NodeValue(schematic.getNodeType("fluidExit"), + noAttributes, portAttrsMap); + return exit; + } + + public static NodeValue instantiateTJunction(Schematic schematic) + throws SchematicException { + // TODO + return null; + } + /** * Instantiate a pressure control point with the given number of ports. * The control point's typename will be "pressureControlPointN", @@ -192,13 +251,6 @@ public static NodeValue instantiateVoltageControlPoint(Schematic schematic, NodeValue cp = new NodeValue(cpType, noAttributes, portAttrsMap); return cp; } - - public static ConnectionValue instantiateChannel(PortValue from, PortValue to) - throws UndeclaredAttributeException, InvalidAttributeException, - TypeMismatchException { - ConnectionValue channel = new ConnectionValue(from, to, noAttributes); - return channel; - } public static ConstraintValue instantiateControlPointPlacementConstraint( NodeValue node, Double x, Double y) throws UndeclaredAttributeException, From d3e8e93082027a238638801652916e2f042e558d Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 1 Feb 2015 17:50:59 -0500 Subject: [PATCH 05/20] sort s-exprs: decls appear before asserts --- .../microfluidics/MicrofluidicsBackend.java | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java index 476ce9b..785f7ce 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java @@ -124,6 +124,41 @@ private static void checkTypeHierarchy(PrimitiveTypeTable typeTable) { } } + // Sort a list of unsorted expressions so that all declarations (declare-fun) + // come before all assertions (assert). + public List sortExprs(List unsorted) { + List retval = new LinkedList<>(); + List decls = new LinkedList<>(); + List asserts = new LinkedList<>(); + List others = new LinkedList<>(); + + for (SExpression expr : unsorted) { + if (expr instanceof ParenList) { + ParenList list = (ParenList) expr; + SExpression head = list.getExprs().get(0); + if (head instanceof Symbol) { + Symbol s = (Symbol) head; + if (s.getName().equals("declare-fun")) { + decls.add(expr); + } else if(s.getName().equals("assert")) { + asserts.add(expr); + } else { + others.add(expr); + } + } else { + others.add(expr); + } + } else { + others.add(expr); + } + } + + retval.addAll(decls); + retval.addAll(asserts); + retval.addAll(others); + return retval; + } + public void run(Schematic schematic) throws IOException { primitiveTypes = constructTypeTable(schematic); // translation step @@ -131,17 +166,21 @@ public void run(Schematic schematic) throws IOException { List exprs = new LinkedList<>(); exprs.add(QFNRA.useQFNRA()); + List unsortedExprs = new LinkedList<>(); + PlacementTranslationStrategySet placeSet = new PlacementTranslationStrategySet(); - exprs.addAll(placeSet.translate( + unsortedExprs.addAll(placeSet.translate( schematic, processParams, primitiveTypes)); MultiPhaseStrategySet multiPhase = new MultiPhaseStrategySet(); - exprs.addAll(multiPhase.translate( + unsortedExprs.addAll(multiPhase.translate( schematic, processParams, primitiveTypes)); PressureFlowStrategySet pressureFlow = new PressureFlowStrategySet(); - exprs.addAll(pressureFlow.translate( + unsortedExprs.addAll(pressureFlow.translate( schematic, processParams, primitiveTypes)); + exprs.addAll(sortExprs(unsortedExprs)); + // (check-sat) (exit) exprs.add(new ParenList(new SExpression[] { new Symbol("check-sat") From 8ae1fc29ba88aa0c804ffe3bfdb18e2a35d5a428 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 1 Feb 2015 18:23:41 -0500 Subject: [PATCH 06/20] all variables declared for entry-to-exit synthesis --- .../strategies/pressureflow/ChannelResistanceStrategy.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java index 91751bf..62b19d5 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java @@ -42,6 +42,13 @@ private List translateRectangularChannel( Symbol h = SymbolNameGenerator.getsym_ChannelHeight(schematic, channel); Symbol mu = SymbolNameGenerator.getsym_ChannelViscosity(schematic, channel); Symbol L = SymbolNameGenerator.getsym_ChannelLength(schematic, channel); + + exprs.add(QFNRA.declareRealVariable(R)); + exprs.add(QFNRA.declareRealVariable(w)); + exprs.add(QFNRA.declareRealVariable(h)); + exprs.add(QFNRA.declareRealVariable(mu)); + exprs.add(QFNRA.declareRealVariable(L)); + SExpression resistanceRectangular = QFNRA.assertEqual(R, QFNRA.divide(QFNRA.multiply(new Decimal(12.0), QFNRA.multiply(mu, L)), QFNRA.multiply(w, QFNRA.multiply(QFNRA.pow(h, new Decimal(3.0)), From ffc0eb0a6d33e9d65b63c1d45f4ee7815dd2400b Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 1 Feb 2015 18:36:35 -0500 Subject: [PATCH 07/20] working entry-to-exit synthesis --- .../strategies/placement/FiniteChipAreaRuleStrategy.java | 6 ++---- .../strategies/pressureflow/ChannelResistanceStrategy.java | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/placement/FiniteChipAreaRuleStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/placement/FiniteChipAreaRuleStrategy.java index a708552..4b98581 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/placement/FiniteChipAreaRuleStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/placement/FiniteChipAreaRuleStrategy.java @@ -19,11 +19,9 @@ public class FiniteChipAreaRuleStrategy extends ChipAreaRuleStrategy { protected List translationStep(Schematic schematic, ProcessParameters processParams, PrimitiveTypeTable typeTable) { List exprs = new LinkedList<>(); - // loop through all control points + // loop through all nodes for (NodeValue n : schematic.getNodes().values()) { - if (!(n.getType().isSubtypeOf(typeTable.getControlPointNodeType()))) { - continue; - } + // TODO is microfluidic node? Symbol nodeX = SymbolNameGenerator.getsym_NodeX(schematic, n); Symbol nodeY = SymbolNameGenerator.getsym_NodeY(schematic, n); exprs.add(QFNRA.assertGreater(nodeX, new Decimal(0.0))); diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java index 62b19d5..775d54a 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java @@ -44,10 +44,15 @@ private List translateRectangularChannel( Symbol L = SymbolNameGenerator.getsym_ChannelLength(schematic, channel); exprs.add(QFNRA.declareRealVariable(R)); + exprs.add(QFNRA.assertGreater(R, new Decimal(0.0))); exprs.add(QFNRA.declareRealVariable(w)); + exprs.add(QFNRA.assertGreater(w, new Decimal(0.0))); exprs.add(QFNRA.declareRealVariable(h)); + exprs.add(QFNRA.assertGreater(h, new Decimal(0.0))); exprs.add(QFNRA.declareRealVariable(mu)); + exprs.add(QFNRA.assertGreater(mu, new Decimal(0.0))); exprs.add(QFNRA.declareRealVariable(L)); + exprs.add(QFNRA.assertGreater(L, new Decimal(0.0))); SExpression resistanceRectangular = QFNRA.assertEqual(R, QFNRA.divide(QFNRA.multiply(new Decimal(12.0), QFNRA.multiply(mu, L)), From 754e5a1d67aea548d1472a103632743bfdd44f66 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 10 Feb 2015 22:34:45 -0500 Subject: [PATCH 08/20] add code gen for pressure-volume --- .../strategies/PressureFlowStrategySet.java | 10 +++++ .../pressureflow/PressureFlowStrategy.java | 7 ++++ .../SimplePressureFlowStrategy.java | 40 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/PressureFlowStrategy.java create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java index 3bfa3d8..2dec01f 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/PressureFlowStrategySet.java @@ -9,6 +9,8 @@ import org.manifold.compiler.back.microfluidics.smt2.SExpression; import org.manifold.compiler.back.microfluidics.strategies.pressureflow.ChannelResistanceStrategy; import org.manifold.compiler.back.microfluidics.strategies.pressureflow.FluidEntryExitDeviceStrategy; +import org.manifold.compiler.back.microfluidics.strategies.pressureflow.PressureFlowStrategy; +import org.manifold.compiler.back.microfluidics.strategies.pressureflow.SimplePressureFlowStrategy; import org.manifold.compiler.middle.Schematic; public class PressureFlowStrategySet extends TranslationStrategy { @@ -25,9 +27,15 @@ public void useFluidEntryExitDeviceStrategy( this.entryExitStrategy = strat; } + private PressureFlowStrategy pressureFlow; + public void usePressureFlowStrategy(PressureFlowStrategy strat) { + this.pressureFlow = strat; + } + public PressureFlowStrategySet() { channelResistanceStrategy = new ChannelResistanceStrategy(); entryExitStrategy = new FluidEntryExitDeviceStrategy(); + pressureFlow = new SimplePressureFlowStrategy(); } @Override @@ -38,6 +46,8 @@ protected List translationStep(Schematic schematic, schematic, processParams, typeTable)); exprs.addAll(entryExitStrategy.translate( schematic, processParams, typeTable)); + exprs.addAll(pressureFlow.translate( + schematic, processParams, typeTable)); return exprs; } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/PressureFlowStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/PressureFlowStrategy.java new file mode 100644 index 0000000..45b8fb7 --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/PressureFlowStrategy.java @@ -0,0 +1,7 @@ +package org.manifold.compiler.back.microfluidics.strategies.pressureflow; + +import org.manifold.compiler.back.microfluidics.TranslationStrategy; + +public abstract class PressureFlowStrategy extends TranslationStrategy { + +} diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java new file mode 100644 index 0000000..d7b9a65 --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java @@ -0,0 +1,40 @@ +package org.manifold.compiler.back.microfluidics.strategies.pressureflow; + +import java.util.LinkedList; +import java.util.List; + +import org.manifold.compiler.ConnectionValue; +import org.manifold.compiler.back.microfluidics.PrimitiveTypeTable; +import org.manifold.compiler.back.microfluidics.ProcessParameters; +import org.manifold.compiler.back.microfluidics.smt2.QFNRA; +import org.manifold.compiler.back.microfluidics.smt2.SExpression; +import org.manifold.compiler.back.microfluidics.smt2.Symbol; +import org.manifold.compiler.back.microfluidics.smt2.SymbolNameGenerator; +import org.manifold.compiler.middle.Schematic; + +public class SimplePressureFlowStrategy extends PressureFlowStrategy { + + @Override + protected List translationStep(Schematic schematic, + ProcessParameters processParams, PrimitiveTypeTable typeTable) { + List exprs = new LinkedList<>(); + for (ConnectionValue conn : schematic.getConnections().values()) { + exprs.add(translate(conn, schematic)); + } + return exprs; + } + + private SExpression translate(ConnectionValue conn, + Schematic schematic) { + // for each channel, generate an expression of the form dP = V*R + Symbol P1 = SymbolNameGenerator.getSym_PortPressure(schematic, + conn.getFrom()); + Symbol P2 = SymbolNameGenerator.getSym_PortPressure(schematic, + conn.getTo()); + Symbol V = SymbolNameGenerator.getsym_ChannelFlowRate(schematic, conn); + Symbol R = SymbolNameGenerator.getsym_ChannelResistance(schematic, conn); + return QFNRA.assertEqual(QFNRA.subtract(P1, P2), + QFNRA.multiply(V, R)); + } + +} From e07efd41f6291b3bf9b1a1e05837a3395731229d Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 22 Feb 2015 12:14:53 -0500 Subject: [PATCH 09/20] checkstyle fixes --- .../microfluidics/MicrofluidicsBackend.java | 8 ++-- .../back/microfluidics/smt2/QFNRA.java | 12 +++--- .../ChannelResistanceStrategy.java | 18 +++++---- .../FluidEntryExitDeviceStrategy.java | 39 ++++++++++--------- .../SimplePressureFlowStrategy.java | 12 +++--- .../TestMicrofluidicsBackend.java | 12 +++--- 6 files changed, 52 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java index 785f7ce..9292000 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java @@ -140,7 +140,7 @@ public List sortExprs(List unsorted) { Symbol s = (Symbol) head; if (s.getName().equals("declare-fun")) { decls.add(expr); - } else if(s.getName().equals("assert")) { + } else if (s.getName().equals("assert")) { asserts.add(expr); } else { others.add(expr); @@ -183,14 +183,14 @@ public void run(Schematic schematic) throws IOException { // (check-sat) (exit) exprs.add(new ParenList(new SExpression[] { - new Symbol("check-sat") + new Symbol("check-sat") })); exprs.add(new ParenList(new SExpression[] { - new Symbol("exit") + new Symbol("exit") })); // write to "schematic-name.smt2" String filename = schematic.getName() + ".smt2"; - try(BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { for (SExpression expr : exprs) { expr.write(writer); writer.newLine(); diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java index 74e2a99..7b04612 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java @@ -14,18 +14,18 @@ private static SExpression infix(SExpression e1, String op, SExpression e2) { public static SExpression useQFNRA() { SExpression exprs[] = new SExpression[] { - new Symbol("set-logic"), - new Symbol("QF_NRA") + new Symbol("set-logic"), + new Symbol("QF_NRA") }; return new ParenList(exprs); } public static SExpression declareRealVariable(Symbol var) { SExpression exprs[] = new SExpression[] { - new Symbol("declare-fun"), - var, - new ParenList(), - new Symbol("Real") + new Symbol("declare-fun"), + var, + new ParenList(), + new Symbol("Real") }; return new ParenList(exprs); } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java index 775d54a..901168f 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java @@ -37,25 +37,27 @@ private List translateRectangularChannel( // for channel width w, height h, h < w // total length L // viscosity of the solvent is mu - Symbol R = SymbolNameGenerator.getsym_ChannelResistance(schematic, channel); + Symbol chR = SymbolNameGenerator.getsym_ChannelResistance( + schematic, channel); Symbol w = SymbolNameGenerator.getsym_ChannelWidth(schematic, channel); Symbol h = SymbolNameGenerator.getsym_ChannelHeight(schematic, channel); Symbol mu = SymbolNameGenerator.getsym_ChannelViscosity(schematic, channel); - Symbol L = SymbolNameGenerator.getsym_ChannelLength(schematic, channel); + Symbol chL = SymbolNameGenerator.getsym_ChannelLength(schematic, channel); - exprs.add(QFNRA.declareRealVariable(R)); - exprs.add(QFNRA.assertGreater(R, new Decimal(0.0))); + exprs.add(QFNRA.declareRealVariable(chR)); + exprs.add(QFNRA.assertGreater(chR, new Decimal(0.0))); exprs.add(QFNRA.declareRealVariable(w)); exprs.add(QFNRA.assertGreater(w, new Decimal(0.0))); exprs.add(QFNRA.declareRealVariable(h)); exprs.add(QFNRA.assertGreater(h, new Decimal(0.0))); exprs.add(QFNRA.declareRealVariable(mu)); exprs.add(QFNRA.assertGreater(mu, new Decimal(0.0))); - exprs.add(QFNRA.declareRealVariable(L)); - exprs.add(QFNRA.assertGreater(L, new Decimal(0.0))); + exprs.add(QFNRA.declareRealVariable(chL)); + exprs.add(QFNRA.assertGreater(chL, new Decimal(0.0))); - SExpression resistanceRectangular = QFNRA.assertEqual(R, - QFNRA.divide(QFNRA.multiply(new Decimal(12.0), QFNRA.multiply(mu, L)), + SExpression resistanceRectangular = QFNRA.assertEqual(chR, + QFNRA.divide(QFNRA.multiply( + new Decimal(12.0), QFNRA.multiply(mu, chL)), QFNRA.multiply(w, QFNRA.multiply(QFNRA.pow(h, new Decimal(3.0)), QFNRA.subtract(new Decimal(1.0), QFNRA.multiply(new Decimal(0.630), QFNRA.divide(h, w))))))); diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java index d5980e3..8e48e6e 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java @@ -38,31 +38,32 @@ protected ConnectionValue getConnection( protected List translationStep(Schematic schematic, ProcessParameters processParams, PrimitiveTypeTable typeTable) { List exprs = new LinkedList<>(); - for (NodeValue node : schematic.getNodes().values()) { - try { - if (node.getType().isSubtypeOf(typeTable.getFluidEntryNodeType())) { - exprs.addAll(translateFluidEntryNode(schematic, node)); - } else if (node.getType().isSubtypeOf(typeTable.getFluidExitNodeType())) { - exprs.addAll(translateFluidExitNode(schematic, node)); - } - } catch (UndeclaredIdentifierException e) { - throw new CodeGenerationError("undeclared identifier '" - + e.getIdentifier() + "' when inspecting fluid entry/exit node '" - + schematic.getNodeName(node) + "'; " - + "possible schematic version mismatch"); - } catch (UndeclaredAttributeException e) { - throw new CodeGenerationError("undeclared attribute " - + " when inspecting fluid entry/exit node '" - + schematic.getNodeName(node) + "'; " - + "possible schematic version mismatch"); + for (NodeValue node : schematic.getNodes().values()) { + try { + if (node.getType().isSubtypeOf(typeTable.getFluidEntryNodeType())) { + exprs.addAll(translateFluidEntryNode(schematic, node)); + } else if (node.getType().isSubtypeOf( + typeTable.getFluidExitNodeType())) { + exprs.addAll(translateFluidExitNode(schematic, node)); } + } catch (UndeclaredIdentifierException e) { + throw new CodeGenerationError("undeclared identifier '" + + e.getIdentifier() + "' when inspecting fluid entry/exit node '" + + schematic.getNodeName(node) + "'; " + + "possible schematic version mismatch"); + } catch (UndeclaredAttributeException e) { + throw new CodeGenerationError("undeclared attribute " + + " when inspecting fluid entry/exit node '" + + schematic.getNodeName(node) + "'; " + + "possible schematic version mismatch"); } + } return exprs; } private List translateFluidEntryNode( Schematic schematic, NodeValue node) - throws UndeclaredIdentifierException, UndeclaredAttributeException { + throws UndeclaredIdentifierException, UndeclaredAttributeException { List exprs = new LinkedList<>(); exprs.add(QFNRA.declareRealVariable( SymbolNameGenerator.getsym_NodeX(schematic, node))); @@ -82,7 +83,7 @@ private List translateFluidEntryNode( private List translateFluidExitNode( Schematic schematic, NodeValue node) - throws UndeclaredIdentifierException { + throws UndeclaredIdentifierException { List exprs = new LinkedList<>(); exprs.add(QFNRA.declareRealVariable( SymbolNameGenerator.getsym_NodeX(schematic, node))); diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java index d7b9a65..3ef2fc1 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java @@ -27,14 +27,14 @@ protected List translationStep(Schematic schematic, private SExpression translate(ConnectionValue conn, Schematic schematic) { // for each channel, generate an expression of the form dP = V*R - Symbol P1 = SymbolNameGenerator.getSym_PortPressure(schematic, + Symbol p1 = SymbolNameGenerator.getSym_PortPressure(schematic, conn.getFrom()); - Symbol P2 = SymbolNameGenerator.getSym_PortPressure(schematic, + Symbol p2 = SymbolNameGenerator.getSym_PortPressure(schematic, conn.getTo()); - Symbol V = SymbolNameGenerator.getsym_ChannelFlowRate(schematic, conn); - Symbol R = SymbolNameGenerator.getsym_ChannelResistance(schematic, conn); - return QFNRA.assertEqual(QFNRA.subtract(P1, P2), - QFNRA.multiply(V, R)); + Symbol chV = SymbolNameGenerator.getsym_ChannelFlowRate(schematic, conn); + Symbol chR = SymbolNameGenerator.getsym_ChannelResistance(schematic, conn); + return QFNRA.assertEqual(QFNRA.subtract(p1, p2), + QFNRA.multiply(chV, chR)); } } diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index 35ed8da..a0b6738 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -29,12 +29,12 @@ public static void setUpClass() { @Test public void testTJunctionSynthesis() throws Exception { String[] args = { - "-bProcessMinimumNodeDistance", "0.0001", - "-bProcessMinimumChannelLength", "0.0001", - "-bProcessMaximumChipSizeX", "0.04", - "-bProcessMaximumChipSizeY", "0.04", - "-bProcessCriticalCrossingAngle", "0.0872664626" - }; + "-bProcessMinimumNodeDistance", "0.0001", + "-bProcessMinimumChannelLength", "0.0001", + "-bProcessMaximumChipSizeX", "0.04", + "-bProcessMaximumChipSizeY", "0.04", + "-bProcessCriticalCrossingAngle", "0.0872664626" + }; Schematic schematic = UtilSchematicConstruction .instantiateSchematic("test"); From 8f4b44f2ec7c38db49237a7b2dc0a5d21e7e2f31 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 28 Feb 2015 18:35:39 -0500 Subject: [PATCH 10/20] address code review --- .../back/microfluidics/strategies/MultiPhaseStrategySet.java | 1 + .../back/microfluidics/TestMicrofluidicsBackend.java | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java index 6d00508..1d9ad93 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/MultiPhaseStrategySet.java @@ -11,6 +11,7 @@ import org.manifold.compiler.back.microfluidics.strategies.multiphase.TJunctionDeviceStrategy; import org.manifold.compiler.middle.Schematic; +// Contains strategies for generation of multi-phase circuits public class MultiPhaseStrategySet extends TranslationStrategy { private DropletConstraintStrategy dropletConstraintStrategy; diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index a0b6738..96c9c8d 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -15,6 +15,8 @@ public class TestMicrofluidicsBackend { + public static double viscosityOfWater = 0.001002; // Pa*s + @BeforeClass public static void setUpClass() { LogManager.getRootLogger().setLevel(Level.ALL); @@ -41,9 +43,8 @@ public void testTJunctionSynthesis() throws Exception { // Make a very simple schematic: // (fluidEntry) ---> (fluidExit) - // Water has a viscosity of 0.001002 Pa*s NodeValue entry = UtilSchematicConstruction.instantiateFluidEntry( - schematic, 0.001002); + schematic, viscosityOfWater); schematic.addNode("n_entry", entry); NodeValue exit = UtilSchematicConstruction.instantiateFluidExit(schematic); schematic.addNode("n_exit", exit); From 04d20b95258ff9afa093669223553e44de0164df Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 5 Mar 2015 14:05:48 -0500 Subject: [PATCH 11/20] rename schematic to match test name --- .../compiler/back/microfluidics/TestMicrofluidicsBackend.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index 96c9c8d..b5d7962 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -39,7 +39,7 @@ public void testTJunctionSynthesis() throws Exception { }; Schematic schematic = UtilSchematicConstruction - .instantiateSchematic("test"); + .instantiateSchematic("testTJunctionSynthesis"); // Make a very simple schematic: // (fluidEntry) ---> (fluidExit) From 181eb1a584f767ef4f997fd2a254a19843bd33f3 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 9 Mar 2015 16:15:52 -0400 Subject: [PATCH 12/20] change node and channel names in test case --- .../back/microfluidics/TestMicrofluidicsBackend.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index 35ed8da..278377e 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -44,12 +44,12 @@ public void testTJunctionSynthesis() throws Exception { // Water has a viscosity of 0.001002 Pa*s NodeValue entry = UtilSchematicConstruction.instantiateFluidEntry( schematic, 0.001002); - schematic.addNode("n_entry", entry); + schematic.addNode("in0", entry); NodeValue exit = UtilSchematicConstruction.instantiateFluidExit(schematic); - schematic.addNode("n_exit", exit); + schematic.addNode("out0", exit); ConnectionValue entryToExit = UtilSchematicConstruction.instantiateChannel( entry.getPort("output"), exit.getPort("input")); - schematic.addConnection("c_entry_to_exit", entryToExit); + schematic.addConnection("channel0", entryToExit); // TODO constrain the pressure in the channel to be 0.001 Pa MicrofluidicsBackend backend = new MicrofluidicsBackend(); From e4717c0794a4eccabd57e833c621b59bfc9d8117 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 15 Mar 2015 21:31:24 -0400 Subject: [PATCH 13/20] add constraints and generate code for t-junctions --- examples/tjunction.smt2 | 31 +++++++++++ .../multiphase/TJunctionDeviceStrategy.java | 19 +++++++ .../TestMicrofluidicsBackend.java | 51 +++++++++++++++++-- .../UtilSchematicConstruction.java | 9 +++- 4 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 examples/tjunction.smt2 diff --git a/examples/tjunction.smt2 b/examples/tjunction.smt2 new file mode 100644 index 0000000..00f229b --- /dev/null +++ b/examples/tjunction.smt2 @@ -0,0 +1,31 @@ +(set-logic QF_NRA) + +(declare-fun rho () Real) +(declare-fun pressure_Cin () Real) +(declare-fun flow_Cin () Real) +(declare-fun res_Cin () Real) +(declare-fun pressure_J () Real) +(declare-fun flow_D () Real) +(declare-fun flow_Ef () Real) +(declare-fun res_Ef () Real) +(declare-fun pressure_G () Real) +(declare-fun flow_Eb () Real) +(declare-fun res_Eb () Real) +(declare-fun pressure_W () Real) + +(assert (= rho 0.013503)) ; rho = resistance per micrometer = 8.1016 * 10e-6 * 10000/60 +(assert (= res_Cin (* rho 100000))) +(assert (= res_Ef (* rho 50000))) +(assert (= res_Eb (* rho 50000))) +;(assert (= pressure_Cin 1500.0)) ; input pressure = 1500 millibars +(assert (= flow_Cin 7.0)) ; input flow rate = 7 uL/min +(assert (= (- pressure_Cin pressure_J) (* res_Cin flow_Cin))) ; Pin - Pj = Rin * Qin +(assert (= flow_D 7.0)) ; disperse flow rate = 7 uL/min +(assert (= flow_Ef (+ flow_Cin flow_D))) ; Qin + Qd = Qef +(assert (= (- pressure_J pressure_G) (* res_Ef flow_Ef))) ; Pj - Pg = Ref * Qef +(assert (= flow_Ef flow_Eb)) ; Qef = Qeb +(assert (= (- pressure_G pressure_W) (* res_Eb flow_Eb))) ; Pg - Pw = Reb * Qeb +(assert (= pressure_W 1013.0)) ; output pressure = 1013 millibars + +(check-sat) +(exit) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java index 93946a5..5f799d3 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java @@ -121,6 +121,8 @@ private List translateTJunction(Schematic schematic, .getsym_ChannelFlowRate(schematic, chContinuous); Symbol qD = SymbolNameGenerator .getsym_ChannelFlowRate(schematic, chDispersed); + Symbol qOut = SymbolNameGenerator + .getsym_ChannelFlowRate(schematic, chOutput); Symbol epsilon = SymbolNameGenerator .getsym_TJunctionEpsilon(schematic, junction); SExpression qGutterByQC = new Decimal(0.1); @@ -156,6 +158,23 @@ private List translateTJunction(Schematic schematic, // constraint: epsilon is non-negative exprs.add(QFNRA.assertGreaterEqual(epsilon, new Numeral(0))); + // constraint: all port pressures are equalized + Symbol nodePressure = SymbolNameGenerator.getSym_NodePressure( + schematic, junction); + Symbol continuousPressure = SymbolNameGenerator.getSym_PortPressure( + schematic, pContinuous); + Symbol dispersePressure = SymbolNameGenerator.getSym_PortPressure( + schematic, pDispersed); + Symbol outputPressure = SymbolNameGenerator.getSym_PortPressure( + schematic, pOutput); + exprs.add(QFNRA.assertEqual(nodePressure, continuousPressure)); + exprs.add(QFNRA.assertEqual(nodePressure, dispersePressure)); + exprs.add(QFNRA.assertEqual(nodePressure, outputPressure)); + + // constraint: the sum of flow rates is zero + exprs.add(QFNRA.assertEqual(new Decimal(0.0), + QFNRA.add(QFNRA.add(qC, qD), qOut))); + /* There are two expressions given for normalized-Vfill. * The (MUCH) simpler expression applies when wIn <= w; * the complex expression applies when wIn > w. diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index d88c7e1..8a7d423 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -29,7 +29,7 @@ public static void setUpClass() { } @Test - public void testTJunctionSynthesis() throws Exception { + public void testSimpleSynthesis() throws Exception { String[] args = { "-bProcessMinimumNodeDistance", "0.0001", "-bProcessMinimumChannelLength", "0.0001", @@ -39,7 +39,7 @@ public void testTJunctionSynthesis() throws Exception { }; Schematic schematic = UtilSchematicConstruction - .instantiateSchematic("testTJunctionSynthesis"); + .instantiateSchematic("testSimpleSynthesis"); // Make a very simple schematic: // (fluidEntry) ---> (fluidExit) @@ -51,7 +51,52 @@ public void testTJunctionSynthesis() throws Exception { ConnectionValue entryToExit = UtilSchematicConstruction.instantiateChannel( entry.getPort("output"), exit.getPort("input")); schematic.addConnection("channel0", entryToExit); - // TODO constrain the pressure in the channel to be 0.001 Pa + + MicrofluidicsBackend backend = new MicrofluidicsBackend(); + Options options = new Options(); + backend.registerArguments(options); + CommandLineParser parser = new org.apache.commons.cli.BasicParser(); + CommandLine cmd = parser.parse(options, args); + backend.invokeBackend(schematic, cmd); + } + + @Test + public void testTJunctionSynthesis() throws Exception { + String[] args = { + "-bProcessMinimumNodeDistance", "0.0001", + "-bProcessMinimumChannelLength", "0.0001", + "-bProcessMaximumChipSizeX", "0.04", + "-bProcessMaximumChipSizeY", "0.04", + "-bProcessCriticalCrossingAngle", "0.0872664626" + }; + + Schematic schematic = UtilSchematicConstruction + .instantiateSchematic("testTJunctionSynthesis"); + + // Make a schematic with two inputs, one output, and a T-junction + NodeValue entry = UtilSchematicConstruction.instantiateFluidEntry( + schematic, viscosityOfWater); + schematic.addNode("in0", entry); + NodeValue disperse = UtilSchematicConstruction.instantiateFluidEntry( + schematic, viscosityOfWater); + schematic.addNode("in1", disperse); + NodeValue exit = UtilSchematicConstruction.instantiateFluidExit(schematic); + schematic.addNode("out0", exit); + + NodeValue junction = UtilSchematicConstruction + .instantiateTJunction(schematic); + schematic.addNode("junction0", junction); + + + ConnectionValue entryToJunction = UtilSchematicConstruction.instantiateChannel( + entry.getPort("output"), junction.getPort("continuous")); + schematic.addConnection("channelC", entryToJunction); + ConnectionValue disperseToJunction = UtilSchematicConstruction.instantiateChannel( + disperse.getPort("output"), junction.getPort("dispersed")); + schematic.addConnection("channelD", disperseToJunction); + ConnectionValue junctionToExit = UtilSchematicConstruction.instantiateChannel( + junction.getPort("output"), exit.getPort("input")); + schematic.addConnection("channelE", junctionToExit); MicrofluidicsBackend backend = new MicrofluidicsBackend(); Options options = new Options(); diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java b/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java index 5957b0e..31423f3 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/UtilSchematicConstruction.java @@ -182,8 +182,13 @@ public static NodeValue instantiateFluidExit(Schematic schematic) public static NodeValue instantiateTJunction(Schematic schematic) throws SchematicException { - // TODO - return null; + Map> portAttrsMap = new HashMap<>(); + portAttrsMap.put("continuous", noAttributes); + portAttrsMap.put("dispersed", noAttributes); + portAttrsMap.put("output", noAttributes); + NodeValue tj = new NodeValue(schematic.getNodeType("tJunction"), + noAttributes, portAttrsMap); + return tj; } /** From ab7640490ddb20d02ad1d04df391fc906c7f2de1 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 16 Mar 2015 15:52:22 -0400 Subject: [PATCH 14/20] declare missing variables; generate flow conservation correctly --- .../microfluidics/MicrofluidicsBackend.java | 8 +++ .../back/microfluidics/smt2/Macros.java | 59 +++++++++++++++++++ .../back/microfluidics/smt2/QFNRA.java | 10 ++++ .../multiphase/TJunctionDeviceStrategy.java | 26 ++++++-- .../SimplePressureFlowStrategy.java | 16 +++-- .../TestMicrofluidicsBackend.java | 4 +- 6 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java index 9292000..cfaea8b 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/MicrofluidicsBackend.java @@ -13,10 +13,12 @@ import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.manifold.compiler.Backend; +import org.manifold.compiler.back.microfluidics.smt2.Decimal; import org.manifold.compiler.back.microfluidics.smt2.ParenList; import org.manifold.compiler.back.microfluidics.smt2.QFNRA; import org.manifold.compiler.back.microfluidics.smt2.SExpression; import org.manifold.compiler.back.microfluidics.smt2.Symbol; +import org.manifold.compiler.back.microfluidics.smt2.SymbolNameGenerator; import org.manifold.compiler.back.microfluidics.strategies.MultiPhaseStrategySet; import org.manifold.compiler.back.microfluidics.strategies.PlacementTranslationStrategySet; import org.manifold.compiler.back.microfluidics.strategies.PressureFlowStrategySet; @@ -167,6 +169,12 @@ public void run(Schematic schematic) throws IOException { exprs.add(QFNRA.useQFNRA()); List unsortedExprs = new LinkedList<>(); + // define constant pi + unsortedExprs.add(QFNRA.declareRealVariable( + SymbolNameGenerator.getsym_constant_pi())); + unsortedExprs.add(QFNRA.assertEqual( + SymbolNameGenerator.getsym_constant_pi(), + new Decimal(Math.PI))); PlacementTranslationStrategySet placeSet = new PlacementTranslationStrategySet(); diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java new file mode 100644 index 0000000..a378f36 --- /dev/null +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java @@ -0,0 +1,59 @@ +package org.manifold.compiler.back.microfluidics.smt2; + +import java.util.LinkedList; +import java.util.List; + +import org.manifold.compiler.ConnectionValue; +import org.manifold.compiler.PortValue; +import org.manifold.compiler.back.microfluidics.CodeGenerationError; +import org.manifold.compiler.middle.Schematic; + +public class Macros { + + //get the connection associated with this port + // TODO this is VERY EXPENSIVE, find an optimization + protected static ConnectionValue getConnection( + Schematic schematic, PortValue port) { + for (ConnectionValue conn : schematic.getConnections().values()) { + if (conn.getFrom().equals(port) || conn.getTo().equals(port)) { + return conn; + } + } + return null; + } + + // generate an expression that describes the constraint + // "total flow in = total flow out" + // this is difficult because the actual flow direction may be backwards + // with respect to the expected direction + public static SExpression generateConservationOfFlow(Schematic schematic, + List connectedPorts) { + List flowRatesIn = new LinkedList(); + List flowRatesOut = new LinkedList(); + for (PortValue port : connectedPorts) { + ConnectionValue channel = getConnection(schematic, port); + boolean connectedIntoJunction; + // check which way the channel is connected + if (channel.getFrom().equals(port)) { + connectedIntoJunction = false; + } else if (channel.getTo().equals(port)) { + connectedIntoJunction = true; + } else { + throw new CodeGenerationError("attempt to generate flow direction " + + "constraint for a channel that is disconnected from the " + + "target port"); + } + Symbol flowRate = SymbolNameGenerator + .getsym_ChannelFlowRate(schematic, channel); + if (connectedIntoJunction) { + // flow rate is positive into the junction + flowRatesIn.add(flowRate); + } else { + // flow rate is positive out of the junction + flowRatesOut.add(flowRate); + } + } + return QFNRA.assertEqual(QFNRA.add(flowRatesIn), QFNRA.add(flowRatesOut)); + } + +} diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java index 7b04612..fb63bf5 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/QFNRA.java @@ -1,5 +1,8 @@ package org.manifold.compiler.back.microfluidics.smt2; +import java.util.LinkedList; +import java.util.List; + // Helper class for generating valid QF_NRA S-expressions. public class QFNRA { @@ -34,6 +37,13 @@ public static SExpression add(SExpression e1, SExpression e2) { return infix(e1, "+", e2); } + public static SExpression add(List terms) { + List exprs = new LinkedList(); + exprs.add(new Symbol("+")); + exprs.addAll(terms); + return new ParenList(exprs); + } + public static SExpression subtract(SExpression e1, SExpression e2) { return infix(e1, "-", e2); } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java index 5f799d3..1388c34 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java @@ -13,6 +13,7 @@ import org.manifold.compiler.back.microfluidics.ProcessParameters; import org.manifold.compiler.back.microfluidics.TranslationStrategy; import org.manifold.compiler.back.microfluidics.smt2.Decimal; +import org.manifold.compiler.back.microfluidics.smt2.Macros; import org.manifold.compiler.back.microfluidics.smt2.Numeral; import org.manifold.compiler.back.microfluidics.smt2.QFNRA; import org.manifold.compiler.back.microfluidics.smt2.SExpression; @@ -89,7 +90,7 @@ private SExpression constrainFlowDirection(Schematic schematic, // if the connection direction and constraint direction are different, // the flow rate must be negative; otherwise the flow is in // the same direction as the channel and so the flow is positive - if (connectedIntoJunction ^ isOutput) { + if (!(connectedIntoJunction ^ isOutput)) { // negative flow return (QFNRA.assertLessThan(flowRate, new Numeral(0))); } else { @@ -110,6 +111,11 @@ private List translateTJunction(Schematic schematic, */ List exprs = new LinkedList<>(); + Symbol nodeX = SymbolNameGenerator.getsym_NodeX(schematic, junction); + Symbol nodeY = SymbolNameGenerator.getsym_NodeY(schematic, junction); + exprs.add(QFNRA.declareRealVariable(nodeX)); + exprs.add(QFNRA.declareRealVariable(nodeY)); + // channel/junction characteristics Symbol h = SymbolNameGenerator .getsym_ChannelHeight(schematic, chContinuous); @@ -128,6 +134,9 @@ private List translateTJunction(Schematic schematic, SExpression qGutterByQC = new Decimal(0.1); Symbol pi = SymbolNameGenerator.getsym_constant_pi(); + // declare epsilon + exprs.add(QFNRA.declareRealVariable(epsilon)); + // TODO constraint: all channels must be rectangular // TODO (?) constraint: continuous and output channel must be parallel // TODO (?) constraint: disperse and output channel must be perpendicular @@ -167,13 +176,21 @@ private List translateTJunction(Schematic schematic, schematic, pDispersed); Symbol outputPressure = SymbolNameGenerator.getSym_PortPressure( schematic, pOutput); + // declare these too + exprs.add(QFNRA.declareRealVariable(nodePressure)); + exprs.add(QFNRA.declareRealVariable(continuousPressure)); + exprs.add(QFNRA.declareRealVariable(dispersePressure)); + exprs.add(QFNRA.declareRealVariable(outputPressure)); exprs.add(QFNRA.assertEqual(nodePressure, continuousPressure)); exprs.add(QFNRA.assertEqual(nodePressure, dispersePressure)); exprs.add(QFNRA.assertEqual(nodePressure, outputPressure)); - // constraint: the sum of flow rates is zero - exprs.add(QFNRA.assertEqual(new Decimal(0.0), - QFNRA.add(QFNRA.add(qC, qD), qOut))); + // constraint: flow in = flow out + List connectedPorts = new LinkedList(); + connectedPorts.add(pContinuous); + connectedPorts.add(pDispersed); + connectedPorts.add(pOutput); + exprs.add(Macros.generateConservationOfFlow(schematic, connectedPorts)); /* There are two expressions given for normalized-Vfill. * The (MUCH) simpler expression applies when wIn <= w; @@ -272,6 +289,7 @@ private List translateTJunction(Schematic schematic, // Voutput/hw^2 = Vfill/hw^2 + alpha * Qd/Qc Symbol vOutput = SymbolNameGenerator .getsym_ChannelDropletVolume(schematic, chOutput); + exprs.add(QFNRA.declareRealVariable(vOutput)); exprs.add(QFNRA.assertEqual(vOutput, QFNRA.multiply(QFNRA.multiply(h, QFNRA.multiply(w, w)), QFNRA.add(normalizedVFill, diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java index 3ef2fc1..bd2cbe8 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java @@ -19,13 +19,15 @@ protected List translationStep(Schematic schematic, ProcessParameters processParams, PrimitiveTypeTable typeTable) { List exprs = new LinkedList<>(); for (ConnectionValue conn : schematic.getConnections().values()) { - exprs.add(translate(conn, schematic)); + exprs.addAll(translate(conn, schematic)); } return exprs; } - private SExpression translate(ConnectionValue conn, + private List translate(ConnectionValue conn, Schematic schematic) { + List exprs = new LinkedList<>(); + // for each channel, generate an expression of the form dP = V*R Symbol p1 = SymbolNameGenerator.getSym_PortPressure(schematic, conn.getFrom()); @@ -33,8 +35,14 @@ private SExpression translate(ConnectionValue conn, conn.getTo()); Symbol chV = SymbolNameGenerator.getsym_ChannelFlowRate(schematic, conn); Symbol chR = SymbolNameGenerator.getsym_ChannelResistance(schematic, conn); - return QFNRA.assertEqual(QFNRA.subtract(p1, p2), - QFNRA.multiply(chV, chR)); + // assume the port pressures and resistance are declared elsewhere; + // we still need to declare the flow rate + exprs.add(QFNRA.declareRealVariable(chV)); + + exprs.add(QFNRA.assertEqual(QFNRA.subtract(p1, p2), + QFNRA.multiply(chV, chR))); + + return exprs; } } diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index 8a7d423..d3cd942 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -65,8 +65,8 @@ public void testTJunctionSynthesis() throws Exception { String[] args = { "-bProcessMinimumNodeDistance", "0.0001", "-bProcessMinimumChannelLength", "0.0001", - "-bProcessMaximumChipSizeX", "0.04", - "-bProcessMaximumChipSizeY", "0.04", + "-bProcessMaximumChipSizeX", "0.10", + "-bProcessMaximumChipSizeY", "0.10", "-bProcessCriticalCrossingAngle", "0.0872664626" }; From ea04c90619884c76b2f76c6e22cb0ac5248d78e9 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 19 Mar 2015 15:42:16 -0400 Subject: [PATCH 15/20] constraint: output viscosity = continuous viscosity (also, set epsilon=0 for sharp-edged T-junctions) --- .../multiphase/TJunctionDeviceStrategy.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java index 1388c34..5ef2000 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java @@ -164,8 +164,8 @@ private List translateTJunction(Schematic schematic, .getsym_ChannelHeight(schematic, chContinuous), SymbolNameGenerator .getsym_ChannelHeight(schematic, chOutput))); - // constraint: epsilon is non-negative - exprs.add(QFNRA.assertGreaterEqual(epsilon, new Numeral(0))); + // constraint: epsilon is zero (sharp-edged t-junction) + exprs.add(QFNRA.assertEqual(epsilon, new Numeral(0))); // constraint: all port pressures are equalized Symbol nodePressure = SymbolNameGenerator.getSym_NodePressure( @@ -192,6 +192,13 @@ private List translateTJunction(Schematic schematic, connectedPorts.add(pOutput); exprs.add(Macros.generateConservationOfFlow(schematic, connectedPorts)); + // constraint: viscosity of output = viscosity of continuous + Symbol continuousViscosity = SymbolNameGenerator.getsym_ChannelViscosity( + schematic, chContinuous); + Symbol outputViscosity = SymbolNameGenerator.getsym_ChannelViscosity( + schematic, chOutput); + exprs.add(QFNRA.assertEqual(continuousViscosity, outputViscosity)); + /* There are two expressions given for normalized-Vfill. * The (MUCH) simpler expression applies when wIn <= w; * the complex expression applies when wIn > w. From 168d5b46e373997cf0cc6b5bc7b63434c212beef Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 19 Mar 2015 15:47:55 -0400 Subject: [PATCH 16/20] refactor: calculatedDropletVolume() --- .../multiphase/TJunctionDeviceStrategy.java | 197 +++++++++--------- 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java index 5ef2000..8f5dafb 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java @@ -99,106 +99,18 @@ private SExpression constrainFlowDirection(Schematic schematic, } } - private List translateTJunction(Schematic schematic, - NodeValue junction, - ConnectionValue chContinuous, ConnectionValue chDispersed, - ConnectionValue chOutput) throws UndeclaredIdentifierException { + private SExpression calculatedDropletVolume(SExpression h, SExpression w, + SExpression wIn, SExpression epsilon, SExpression qD, SExpression qC) { /* Predictive model for the size of bubbles and droplets * created in microfluidic T-junctions. * van Steijn, Kleijn, and Kreutzer. * Lab Chip, 2010, 10, 2513. * doi:10.1039/c002625e */ - List exprs = new LinkedList<>(); - - Symbol nodeX = SymbolNameGenerator.getsym_NodeX(schematic, junction); - Symbol nodeY = SymbolNameGenerator.getsym_NodeY(schematic, junction); - exprs.add(QFNRA.declareRealVariable(nodeX)); - exprs.add(QFNRA.declareRealVariable(nodeY)); - // channel/junction characteristics - Symbol h = SymbolNameGenerator - .getsym_ChannelHeight(schematic, chContinuous); - Symbol w = SymbolNameGenerator - .getsym_ChannelWidth(schematic, chContinuous); - Symbol wIn = SymbolNameGenerator - .getsym_ChannelWidth(schematic, chDispersed); - Symbol qC = SymbolNameGenerator - .getsym_ChannelFlowRate(schematic, chContinuous); - Symbol qD = SymbolNameGenerator - .getsym_ChannelFlowRate(schematic, chDispersed); - Symbol qOut = SymbolNameGenerator - .getsym_ChannelFlowRate(schematic, chOutput); - Symbol epsilon = SymbolNameGenerator - .getsym_TJunctionEpsilon(schematic, junction); SExpression qGutterByQC = new Decimal(0.1); Symbol pi = SymbolNameGenerator.getsym_constant_pi(); - // declare epsilon - exprs.add(QFNRA.declareRealVariable(epsilon)); - - // TODO constraint: all channels must be rectangular - // TODO (?) constraint: continuous and output channel must be parallel - // TODO (?) constraint: disperse and output channel must be perpendicular - // constraint: flow rates must be positive into the junction at inputs - PortValue pContinuous = junction.getPort("continuous"); - exprs.add(constrainFlowDirection( - schematic, pContinuous, chContinuous, false)); - PortValue pDispersed = junction.getPort("dispersed"); - exprs.add(constrainFlowDirection( - schematic, pDispersed, chDispersed, false)); - // constraint: flow rate must be positive out of the junction at output - PortValue pOutput = junction.getPort("output"); - exprs.add(constrainFlowDirection( - schematic, pOutput, chOutput, true)); - // constraint: channel width must be equal at the continuous medium - // port and the output port - exprs.add(QFNRA.assertEqual(w, - SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput))); - - // constraint: the height of all connected channels is equal - exprs.add(QFNRA.assertEqual(SymbolNameGenerator - .getsym_ChannelHeight(schematic, chContinuous), SymbolNameGenerator - .getsym_ChannelHeight(schematic, chDispersed))); - exprs.add(QFNRA.assertEqual(SymbolNameGenerator - .getsym_ChannelHeight(schematic, chContinuous), SymbolNameGenerator - .getsym_ChannelHeight(schematic, chOutput))); - - // constraint: epsilon is zero (sharp-edged t-junction) - exprs.add(QFNRA.assertEqual(epsilon, new Numeral(0))); - - // constraint: all port pressures are equalized - Symbol nodePressure = SymbolNameGenerator.getSym_NodePressure( - schematic, junction); - Symbol continuousPressure = SymbolNameGenerator.getSym_PortPressure( - schematic, pContinuous); - Symbol dispersePressure = SymbolNameGenerator.getSym_PortPressure( - schematic, pDispersed); - Symbol outputPressure = SymbolNameGenerator.getSym_PortPressure( - schematic, pOutput); - // declare these too - exprs.add(QFNRA.declareRealVariable(nodePressure)); - exprs.add(QFNRA.declareRealVariable(continuousPressure)); - exprs.add(QFNRA.declareRealVariable(dispersePressure)); - exprs.add(QFNRA.declareRealVariable(outputPressure)); - exprs.add(QFNRA.assertEqual(nodePressure, continuousPressure)); - exprs.add(QFNRA.assertEqual(nodePressure, dispersePressure)); - exprs.add(QFNRA.assertEqual(nodePressure, outputPressure)); - - // constraint: flow in = flow out - List connectedPorts = new LinkedList(); - connectedPorts.add(pContinuous); - connectedPorts.add(pDispersed); - connectedPorts.add(pOutput); - exprs.add(Macros.generateConservationOfFlow(schematic, connectedPorts)); - - // constraint: viscosity of output = viscosity of continuous - Symbol continuousViscosity = SymbolNameGenerator.getsym_ChannelViscosity( - schematic, chContinuous); - Symbol outputViscosity = SymbolNameGenerator.getsym_ChannelViscosity( - schematic, chOutput); - exprs.add(QFNRA.assertEqual(continuousViscosity, outputViscosity)); - /* There are two expressions given for normalized-Vfill. * The (MUCH) simpler expression applies when wIn <= w; * the complex expression applies when wIn > w. @@ -294,13 +206,110 @@ private List translateTJunction(Schematic schematic, QFNRA.divide(h, w)))))); // the droplet volume at the output (Voutput) is given by // Voutput/hw^2 = Vfill/hw^2 + alpha * Qd/Qc + return QFNRA.multiply(QFNRA.multiply(h, QFNRA.multiply(w, w)), + QFNRA.add(normalizedVFill, + QFNRA.multiply(alpha, QFNRA.divide(qD, qC)))); + } + + private List translateTJunction(Schematic schematic, + NodeValue junction, + ConnectionValue chContinuous, ConnectionValue chDispersed, + ConnectionValue chOutput) throws UndeclaredIdentifierException { + List exprs = new LinkedList<>(); + + Symbol nodeX = SymbolNameGenerator.getsym_NodeX(schematic, junction); + Symbol nodeY = SymbolNameGenerator.getsym_NodeY(schematic, junction); + exprs.add(QFNRA.declareRealVariable(nodeX)); + exprs.add(QFNRA.declareRealVariable(nodeY)); + + // channel/junction characteristics + Symbol h = SymbolNameGenerator + .getsym_ChannelHeight(schematic, chContinuous); + Symbol w = SymbolNameGenerator + .getsym_ChannelWidth(schematic, chContinuous); + Symbol wIn = SymbolNameGenerator + .getsym_ChannelWidth(schematic, chDispersed); + Symbol qC = SymbolNameGenerator + .getsym_ChannelFlowRate(schematic, chContinuous); + Symbol qD = SymbolNameGenerator + .getsym_ChannelFlowRate(schematic, chDispersed); + Symbol qOut = SymbolNameGenerator + .getsym_ChannelFlowRate(schematic, chOutput); + Symbol epsilon = SymbolNameGenerator + .getsym_TJunctionEpsilon(schematic, junction); + Symbol pi = SymbolNameGenerator.getsym_constant_pi(); + + // declare epsilon + exprs.add(QFNRA.declareRealVariable(epsilon)); + + // TODO constraint: all channels must be rectangular + // TODO (?) constraint: continuous and output channel must be parallel + // TODO (?) constraint: disperse and output channel must be perpendicular + // constraint: flow rates must be positive into the junction at inputs + PortValue pContinuous = junction.getPort("continuous"); + exprs.add(constrainFlowDirection( + schematic, pContinuous, chContinuous, false)); + PortValue pDispersed = junction.getPort("dispersed"); + exprs.add(constrainFlowDirection( + schematic, pDispersed, chDispersed, false)); + // constraint: flow rate must be positive out of the junction at output + PortValue pOutput = junction.getPort("output"); + exprs.add(constrainFlowDirection( + schematic, pOutput, chOutput, true)); + // constraint: channel width must be equal at the continuous medium + // port and the output port + exprs.add(QFNRA.assertEqual(w, + SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput))); + + // constraint: the height of all connected channels is equal + exprs.add(QFNRA.assertEqual(SymbolNameGenerator + .getsym_ChannelHeight(schematic, chContinuous), SymbolNameGenerator + .getsym_ChannelHeight(schematic, chDispersed))); + exprs.add(QFNRA.assertEqual(SymbolNameGenerator + .getsym_ChannelHeight(schematic, chContinuous), SymbolNameGenerator + .getsym_ChannelHeight(schematic, chOutput))); + + // constraint: epsilon is zero (sharp-edged t-junction) + exprs.add(QFNRA.assertEqual(epsilon, new Numeral(0))); + + // constraint: all port pressures are equalized + Symbol nodePressure = SymbolNameGenerator.getSym_NodePressure( + schematic, junction); + Symbol continuousPressure = SymbolNameGenerator.getSym_PortPressure( + schematic, pContinuous); + Symbol dispersePressure = SymbolNameGenerator.getSym_PortPressure( + schematic, pDispersed); + Symbol outputPressure = SymbolNameGenerator.getSym_PortPressure( + schematic, pOutput); + // declare these too + exprs.add(QFNRA.declareRealVariable(nodePressure)); + exprs.add(QFNRA.declareRealVariable(continuousPressure)); + exprs.add(QFNRA.declareRealVariable(dispersePressure)); + exprs.add(QFNRA.declareRealVariable(outputPressure)); + exprs.add(QFNRA.assertEqual(nodePressure, continuousPressure)); + exprs.add(QFNRA.assertEqual(nodePressure, dispersePressure)); + exprs.add(QFNRA.assertEqual(nodePressure, outputPressure)); + + // constraint: flow in = flow out + List connectedPorts = new LinkedList(); + connectedPorts.add(pContinuous); + connectedPorts.add(pDispersed); + connectedPorts.add(pOutput); + exprs.add(Macros.generateConservationOfFlow(schematic, connectedPorts)); + + // constraint: viscosity of output = viscosity of continuous + Symbol continuousViscosity = SymbolNameGenerator.getsym_ChannelViscosity( + schematic, chContinuous); + Symbol outputViscosity = SymbolNameGenerator.getsym_ChannelViscosity( + schematic, chOutput); + exprs.add(QFNRA.assertEqual(continuousViscosity, outputViscosity)); + + Symbol vOutput = SymbolNameGenerator .getsym_ChannelDropletVolume(schematic, chOutput); exprs.add(QFNRA.declareRealVariable(vOutput)); - exprs.add(QFNRA.assertEqual(vOutput, - QFNRA.multiply(QFNRA.multiply(h, QFNRA.multiply(w, w)), - QFNRA.add(normalizedVFill, - QFNRA.multiply(alpha, QFNRA.divide(qD, qC)))))); + exprs.add(QFNRA.assertEqual(vOutput, calculatedDropletVolume( + h, w, wIn, epsilon, qD, qC))); return exprs; } From 6d67b68b58a94ad37651c7140d20fdf50ece5a14 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 16 Jun 2015 12:38:30 -0400 Subject: [PATCH 17/20] WIP from symposium --- .../back/microfluidics/smt2/Macros.java | 15 ++- .../smt2/SymbolNameGenerator.java | 61 +++++++++ .../multiphase/TJunctionDeviceStrategy.java | 124 +++++++++++++++++- .../ChannelResistanceStrategy.java | 10 ++ .../SimplePressureFlowStrategy.java | 18 +++ .../TestMicrofluidicsBackend.java | 4 +- 6 files changed, 224 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java index a378f36..101a64e 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/Macros.java @@ -26,10 +26,14 @@ protected static ConnectionValue getConnection( // "total flow in = total flow out" // this is difficult because the actual flow direction may be backwards // with respect to the expected direction - public static SExpression generateConservationOfFlow(Schematic schematic, + public static List generateConservationOfFlow(Schematic schematic, List connectedPorts) { List flowRatesIn = new LinkedList(); List flowRatesOut = new LinkedList(); + + List flowRatesIn_WorstCase = new LinkedList(); + List flowRatesOut_WorstCase = new LinkedList(); + for (PortValue port : connectedPorts) { ConnectionValue channel = getConnection(schematic, port); boolean connectedIntoJunction; @@ -45,15 +49,22 @@ public static SExpression generateConservationOfFlow(Schematic schematic, } Symbol flowRate = SymbolNameGenerator .getsym_ChannelFlowRate(schematic, channel); + Symbol flowRate_WorstCase = SymbolNameGenerator + .getsym_ChannelFlowRate_WorstCase(schematic, channel); if (connectedIntoJunction) { // flow rate is positive into the junction flowRatesIn.add(flowRate); + flowRatesIn_WorstCase.add(flowRate_WorstCase); } else { // flow rate is positive out of the junction flowRatesOut.add(flowRate); + flowRatesOut_WorstCase.add(flowRate_WorstCase); } } - return QFNRA.assertEqual(QFNRA.add(flowRatesIn), QFNRA.add(flowRatesOut)); + List exprs = new LinkedList<>(); + exprs.add(QFNRA.assertEqual(QFNRA.add(flowRatesIn), QFNRA.add(flowRatesOut))); + exprs.add(QFNRA.assertEqual(QFNRA.add(flowRatesIn_WorstCase), QFNRA.add(flowRatesOut_WorstCase))); + return exprs; } } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java index 7061a2b..3a9f968 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/smt2/SymbolNameGenerator.java @@ -87,6 +87,12 @@ public static Symbol getsym_ChannelFlowRate(Schematic schematic, return new Symbol(chName.concat("_flowrate")); } + public static Symbol getsym_ChannelFlowRate_WorstCase(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_flowrate_worst_case")); + } + /** * Retrieves the symbol that defines the viscosity of fluid * present in a channel. @@ -116,7 +122,62 @@ public static Symbol getsym_ChannelDropletVolume(Schematic schematic, String chName = schematic.getConnectionName(ch); return new Symbol(chName.concat("_droplet_volume")); } + + public static Symbol getsym_ChannelDropletVolume_WorstCase(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_droplet_volume_worst_case")); + } + + /** + * Retrieves the symbol that defines the resistance of a droplet + * in a channel. + */ + public static Symbol getsym_ChannelDropletResistance(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_droplet_resistance")); + } + /** + * Retrieves the symbol that defines the speed of a droplet in a channel. + */ + public static Symbol getsym_ChannelDropletVelocity(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_droplet_velocity")); + } + + /** + * Retrieves the symbol that defines the frequency with which droplets + * are produced into a channel. + */ + public static Symbol getsym_ChannelDropletFrequency(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_droplet_frequency")); + } + + /** + * Retrieves the symbol that defines the spacing between droplets + * in a channel. + */ + public static Symbol getsym_ChannelDropletSpacing(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_droplet_spacing")); + } + + /** + * Retrieves the symbol that defines the maximum number of droplets + * that can be in a channel at one time. + */ + public static Symbol getsym_ChannelMaxDroplets(Schematic schematic, + ConnectionValue ch) { + String chName = schematic.getConnectionName(ch); + return new Symbol(chName.concat("_max_droplets")); + } + /** * Retrieves the symbol that defines the height of a (rectangular) channel. */ diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java index 8f5dafb..9a47938 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java @@ -167,11 +167,14 @@ private SExpression calculatedDropletVolume(SExpression h, SExpression w, ) ) ); - + /* SExpression normalizedVFill = QFNRA.conditional( QFNRA.lessThanEqual(wIn, w), vFillSimple, vFillComplex); + */ + + SExpression normalizedVFill = vFillSimple; // alpha depends on these intermediate expressions // this first one appears at least three times as a subexpression of rPinch @@ -188,8 +191,12 @@ private SExpression calculatedDropletVolume(SExpression h, SExpression w, QFNRA.subtract(w, hwParallel) )), new Decimal(0.5)))); // rFill = max(w, wIn) + /* SExpression rFill = QFNRA.conditional( QFNRA.greater(w, wIn), w, wIn); + */ + SExpression rFill = w; + SExpression alpha = QFNRA.multiply( QFNRA.subtract(new Numeral(1), QFNRA.divide(pi, new Numeral(4))), QFNRA.multiply( @@ -211,6 +218,27 @@ private SExpression calculatedDropletVolume(SExpression h, SExpression w, QFNRA.multiply(alpha, QFNRA.divide(qD, qC)))); } + // R1: channel resistance with no droplets + // alpha: experimentally-determined constant + // Ca: capillary number + // sigma: interfacial tension + // muD: viscosity of dispersed medium + // muC: viscosity of continuous medium + // L, w, h: channel length/width/height + private SExpression calculatedDropletResistance( + SExpression R1, SExpression alpha, SExpression Ca, + SExpression sigma, + SExpression muD, SExpression muC, + SExpression L, SExpression w, SExpression h) { + return QFNRA.multiply(R1, QFNRA.add( + QFNRA.divide(alpha, Ca), + QFNRA.multiply(sigma, + QFNRA.multiply(QFNRA.subtract( + QFNRA.divide(muD, muC), new Numeral(1)), + QFNRA.divide(L, QFNRA.multiply(w, + QFNRA.pow(h, new Numeral(3)))))))); + } + private List translateTJunction(Schematic schematic, NodeValue junction, ConnectionValue chContinuous, ConnectionValue chDispersed, @@ -270,7 +298,8 @@ private List translateTJunction(Schematic schematic, .getsym_ChannelHeight(schematic, chOutput))); // constraint: epsilon is zero (sharp-edged t-junction) - exprs.add(QFNRA.assertEqual(epsilon, new Numeral(0))); + //exprs.add(QFNRA.assertEqual(epsilon, new Numeral(0))); + exprs.add(QFNRA.assertGreaterEqual(epsilon, new Numeral(0))); // constraint: all port pressures are equalized Symbol nodePressure = SymbolNameGenerator.getSym_NodePressure( @@ -295,22 +324,109 @@ private List translateTJunction(Schematic schematic, connectedPorts.add(pContinuous); connectedPorts.add(pDispersed); connectedPorts.add(pOutput); - exprs.add(Macros.generateConservationOfFlow(schematic, connectedPorts)); + exprs.addAll(Macros.generateConservationOfFlow(schematic, connectedPorts)); // constraint: viscosity of output = viscosity of continuous + Symbol dispersedViscosity = SymbolNameGenerator.getsym_ChannelViscosity( + schematic, chDispersed); Symbol continuousViscosity = SymbolNameGenerator.getsym_ChannelViscosity( schematic, chContinuous); Symbol outputViscosity = SymbolNameGenerator.getsym_ChannelViscosity( schematic, chOutput); exprs.add(QFNRA.assertEqual(continuousViscosity, outputViscosity)); - + // constraint: calculate droplet volume Symbol vOutput = SymbolNameGenerator .getsym_ChannelDropletVolume(schematic, chOutput); exprs.add(QFNRA.declareRealVariable(vOutput)); exprs.add(QFNRA.assertEqual(vOutput, calculatedDropletVolume( h, w, wIn, epsilon, qD, qC))); + // constraint: assume interfacial tension = 0.042 N*m + // TODO figure out what this is a property of + Symbol interfacialTension = new Symbol("interfacialTension"); + exprs.add(QFNRA.declareRealVariable(interfacialTension)); + exprs.add(QFNRA.assertEqual(interfacialTension, new Decimal(0.042))); + + // constraint: calculate droplet resistance + Symbol dropletResistance = SymbolNameGenerator + .getsym_ChannelDropletResistance(schematic, chOutput); + // assume this is declared elsewhere + exprs.add(QFNRA.assertEqual(dropletResistance, + calculatedDropletResistance( + SymbolNameGenerator.getsym_ChannelResistance(schematic, chOutput), + new Decimal(1.0), // alpha + new Decimal(0.0036), // Ca + interfacialTension, + dispersedViscosity, continuousViscosity, + SymbolNameGenerator.getsym_ChannelLength(schematic, chOutput), + SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput), + SymbolNameGenerator.getsym_ChannelHeight(schematic, chOutput) + ))); + + // constraint: calculate droplet velocity + // v_d ~= Qd / (w * h) + + Symbol dropletVelocity = SymbolNameGenerator + .getsym_ChannelDropletVelocity(schematic, chOutput); + exprs.add(QFNRA.declareRealVariable(dropletVelocity)); + exprs.add(QFNRA.assertEqual(dropletVelocity, + QFNRA.divide(qD, QFNRA.multiply( + SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput), + SymbolNameGenerator.getsym_ChannelHeight(schematic, chOutput))))); + + // constraint: calculate droplet production frequency + // f = Qd / Vd + Symbol dropletFrequency = SymbolNameGenerator + .getsym_ChannelDropletFrequency(schematic, chOutput); + exprs.add(QFNRA.declareRealVariable(dropletFrequency)); + exprs.add(QFNRA.assertEqual(dropletFrequency, QFNRA.divide(qD, vOutput))); + + // constraint: calculate droplet spacing + // spacing = v_d / f + Symbol dropletSpacing = SymbolNameGenerator + .getsym_ChannelDropletSpacing(schematic, chOutput); + exprs.add(QFNRA.declareRealVariable(dropletSpacing)); + exprs.add(QFNRA.assertEqual(dropletSpacing, + QFNRA.divide(dropletVelocity, dropletFrequency))); + + // constraint: calculate maximum number of droplets + // n ~= length / spacing + Symbol nDroplets_Continuous = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, chContinuous); + Symbol nDroplets_Dispersed = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, chDispersed); + Symbol nDroplets_Output = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, chOutput); + // assume this is already declared for every channel + // continuous and dispersed channels can have zero droplets + // TODO droplets inside droplets break this + exprs.add(QFNRA.assertEqual(nDroplets_Continuous, new Numeral(0))); + exprs.add(QFNRA.assertEqual(nDroplets_Dispersed, new Numeral(0))); + // compute upper bound for output channel + exprs.add(QFNRA.assertEqual(nDroplets_Output, + QFNRA.divide(SymbolNameGenerator + .getsym_ChannelLength(schematic, chOutput), dropletSpacing))); + + // constraint: calculate worst-case/steady-state droplet volume + // this is a function of a slightly different flow rate than before + Symbol vOutput_WorstCase = SymbolNameGenerator + .getsym_ChannelDropletVolume_WorstCase(schematic, chOutput); + Symbol qD_WorstCase = SymbolNameGenerator + .getsym_ChannelFlowRate_WorstCase(schematic, chDispersed); + Symbol qC_WorstCase = SymbolNameGenerator + .getsym_ChannelFlowRate_WorstCase(schematic, chContinuous); + exprs.add(QFNRA.declareRealVariable(vOutput_WorstCase)); + exprs.add(QFNRA.assertEqual(vOutput_WorstCase, calculatedDropletVolume( + h, w, wIn, epsilon, qD_WorstCase, qC_WorstCase))); + + // constraint: target volume and worst-case volume differ by at most 5% + SExpression tolerance = new Decimal(0.05); + exprs.add(QFNRA.assertGreaterEqual(QFNRA.divide(vOutput_WorstCase, vOutput), + QFNRA.subtract(new Numeral(1), tolerance))); + exprs.add(QFNRA.assertLessThanEqual(QFNRA.divide(vOutput_WorstCase, vOutput), + QFNRA.add(new Numeral(1), tolerance))); + return exprs; } diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java index 901168f..79bfc0e 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/ChannelResistanceStrategy.java @@ -25,6 +25,16 @@ protected List translationStep(Schematic schematic, // TODO it would be really cool to make the channel type // part of the SMT2 equations so we could solve for that too // TODO we are just assuming all channels are rectangular right now + + // TODO this might not stay here + Symbol nDroplets = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, conn); + exprs.add(QFNRA.declareRealVariable(nDroplets)); + + Symbol dropletResistance = SymbolNameGenerator + .getsym_ChannelDropletResistance(schematic, conn); + exprs.add(QFNRA.declareRealVariable(dropletResistance)); + exprs.addAll(translateRectangularChannel(schematic, conn)); } return exprs; diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java index bd2cbe8..2c5de59 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/SimplePressureFlowStrategy.java @@ -42,6 +42,24 @@ private List translate(ConnectionValue conn, exprs.add(QFNRA.assertEqual(QFNRA.subtract(p1, p2), QFNRA.multiply(chV, chR))); + // now declare a "worst case" flow rate, i.e. with maximum # of droplets + Symbol chV_WorstCase = SymbolNameGenerator.getsym_ChannelFlowRate_WorstCase(schematic, conn); + exprs.add(QFNRA.declareRealVariable(chV_WorstCase)); + // the resistance in the worst case is (approximately) + // equal to the base channel resistance plus + // the number of droplets times the resistance of each droplet + Symbol nDroplets = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, conn); + Symbol dropletResistance = SymbolNameGenerator + .getsym_ChannelDropletResistance(schematic, conn); + SExpression chR_WorstCase = QFNRA.add(chR, + QFNRA.multiply(nDroplets, dropletResistance)); + // assume pressures are the same as before, + // but flow rates can change in the worst case + // TODO is this right? + exprs.add(QFNRA.assertEqual(QFNRA.subtract(p1, p2), + QFNRA.multiply(chV_WorstCase, chR_WorstCase))); + return exprs; } diff --git a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java index d3cd942..cef402c 100644 --- a/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java +++ b/src/test/java/org/manifold/compiler/back/microfluidics/TestMicrofluidicsBackend.java @@ -75,10 +75,10 @@ public void testTJunctionSynthesis() throws Exception { // Make a schematic with two inputs, one output, and a T-junction NodeValue entry = UtilSchematicConstruction.instantiateFluidEntry( - schematic, viscosityOfWater); + schematic, 0.01); schematic.addNode("in0", entry); NodeValue disperse = UtilSchematicConstruction.instantiateFluidEntry( - schematic, viscosityOfWater); + schematic, 0.001); schematic.addNode("in1", disperse); NodeValue exit = UtilSchematicConstruction.instantiateFluidExit(schematic); schematic.addNode("out0", exit); From 1371ffd35d9e4f47066d5b561a2cf17e584baf32 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 7 Jul 2015 15:31:07 -0400 Subject: [PATCH 18/20] selectively disable droplet derived quantity analysis and worst-case droplet stuff --- .../multiphase/TJunctionDeviceStrategy.java | 169 ++++++++++-------- 1 file changed, 90 insertions(+), 79 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java index 9a47938..098f2d3 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/multiphase/TJunctionDeviceStrategy.java @@ -23,6 +23,9 @@ public class TJunctionDeviceStrategy extends TranslationStrategy { + private static boolean calculateDropletDerivedQuantities = false; + private static boolean performWorstCaseAnalysis = false; + //get the connection associated with this port // TODO this is VERY EXPENSIVE, find an optimization protected ConnectionValue getConnection( @@ -342,90 +345,98 @@ private List translateTJunction(Schematic schematic, exprs.add(QFNRA.assertEqual(vOutput, calculatedDropletVolume( h, w, wIn, epsilon, qD, qC))); - // constraint: assume interfacial tension = 0.042 N*m - // TODO figure out what this is a property of - Symbol interfacialTension = new Symbol("interfacialTension"); - exprs.add(QFNRA.declareRealVariable(interfacialTension)); - exprs.add(QFNRA.assertEqual(interfacialTension, new Decimal(0.042))); - - // constraint: calculate droplet resistance - Symbol dropletResistance = SymbolNameGenerator - .getsym_ChannelDropletResistance(schematic, chOutput); - // assume this is declared elsewhere - exprs.add(QFNRA.assertEqual(dropletResistance, - calculatedDropletResistance( - SymbolNameGenerator.getsym_ChannelResistance(schematic, chOutput), - new Decimal(1.0), // alpha - new Decimal(0.0036), // Ca - interfacialTension, - dispersedViscosity, continuousViscosity, - SymbolNameGenerator.getsym_ChannelLength(schematic, chOutput), - SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput), - SymbolNameGenerator.getsym_ChannelHeight(schematic, chOutput) - ))); + if (calculateDropletDerivedQuantities) { - // constraint: calculate droplet velocity - // v_d ~= Qd / (w * h) + // constraint: assume interfacial tension = 0.042 N*m + // TODO figure out what this is a property of + Symbol interfacialTension = new Symbol("interfacialTension"); + exprs.add(QFNRA.declareRealVariable(interfacialTension)); + exprs.add(QFNRA.assertEqual(interfacialTension, new Decimal(0.042))); + + // constraint: calculate droplet resistance + Symbol dropletResistance = SymbolNameGenerator + .getsym_ChannelDropletResistance(schematic, chOutput); + // assume this is declared elsewhere + exprs.add(QFNRA.assertEqual(dropletResistance, + calculatedDropletResistance( + SymbolNameGenerator.getsym_ChannelResistance(schematic, chOutput), + new Decimal(1.0), // alpha + new Decimal(0.0036), // Ca + interfacialTension, + dispersedViscosity, continuousViscosity, + SymbolNameGenerator.getsym_ChannelLength(schematic, chOutput), + SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput), + SymbolNameGenerator.getsym_ChannelHeight(schematic, chOutput) + ))); + + // constraint: calculate droplet velocity + // v_d ~= Qd / (w * h) + + Symbol dropletVelocity = SymbolNameGenerator + .getsym_ChannelDropletVelocity(schematic, chOutput); + exprs.add(QFNRA.declareRealVariable(dropletVelocity)); + exprs.add(QFNRA.assertEqual(dropletVelocity, + QFNRA.divide(qD, QFNRA.multiply( + SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput), + SymbolNameGenerator.getsym_ChannelHeight(schematic, chOutput))))); + + // constraint: calculate droplet production frequency + // f = Qd / Vd + Symbol dropletFrequency = SymbolNameGenerator + .getsym_ChannelDropletFrequency(schematic, chOutput); + exprs.add(QFNRA.declareRealVariable(dropletFrequency)); + exprs.add(QFNRA.assertEqual(dropletFrequency, QFNRA.divide(qD, vOutput))); + + // constraint: calculate droplet spacing + // spacing = v_d / f + Symbol dropletSpacing = SymbolNameGenerator + .getsym_ChannelDropletSpacing(schematic, chOutput); + exprs.add(QFNRA.declareRealVariable(dropletSpacing)); + exprs.add(QFNRA.assertEqual(dropletSpacing, + QFNRA.divide(dropletVelocity, dropletFrequency))); + + // constraint: calculate maximum number of droplets + // n ~= length / spacing + Symbol nDroplets_Continuous = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, chContinuous); + Symbol nDroplets_Dispersed = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, chDispersed); + Symbol nDroplets_Output = SymbolNameGenerator + .getsym_ChannelMaxDroplets(schematic, chOutput); + // assume this is already declared for every channel + // continuous and dispersed channels can have zero droplets + // TODO droplets inside droplets break this + exprs.add(QFNRA.assertEqual(nDroplets_Continuous, new Numeral(0))); + exprs.add(QFNRA.assertEqual(nDroplets_Dispersed, new Numeral(0))); + // compute upper bound for output channel + exprs.add(QFNRA.assertEqual(nDroplets_Output, + QFNRA.divide(SymbolNameGenerator + .getsym_ChannelLength(schematic, chOutput), dropletSpacing))); - Symbol dropletVelocity = SymbolNameGenerator - .getsym_ChannelDropletVelocity(schematic, chOutput); - exprs.add(QFNRA.declareRealVariable(dropletVelocity)); - exprs.add(QFNRA.assertEqual(dropletVelocity, - QFNRA.divide(qD, QFNRA.multiply( - SymbolNameGenerator.getsym_ChannelWidth(schematic, chOutput), - SymbolNameGenerator.getsym_ChannelHeight(schematic, chOutput))))); + } // calculateDropletDerivedQuantities - // constraint: calculate droplet production frequency - // f = Qd / Vd - Symbol dropletFrequency = SymbolNameGenerator - .getsym_ChannelDropletFrequency(schematic, chOutput); - exprs.add(QFNRA.declareRealVariable(dropletFrequency)); - exprs.add(QFNRA.assertEqual(dropletFrequency, QFNRA.divide(qD, vOutput))); + if (performWorstCaseAnalysis) { - // constraint: calculate droplet spacing - // spacing = v_d / f - Symbol dropletSpacing = SymbolNameGenerator - .getsym_ChannelDropletSpacing(schematic, chOutput); - exprs.add(QFNRA.declareRealVariable(dropletSpacing)); - exprs.add(QFNRA.assertEqual(dropletSpacing, - QFNRA.divide(dropletVelocity, dropletFrequency))); - - // constraint: calculate maximum number of droplets - // n ~= length / spacing - Symbol nDroplets_Continuous = SymbolNameGenerator - .getsym_ChannelMaxDroplets(schematic, chContinuous); - Symbol nDroplets_Dispersed = SymbolNameGenerator - .getsym_ChannelMaxDroplets(schematic, chDispersed); - Symbol nDroplets_Output = SymbolNameGenerator - .getsym_ChannelMaxDroplets(schematic, chOutput); - // assume this is already declared for every channel - // continuous and dispersed channels can have zero droplets - // TODO droplets inside droplets break this - exprs.add(QFNRA.assertEqual(nDroplets_Continuous, new Numeral(0))); - exprs.add(QFNRA.assertEqual(nDroplets_Dispersed, new Numeral(0))); - // compute upper bound for output channel - exprs.add(QFNRA.assertEqual(nDroplets_Output, - QFNRA.divide(SymbolNameGenerator - .getsym_ChannelLength(schematic, chOutput), dropletSpacing))); - - // constraint: calculate worst-case/steady-state droplet volume - // this is a function of a slightly different flow rate than before - Symbol vOutput_WorstCase = SymbolNameGenerator - .getsym_ChannelDropletVolume_WorstCase(schematic, chOutput); - Symbol qD_WorstCase = SymbolNameGenerator - .getsym_ChannelFlowRate_WorstCase(schematic, chDispersed); - Symbol qC_WorstCase = SymbolNameGenerator - .getsym_ChannelFlowRate_WorstCase(schematic, chContinuous); - exprs.add(QFNRA.declareRealVariable(vOutput_WorstCase)); - exprs.add(QFNRA.assertEqual(vOutput_WorstCase, calculatedDropletVolume( - h, w, wIn, epsilon, qD_WorstCase, qC_WorstCase))); + // constraint: calculate worst-case/steady-state droplet volume + // this is a function of a slightly different flow rate than before + Symbol vOutput_WorstCase = SymbolNameGenerator + .getsym_ChannelDropletVolume_WorstCase(schematic, chOutput); + Symbol qD_WorstCase = SymbolNameGenerator + .getsym_ChannelFlowRate_WorstCase(schematic, chDispersed); + Symbol qC_WorstCase = SymbolNameGenerator + .getsym_ChannelFlowRate_WorstCase(schematic, chContinuous); + exprs.add(QFNRA.declareRealVariable(vOutput_WorstCase)); + exprs.add(QFNRA.assertEqual(vOutput_WorstCase, calculatedDropletVolume( + h, w, wIn, epsilon, qD_WorstCase, qC_WorstCase))); + + // constraint: target volume and worst-case volume differ by at most 5% + SExpression tolerance = new Decimal(0.05); + exprs.add(QFNRA.assertGreaterEqual(QFNRA.divide(vOutput_WorstCase, vOutput), + QFNRA.subtract(new Numeral(1), tolerance))); + exprs.add(QFNRA.assertLessThanEqual(QFNRA.divide(vOutput_WorstCase, vOutput), + QFNRA.add(new Numeral(1), tolerance))); - // constraint: target volume and worst-case volume differ by at most 5% - SExpression tolerance = new Decimal(0.05); - exprs.add(QFNRA.assertGreaterEqual(QFNRA.divide(vOutput_WorstCase, vOutput), - QFNRA.subtract(new Numeral(1), tolerance))); - exprs.add(QFNRA.assertLessThanEqual(QFNRA.divide(vOutput_WorstCase, vOutput), - QFNRA.add(new Numeral(1), tolerance))); + } // performWorstCaseAnalysis return exprs; } From aa373f41c695660325466eb401d242d897907196 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 7 Jul 2015 15:37:27 -0400 Subject: [PATCH 19/20] constraint fluid input/output pressures to be >= 0 --- .../pressureflow/FluidEntryExitDeviceStrategy.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java index 8e48e6e..181b53b 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java @@ -14,6 +14,7 @@ import org.manifold.compiler.back.microfluidics.ProcessParameters; import org.manifold.compiler.back.microfluidics.TranslationStrategy; import org.manifold.compiler.back.microfluidics.smt2.Decimal; +import org.manifold.compiler.back.microfluidics.smt2.Numeral; import org.manifold.compiler.back.microfluidics.smt2.QFNRA; import org.manifold.compiler.back.microfluidics.smt2.SExpression; import org.manifold.compiler.back.microfluidics.smt2.Symbol; @@ -72,6 +73,10 @@ private List translateFluidEntryNode( exprs.add(QFNRA.declareRealVariable( SymbolNameGenerator.getSym_PortPressure(schematic, node.getPort("output")))); + // constraint: port pressure >= 0 + exprs.add(QFNRA.assertEqual(new Numeral(0), + SymbolNameGenerator.getSym_PortPressure(schematic, node.getPort("output")))); + // the viscosity in the channel connected to output // is the viscosity given at the entry ConnectionValue ch = getConnection(schematic, node.getPort("output")); @@ -92,6 +97,11 @@ private List translateFluidExitNode( exprs.add(QFNRA.declareRealVariable( SymbolNameGenerator.getSym_PortPressure(schematic, node.getPort("input")))); + + // constraint: port pressure >= 0 + exprs.add(QFNRA.assertEqual(new Numeral(0), + SymbolNameGenerator.getSym_PortPressure(schematic, node.getPort("input")))); + return exprs; } From 20245b4ea70819238006ae667b56c2c531a58158 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 7 Jul 2015 19:10:25 -0400 Subject: [PATCH 20/20] fix: assertEqual -> assertLessThanEqual --- .../strategies/pressureflow/FluidEntryExitDeviceStrategy.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java index 181b53b..4ecc1fe 100644 --- a/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java +++ b/src/main/java/org/manifold/compiler/back/microfluidics/strategies/pressureflow/FluidEntryExitDeviceStrategy.java @@ -74,7 +74,7 @@ private List translateFluidEntryNode( SymbolNameGenerator.getSym_PortPressure(schematic, node.getPort("output")))); // constraint: port pressure >= 0 - exprs.add(QFNRA.assertEqual(new Numeral(0), + exprs.add(QFNRA.assertLessThanEqual(new Numeral(0), SymbolNameGenerator.getSym_PortPressure(schematic, node.getPort("output")))); // the viscosity in the channel connected to output @@ -99,7 +99,7 @@ private List translateFluidExitNode( node.getPort("input")))); // constraint: port pressure >= 0 - exprs.add(QFNRA.assertEqual(new Numeral(0), + exprs.add(QFNRA.assertLessThanEqual(new Numeral(0), SymbolNameGenerator.getSym_PortPressure(schematic, node.getPort("input")))); return exprs;