Admittance matrix (Knotenpotentialverfahren)

This commit is contained in:
Patrick Haßel 2024-05-22 11:46:52 +02:00
parent e91d1eadde
commit dfea68adaf
29 changed files with 575 additions and 326 deletions

View File

@ -35,6 +35,11 @@
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
</dependencies>
</project>

View File

@ -6,9 +6,7 @@ import static java.lang.Math.round;
public class CONFIG {
public static final double BULB_VOLTAGE_MIN = 0.5;
public static final double VOLTAGE = 3.0;
public static final double VOLTAGE_HIGH_MIN = 0.1;
public static final int RASTER = 200;

View File

@ -0,0 +1,216 @@
package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.InnerConnection;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.parts.PartBattery;
import de.ph87.electro.circuit.part.parts.PartLight;
import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.linear.*;
import java.util.Collections;
import java.util.List;
import static java.lang.Math.max;
@Slf4j
@Getter
public class Calculation {
private static final double NO_RESISTANCE = 1e-12;
private static final double FULL_ADMITTANCE = 1 / NO_RESISTANCE;
private final List<Junction> junctions;
private final RealMatrix matrix;
private final RealVector currents;
private RealVector potentials = null;
public Calculation(final int numNodes) {
junctions = Collections.emptyList();
matrix = new Array2DRowRealMatrix(numNodes, numNodes);
currents = new ArrayRealVector(numNodes);
}
public Calculation(final Circuit circuit) {
final PartBattery pivot = circuit.streamParts().flatMap(PartBattery::filterCast).findFirst().orElseThrow(RuntimeException::new);
junctions = circuit.streamJunctions().filter(junction -> junction != pivot.getMinus()).toList(); // pivot.minus is GND and cannot be part of the matrix (linear dependency)
matrix = new Array2DRowRealMatrix(junctions.size(), junctions.size());
currents = new ArrayRealVector(junctions.size());
fromSchematic(circuit);
solve();
toSchematic(circuit);
}
private void fromSchematic(final Circuit circuit) {
circuit.streamWires().forEach(
wire -> addResistor(wire.getA(), wire.getB(), NO_RESISTANCE)
);
circuit.streamParts().forEach(part -> {
for (final InnerConnection innerConnection : part.getInnerConnections()) {
addResistor(innerConnection.a, innerConnection.b, NO_RESISTANCE);
}
if (part instanceof final PartBattery battery) {
addBattery(battery);
} else if (part instanceof final PartLight light) {
addResistor(light.getA(), light.getB(), light.getResistance());
}
});
}
private void toSchematic(final Circuit circuit) {
circuit.streamJunctions().forEach(junction -> junction.setVoltage(getPotential(junction)));
circuit.streamWires().forEach(wire -> wire.setCurrent(getCurrent(wire)));
circuit.streamParts().forEach(Part::postCalculate);
}
private double getPotential(final @NonNull Junction junction) {
final int index = getJunctionIndex(junction);
if (index < 0) {
return 0; // per definition
}
if (potentials == null) {
return Double.NaN;
}
return potentials.getEntry(index);
}
private double getCurrent(final Wire wire) {
final int indexA = getJunctionIndex(wire.getA());
final int indexB = getJunctionIndex(wire.getB());
final double conductance = indexA >= 0 && indexB >= 0 ? matrix.getEntry(indexA, indexB) : FULL_ADMITTANCE;
final double potentialDifference = getPotential(wire.getB()) - getPotential(wire.getA());
return conductance * potentialDifference;
}
private int getJunctionIndex(@NonNull final Junction junction) {
for (int i = 0; i < junctions.size(); i++) {
if (junctions.get(i) == junction) {
return i;
}
}
return -1;
}
public void addResistor(final Junction a, final Junction b, final double resistance) {
final int indexA = getJunctionIndex(a);
final int indexB = getJunctionIndex(b);
addResistor(indexA, indexB, resistance);
}
public void addResistor(final int index0, final int index1, double resistance) {
double conductance = 1.0 / resistance;
if (index0 >= 0) {
matrix.setEntry(index0, index0, matrix.getEntry(index0, index0) + conductance);
}
if (index1 >= 0) {
matrix.setEntry(index1, index1, matrix.getEntry(index1, index1) + conductance);
}
if (index0 >= 0 && index1 >= 0) {
matrix.setEntry(index0, index1, matrix.getEntry(index0, index1) - conductance);
matrix.setEntry(index1, index0, matrix.getEntry(index1, index0) - conductance);
}
potentials = null;
}
public void addBattery(final PartBattery battery) {
final double current = battery.getVoltage() / battery.getResistance();
final int indexMinus = getJunctionIndex(battery.getMinus());
final int indexPlus = getJunctionIndex(battery.getPlus());
addCurrentSource(indexMinus, indexPlus, current, battery.getResistance());
}
public void addCurrentSource(final int index0, final int index1, final double current, final double resistance) {
if (index0 >= 0) {
currents.setEntry(index0, currents.getEntry(index0) - current);
}
if (index1 >= 0) {
currents.setEntry(index1, currents.getEntry(index1) + current);
}
addResistor(index0, index1, resistance);
potentials = null;
}
public void solve() {
final DecompositionSolver solver = new LUDecomposition(matrix).getSolver();
try {
potentials = solver.solve(currents);
} catch (SingularMatrixException e) {
potentials = null;
log.error("Schaltung fehlerhaft: {}", e.getMessage());
}
}
@Override
public String toString() {
final Format matrixFormat = new Format(matrix);
final Format currentsFormat = new Format(currents);
final Format potentialsFormat = new Format(potentials);
final StringBuilder builder = new StringBuilder();
for (int row = 0; row < matrix.getRowDimension(); row++) {
builder.append("[");
String prefix = "";
for (int c = 0; c < matrix.getColumnDimension(); c++) {
builder.append(prefix);
builder.append(matrixFormat.format(matrix.getEntry(row, c)));
prefix = ", ";
}
final String potentialString = potentialsFormat.format(potentials.getEntry(row));
final String currentString = currentsFormat.format(currents.getEntry(row));
builder.append("] x [%s V] = [%s A]\n".formatted(potentialString, currentString));
}
return builder.toString();
}
@Getter
private static final class Format {
private int integer;
private int decimal;
private final String format;
public Format(final RealMatrix matrix) {
for (int r = 0; r < matrix.getRowDimension(); r++) {
for (int c = 0; c < matrix.getColumnDimension(); c++) {
append(matrix.getEntry(r, c));
}
}
format = calculateFormat();
}
public Format(final RealVector vector) {
for (int r = 0; r < vector.getDimension(); r++) {
append(vector.getEntry(r));
}
format = calculateFormat();
}
private void append(final double value) {
final String string = "%+f".formatted(value);
final String[] parts = string.split("[.,]");
integer = max(integer, parts[0].length());
if (parts.length > 1) {
decimal = max(decimal, parts[1].length());
}
}
private String calculateFormat() {
return "%%+%d.%df".formatted(integer + decimal + (decimal > 0 ? 1 : 0), decimal);
}
public String format(final double value) {
return format.formatted(value);
}
}
}

View File

@ -4,7 +4,6 @@ import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.parts.PartBattery;
import lombok.NoArgsConstructor;
import lombok.NonNull;
@ -18,6 +17,8 @@ public class Circuit {
private final List<Part> parts = new ArrayList<>();
private final List<Junction> junctions = new ArrayList<>();
private final List<Wire> wires = new ArrayList<>();
public Circuit(final CircuitDto dto) {
@ -41,25 +42,17 @@ public class Circuit {
}
public void evaluateAndRender() {
parts.forEach(Part::reset);
streamBatteries().forEach(PartBattery::startPropagation);
new Calculation(this);
parts.forEach(Part::render);
}
private Stream<PartBattery> streamBatteries() {
return parts.stream().filter(part -> part instanceof PartBattery).map(part -> (PartBattery) part);
}
public Stream<Wire> streamWires() {
return wires.stream();
}
public <T extends Part> T addPart(final T part) {
if (parts.contains(part)) {
throw new RuntimeException();
}
if (isFree(part.getPosition())) {
parts.add(part);
junctions.addAll(part.getJunctions());
part.render();
}
return part;
@ -82,6 +75,14 @@ public class Circuit {
return parts.stream();
}
public Stream<Junction> streamJunctions() {
return junctions.stream();
}
public Stream<Wire> streamWires() {
return wires.stream();
}
public int getPartCount() {
return parts.size();
}

View File

@ -1,6 +1,8 @@
package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.parts.PartBattery;
import de.ph87.electro.circuit.part.parts.PartLight;
import javax.swing.*;
import java.awt.*;
@ -15,6 +17,19 @@ public class CircuitPanel extends JPanel {
public CircuitPanel() {
new CircuitPanelDropTarget(this, circuit);
final PartBattery battery = circuit.addPart(new PartBattery(Position.ofRaster(1, 0)));
final PartLight light0 = circuit.addPart(new PartLight(Position.ofRaster(0, 2)));
light0.clockwise();
final PartLight light1 = circuit.addPart(new PartLight(Position.ofRaster(1, 2)));
light1.clockwise();
light1.clockwise();
light1.clockwise();
circuit.connect(battery.getMinus(), light0.getA());
circuit.connect(light0.getB(), light1.getA());
circuit.connect(light1.getB(), battery.getPlus());
}
@Override
@ -51,13 +66,7 @@ public class CircuitPanel extends JPanel {
}
private void drawWires(final Graphics2D g) {
circuit.streamWires().forEach(wire -> {
final Position a = wire.getA().getPosition();
final Position b = wire.getB().getPosition();
g.setColor(wire.getA().getColor());
g.setStroke(WIRE_STROKE);
g.drawLine(a.absolute.x, a.absolute.y, b.absolute.x, b.absolute.y);
});
circuit.streamWires().forEach(wire -> wire.draw(g));
}
}

View File

@ -2,18 +2,33 @@ package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Position;
import lombok.Data;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
@Data
import java.awt.*;
import java.awt.geom.Rectangle2D;
import static de.ph87.electro.CONFIG.LABEL_FONT;
import static de.ph87.electro.CONFIG.WIRE_STROKE;
import static java.lang.Math.abs;
import static java.lang.Math.round;
@ToString
public class Wire {
@Getter
@NonNull
private final Junction a;
@Getter
@NonNull
private final Junction b;
@Setter
private double current = Double.NaN;
public Wire(@NonNull final Junction a, @NonNull final Junction b) {
this.a = a;
this.b = b;
@ -34,4 +49,34 @@ public class Wire {
throw new RuntimeException();
}
public double getCurrent(final Junction junction) {
return junction == a ? current : -current;
}
public void draw(final Graphics2D g) {
g.setColor(a.getColor());
g.setStroke(WIRE_STROKE);
g.drawLine(a.getPosition().absolute.x, a.getPosition().absolute.y, b.getPosition().absolute.x, b.getPosition().absolute.y);
drawValues(g, "%.2f A".formatted(abs(current)), -0.5);
drawValues(g, "%.2f V".formatted(a.getVoltage()), +0.5);
}
private void drawValues(final Graphics2D g, final String string, final double offset) {
g.setFont(LABEL_FONT);
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g);
final int mx = (a.getPosition().absolute.x + b.getPosition().absolute.x) / 2;
final int my = (a.getPosition().absolute.y + b.getPosition().absolute.y) / 2;
final int bx = (int) round((mx - bounds.getWidth() / 2)) - 2;
final int by = (int) round((my - bounds.getHeight() / 2) + offset * bounds.getHeight()) - 2;
g.setColor(new Color(255, 255, 255, 200));
g.fillRect(bx, by, (int) round(bounds.getWidth()) + 4, (int) round(bounds.getHeight()) + 4);
final int sx = (int) round((mx - bounds.getWidth() / 2));
final int sy = (int) round(my + bounds.getHeight() / 3 + offset * bounds.getHeight());
g.setColor(a.getColor());
g.drawString(string, sx, sy);
}
}

View File

@ -0,0 +1,14 @@
package de.ph87.electro.circuit.part;
public class InnerConnection {
public final Junction a;
public final Junction b;
public InnerConnection(final Junction a, final Junction b) {
this.a = a;
this.b = b;
}
}

View File

@ -1,9 +1,7 @@
package de.ph87.electro.circuit.part;
import de.ph87.electro.circuit.ShortCircuit;
import de.ph87.electro.circuit.Wire;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.awt.*;
@ -32,7 +30,6 @@ public class Junction {
private final Set<Wire> wires = new HashSet<>();
@Setter
@ToString.Include
private double voltage = Double.NaN;
@ -60,31 +57,9 @@ public class Junction {
updatePosition();
}
public void reset() {
voltage = Double.NaN;
color = VOLTAGE_UNKNOWN_COLOR;
}
public void propagate(final double newVoltage) throws ShortCircuit {
if (voltage == newVoltage) {
return;
}
if (Double.isNaN(voltage)) {
voltage = newVoltage;
if (Double.isNaN(voltage)) {
color = Color.GRAY;
} else if (voltage > 0) {
color = VOLTAGE_HIGH_COLOR;
} else {
color = VOLTAGE_LOW_COLOR;
}
owner.propagate(this);
for (Wire wire : wires) {
wire.getOpposite(this).propagate(newVoltage);
}
return;
}
throw new ShortCircuit();
public void setVoltage(final double voltage) {
this.voltage = voltage;
this.color = Double.isNaN(voltage) ? VOLTAGE_UNKNOWN_COLOR : ((voltage >= VOLTAGE_HIGH_MIN) ? VOLTAGE_HIGH_COLOR : VOLTAGE_LOW_COLOR);
}
public void render(final Render g) {

View File

@ -1,16 +1,13 @@
package de.ph87.electro.circuit.part;
import de.ph87.electro.circuit.ShortCircuit;
import de.ph87.electro.circuit.part.parts.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import static de.ph87.electro.CONFIG.PART_BACKGROUND;
import static de.ph87.electro.CONFIG.RASTER;
@ -29,17 +26,16 @@ public abstract class Part {
private Position position;
private Orientation orientation;
private Orientation orientation = Orientation.R0;
private final List<Junction> junctions = new ArrayList<>();
protected final Render render = new Render();
protected Part(final String name, final Position position, final Orientation orientation) {
protected Part(final String name, final Position position) {
this.uuid = UUID.randomUUID().toString();
this.name = name;
setPosition(position);
this.orientation = orientation;
}
protected Part(final PartDto dto) {
@ -73,10 +69,6 @@ public abstract class Part {
render();
}
public void reset() {
junctions.forEach(Junction::reset);
}
public void render() {
render.rect(ZERO, RASTER, RASTER, null, null, PART_BACKGROUND);
@ -106,7 +98,7 @@ public abstract class Part {
// may be overwritten
}
public void propagate(final Junction source) throws ShortCircuit {
public void postCalculate() {
// may be overwritten
}
@ -132,4 +124,8 @@ public abstract class Part {
};
}
public List<InnerConnection> getInnerConnections() {
return Collections.emptyList();
}
}

View File

@ -2,8 +2,8 @@ package de.ph87.electro.circuit.part;
public abstract class PartOther extends Part {
protected PartOther(final String name, final Position position, final Orientation orientation) {
super(name, position, orientation);
protected PartOther(final String name, final Position position) {
super(name, position);
}
protected PartOther(final PartDto dto) {

View File

@ -28,6 +28,10 @@ public final class Position {
this.inside = new Point(x % RASTER, y % RASTER);
}
public static Position ofRaster(final int x, final int y) {
return new Position(x * RASTER, y * RASTER);
}
@Override
public int hashCode() {
return absolute.hashCode();

View File

@ -61,11 +61,13 @@ public class Render {
}
}
public void textCenter(final Font font, final String string, final int y, final Color color) {
public void textCenter(final Font font, final String string, final int innerX, final int innerY, final Color color) {
g.setFont(font);
g.setColor(color);
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g);
g.drawString(string, (int) round(RASTER - bounds.getWidth()) / 2, y + g.getFont().getSize());
final int x = innerX - (int) round(bounds.getWidth()) / 2;
final int y = innerY + (int) round(bounds.getHeight()) / 2;
g.drawString(string, x, y);
}
public void clockwise(final Orientation orientation) {

View File

@ -1,6 +1,5 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.ShortCircuit;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.Part;
@ -10,11 +9,12 @@ import lombok.Setter;
import lombok.ToString;
import java.awt.*;
import java.util.stream.Stream;
import static de.ph87.electro.CONFIG.*;
@Getter
@ToString(callSuper = true)
@ToString(callSuper = true, onlyExplicitlyIncluded = true)
public class PartBattery extends Part {
private static final double MINUS_W = 0.1 * RASTER;
@ -32,24 +32,21 @@ public class PartBattery extends Part {
private final Junction plus;
@Setter
private double voltage;
@ToString.Include
private double voltage = 3;
private ShortCircuit shortCircuit = null;
@Setter
@ToString.Include
private double resistance = 0.05;
public PartBattery() {
this(Position.ZERO);
}
public PartBattery(final Position position) {
this("Batterie", position, Orientation.R0, VOLTAGE);
}
public PartBattery(final String name, final Position position, final Orientation orientation, final double initialVoltage) {
super(name, position, orientation);
minus = newJunction(this, "-", P10, P50);
plus = newJunction(this, "+", P90, P50);
voltage = initialVoltage;
startPropagation();
super("Batterie", position);
minus = newJunction(this, "PLUS", P10, P50);
plus = newJunction(this, "MINUS", P90, P50);
}
public PartBattery(final PartBatteryDto dto) {
@ -57,17 +54,6 @@ public class PartBattery extends Part {
minus = newJunction(this, dto.getMinus(), P10, P50);
plus = newJunction(this, dto.getPlus(), P90, P50);
voltage = dto.getVoltage();
startPropagation();
}
public void startPropagation() {
try {
shortCircuit = null;
minus.propagate(0);
plus.propagate(voltage);
} catch (ShortCircuit e) {
shortCircuit = e;
}
}
@Override
@ -81,20 +67,22 @@ public class PartBattery extends Part {
@Override
protected void _labels() {
final int x = RASTER / 2;
final int y0;
final int y1;
if (getOrientation() == Orientation.R180) {
y0 = P95 - LABEL_FONT.getSize();
y1 = P05;
} else {
render.clockwise(getOrientation());
y0 = P05;
y1 = P95 - LABEL_FONT.getSize();
}
render.textCenter(LABEL_FONT, "%.1fV".formatted(voltage), y0, Color.BLACK);
if (shortCircuit != null) {
render.textCenter(LABEL_FONT, "KURZSCHLUSS", y1, Color.RED);
render.textCenter(LABEL_FONT, "%.1fV".formatted(voltage), x, y0, Color.BLACK);
}
public static Stream<PartBattery> filterCast(final Part part) {
if (part instanceof final PartBattery battery) {
return Stream.of(battery);
}
return Stream.empty();
}
}

View File

@ -1,13 +1,13 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.PartOther;
import de.ph87.electro.circuit.part.Position;
import lombok.Getter;
import lombok.ToString;
import static de.ph87.electro.CONFIG.*;
import static de.ph87.electro.CONFIG.P10;
import static de.ph87.electro.CONFIG.P90;
@Getter
@ToString(callSuper = true)
@ -22,13 +22,9 @@ public class PartJunctionCorner extends PartOther {
}
public PartJunctionCorner(final Position position) {
this("", position, Orientation.R0);
}
public PartJunctionCorner(final String name, final Position position, final Orientation orientation) {
super(name, position, orientation);
j0 = newJunction(this, "", P10, P10);
j1 = newJunction(this, "", P90, P90);
super("", position);
j0 = newJunction(this, "J0", P10, P10);
j1 = newJunction(this, "J1", P90, P90);
}
public PartJunctionCorner(final PartJunctionCornerDto dto) {

View File

@ -1,14 +1,13 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.PartOther;
import de.ph87.electro.circuit.part.Position;
import lombok.Getter;
import lombok.ToString;
import static de.ph87.electro.CONFIG.P50;
import static de.ph87.electro.CONFIG.P10;
import static de.ph87.electro.CONFIG.P50;
@Getter
@ToString(callSuper = true)
@ -23,13 +22,9 @@ public class PartJunctionEdge extends PartOther {
}
public PartJunctionEdge(final Position position) {
this("", position, Orientation.R0);
}
public PartJunctionEdge(final String name, final Position position, final Orientation orientation) {
super(name, position, orientation);
j0 = newJunction(this, "", P10, P50);
j1 = newJunction(this, "", P50, P10);
super("", position);
j0 = newJunction(this, "J0", P10, P50);
j1 = newJunction(this, "J1", P50, P10);
}
public PartJunctionEdge(final PartJunctionEdgeDto dto) {

View File

@ -1,7 +1,6 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.PartOther;
import de.ph87.electro.circuit.part.Position;
import lombok.Getter;
@ -20,12 +19,8 @@ public class PartJunctionMiddle extends PartOther {
}
public PartJunctionMiddle(final Position position) {
this("", position, Orientation.R0);
}
public PartJunctionMiddle(final String name, final Position position, final Orientation orientation) {
super(name, position, orientation);
junction = newJunction(this, "", P50, P50);
super("", position);
junction = newJunction(this, "J", P50, P50);
}
public PartJunctionMiddle(final PartJunctionMiddleDto dto) {

View File

@ -33,17 +33,19 @@ public class PartLight extends PartOther {
private static final Color BULB_OFF_COLOR = Color.DARK_GRAY;
private final Junction pin0;
private final Junction a;
private final Junction pin1;
private final Junction b;
private final double minVoltage;
private double resistance = 15;
private final double maxVoltage;
private double minVoltage = 1;
private double maxVoltage = 3;
private boolean defect = false;
private double voltage = 0;
private double potentialDifference = Double.NaN;
private Color color = BULB_OFF_COLOR;
@ -52,21 +54,16 @@ public class PartLight extends PartOther {
}
public PartLight(final Position position) {
this("Licht", position, Orientation.R0, BULB_VOLTAGE_MIN, VOLTAGE);
}
public PartLight(final String name, final Position position, final Orientation orientation, final double minVoltage, final double maxVoltage) {
super(name, position, orientation);
this.minVoltage = minVoltage;
this.maxVoltage = maxVoltage;
pin0 = newJunction(this, "", P10, P50);
pin1 = newJunction(this, "", P90, P50);
super("Licht", position);
a = newJunction(this, "A", P10, P50);
b = newJunction(this, "B", P90, P50);
}
public PartLight(final PartLightDto dto) {
super(dto);
pin0 = newJunction(this, dto.getMinus(), P10, P50);
pin1 = newJunction(this, dto.getPlus(), P90, P50);
a = newJunction(this, dto.getB(), P10, P50);
b = newJunction(this, dto.getA(), P90, P50);
resistance = dto.getResistance();
minVoltage = dto.getMinVoltage();
maxVoltage = dto.getMaxVoltage();
defect = dto.isDefect();
@ -78,26 +75,20 @@ public class PartLight extends PartOther {
}
@Override
public void propagate(final Junction source) {
voltage = abs(pin1.getVoltage() - pin0.getVoltage());
if (voltage > maxVoltage) {
defect = true;
}
if (defect) {
color = COLOR_DEFECT;
public void postCalculate() {
potentialDifference = abs(b.getVoltage() - a.getVoltage());
defect |= potentialDifference > maxVoltage;
if (defect || potentialDifference < minVoltage) {
color = BULB_OFF_COLOR;
} else {
final int v = (int) round(voltage / maxVoltage * 255);
if (v < 10) {
color = BULB_OFF_COLOR;
} else {
color = new Color(v, v, 0);
}
final int c = (int) round(255 * potentialDifference / maxVoltage);
color = new Color(c, c, 0);
}
}
@Override
protected void _render() {
render.line(pin0, pin1, Color.BLACK, SYMBOL_STROKE);
render.line(a, b, Color.BLACK, SYMBOL_STROKE);
render.circle(new Position(P50, P50), BULB_RADIUS, Color.BLACK, SYMBOL_STROKE, color);
final double diag = 0.33 * RASTER;
@ -107,6 +98,7 @@ public class PartLight extends PartOther {
@Override
protected void _labels() {
final int x = RASTER / 2;
final int y0;
final int y1;
if (getOrientation() == Orientation.R180) {
@ -117,9 +109,9 @@ public class PartLight extends PartOther {
y0 = P05;
y1 = P95 - LABEL_FONT.getSize();
}
render.textCenter(LABEL_FONT, "%.1fV (max)".formatted(maxVoltage), y0, Color.BLACK);
render.textCenter(LABEL_FONT, "%.1fV (max)".formatted(maxVoltage), x, y0, Color.BLACK);
if (defect) {
render.textCenter(LABEL_FONT, "DEFEKT", y1, Color.RED);
render.textCenter(LABEL_FONT, "DEFEKT", x, y1, Color.RED);
}
}

View File

@ -11,9 +11,11 @@ import lombok.ToString;
@NoArgsConstructor
public class PartLightDto extends PartDto {
private JunctionDto plus;
private JunctionDto a;
private JunctionDto minus;
private JunctionDto b;
private double resistance;
private double minVoltage;
@ -23,9 +25,10 @@ public class PartLightDto extends PartDto {
public PartLightDto(final PartLight partLight) {
super(partLight);
this.plus = new JunctionDto(partLight.getPin1());
this.minus = new JunctionDto(partLight.getPin0());
this.a = new JunctionDto(partLight.getB());
this.b = new JunctionDto(partLight.getA());
this.defect = partLight.isDefect();
this.resistance = partLight.getResistance();
this.minVoltage = partLight.getMinVoltage();
this.maxVoltage = partLight.getMaxVoltage();
}

View File

@ -1,8 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.ShortCircuit;
import de.ph87.electro.circuit.part.InnerConnection;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.PartOther;
import de.ph87.electro.circuit.part.Position;
import lombok.Getter;
@ -10,6 +9,7 @@ import lombok.Setter;
import lombok.ToString;
import java.awt.*;
import java.util.List;
import static de.ph87.electro.CONFIG.*;
@ -22,21 +22,16 @@ public class PartSwitch1x1 extends PartOther {
private final Junction output;
@Setter
private boolean state;
private boolean state = false;
public PartSwitch1x1() {
this(Position.ZERO);
}
public PartSwitch1x1(final Position position) {
this("Ausschalter", position, Orientation.R0, false);
}
public PartSwitch1x1(final String name, final Position position, final Orientation orientation, final boolean state) {
super(name, position, orientation);
common = newJunction(this, "", P10, P50);
output = newJunction(this, "", P90, P50);
this.state = state;
super("Ausschalter", position);
common = newJunction(this, "C", P10, P50);
output = newJunction(this, "O", P90, P50);
}
public PartSwitch1x1(final PartSwitch1x1Dto dto) {
@ -51,19 +46,6 @@ public class PartSwitch1x1 extends PartOther {
state = !state;
}
@Override
public void propagate(final Junction source) throws ShortCircuit {
if (source == common) {
if (state) {
output.propagate(source.getVoltage());
}
} else if (source == output) {
if (state) {
common.propagate(source.getVoltage());
}
}
}
@Override
protected void _render() {
if (!state) {
@ -74,4 +56,12 @@ public class PartSwitch1x1 extends PartOther {
}
}
@Override
public List<InnerConnection> getInnerConnections() {
if (state) {
return List.of(new InnerConnection(common, output));
}
return super.getInnerConnections();
}
}

View File

@ -1,14 +1,15 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.ShortCircuit;
import de.ph87.electro.circuit.part.InnerConnection;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.PartOther;
import de.ph87.electro.circuit.part.Position;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
import static de.ph87.electro.CONFIG.*;
@Getter
@ -22,22 +23,17 @@ public class PartSwitch1x2 extends PartOther {
private final Junction output1;
@Setter
private boolean state;
private boolean state = false;
public PartSwitch1x2() {
this(Position.ZERO);
}
public PartSwitch1x2(final Position position) {
this("Wechselschalter", position, Orientation.R0, false);
}
public PartSwitch1x2(final String name, final Position position, final Orientation orientation, final boolean state) {
super(name, position, orientation);
common = newJunction(this, "", P10, P50);
output0 = newJunction(this, "", P90, P25);
output1 = newJunction(this, "", P90, P75);
this.state = state;
super("Wechselschalter", position);
common = newJunction(this, "C", P10, P50);
output0 = newJunction(this, "O0", P90, P25);
output1 = newJunction(this, "O1", P90, P75);
}
public PartSwitch1x2(final PartSwitch1x2Dto dto) {
@ -53,25 +49,6 @@ public class PartSwitch1x2 extends PartOther {
state = !state;
}
@Override
public void propagate(final Junction source) throws ShortCircuit {
if (source == common) {
if (state) {
output1.propagate(source.getVoltage());
} else {
output0.propagate(source.getVoltage());
}
} else if (source == output0) {
if (!state) {
common.propagate(source.getVoltage());
}
} else if (source == output1) {
if (state) {
common.propagate(source.getVoltage());
}
}
}
@Override
protected void _render() {
if (!state) {
@ -81,4 +58,12 @@ public class PartSwitch1x2 extends PartOther {
}
}
@Override
public List<InnerConnection> getInnerConnections() {
if (state) {
return List.of(new InnerConnection(common, output1));
}
return List.of(new InnerConnection(common, output0));
}
}

View File

@ -1,14 +1,15 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.ShortCircuit;
import de.ph87.electro.circuit.part.InnerConnection;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.PartOther;
import de.ph87.electro.circuit.part.Position;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
import static de.ph87.electro.CONFIG.*;
@Getter
@ -24,23 +25,18 @@ public class PartSwitchCross extends PartOther {
private final Junction output1;
@Setter
private boolean state;
private boolean state = false;
public PartSwitchCross() {
this(Position.ZERO);
}
public PartSwitchCross(final Position position) {
this("Kreuzschalter", position, Orientation.R0, false);
}
public PartSwitchCross(final String name, final Position position, final Orientation orientation, final boolean state) {
super(name, position, orientation);
common0 = newJunction(this, "", P10, P25);
common1 = newJunction(this, "", P10, P75);
output0 = newJunction(this, "", P90, P25);
output1 = newJunction(this, "", P90, P75);
this.state = state;
super("Kreuzschalter", position);
common0 = newJunction(this, "C0", P10, P25);
common1 = newJunction(this, "C1", P10, P75);
output0 = newJunction(this, "O0", P90, P25);
output1 = newJunction(this, "O1", P90, P75);
}
public PartSwitchCross(final PartSwitchCrossDto dto) {
@ -57,35 +53,6 @@ public class PartSwitchCross extends PartOther {
state = !state;
}
@Override
public void propagate(final Junction source) throws ShortCircuit {
if (source == common0) {
if (state) {
output1.propagate(source.getVoltage());
} else {
output0.propagate(source.getVoltage());
}
} else if (source == common1) {
if (state) {
output0.propagate(source.getVoltage());
} else {
output1.propagate(source.getVoltage());
}
} else if (source == output0) {
if (state) {
common1.propagate(source.getVoltage());
} else {
common0.propagate(source.getVoltage());
}
} else if (source == output1) {
if (state) {
common0.propagate(source.getVoltage());
} else {
common1.propagate(source.getVoltage());
}
}
}
@Override
protected void _render() {
if (!state) {
@ -97,4 +64,18 @@ public class PartSwitchCross extends PartOther {
}
}
@Override
public List<InnerConnection> getInnerConnections() {
if (state) {
return List.of(
new InnerConnection(common0, output1),
new InnerConnection(common1, output0)
);
}
return List.of(
new InnerConnection(common0, output0),
new InnerConnection(common1, output1)
);
}
}

View File

@ -12,27 +12,27 @@ public class BatteryLightTest {
private static final double VOLTAGE = 3;
private static final Circuit circuit = new Circuit();
private static final Circuit CIRCUIT = new Circuit();
private static final PartBattery battery = circuit.addPart(new PartBattery());
private static final PartBattery battery = CIRCUIT.addPart(new PartBattery());
private static final PartLight light = circuit.addPart(new PartLight());
private static final PartLight light = CIRCUIT.addPart(new PartLight());
@BeforeAll
public static void setUp() {
circuit.connect(battery.getPlus(), light.getPin1());
circuit.connect(light.getPin0(), battery.getMinus());
CIRCUIT.connect(battery.getPlus(), light.getB());
CIRCUIT.connect(light.getA(), battery.getMinus());
}
@Test
void test() {
circuit.evaluateAndRender();
CIRCUIT.evaluateAndRender();
assertEquals(VOLTAGE, battery.getPlus().getVoltage());
assertEquals(VOLTAGE, light.getPin1().getVoltage());
assertEquals(VOLTAGE, light.getVoltage());
assertEquals(VOLTAGE, light.getB().getVoltage());
assertEquals(VOLTAGE, light.getPotentialDifference());
assertEquals(0, light.getPin0().getVoltage());
assertEquals(0, light.getA().getVoltage());
assertEquals(0, battery.getMinus().getVoltage());
assertFalse(light.isDefect());

View File

@ -13,19 +13,19 @@ public class BatterySwitcher1x1Test {
private static final double VOLTAGE = 3;
private static final Circuit circuit = new Circuit();
private static final Circuit CIRCUIT = new Circuit();
private static final PartBattery battery = circuit.addPart(new PartBattery());
private static final PartBattery battery = CIRCUIT.addPart(new PartBattery());
private static final PartSwitch1x1 switcher = circuit.addPart(new PartSwitch1x1());
private static final PartSwitch1x1 switcher = CIRCUIT.addPart(new PartSwitch1x1());
private static final PartLight light = circuit.addPart(new PartLight());
private static final PartLight light = CIRCUIT.addPart(new PartLight());
@BeforeAll
public static void setUp() {
circuit.connect(battery.getPlus(), switcher.getCommon());
circuit.connect(switcher.getOutput(), light.getPin1());
circuit.connect(light.getPin0(), battery.getMinus());
CIRCUIT.connect(battery.getPlus(), switcher.getCommon());
CIRCUIT.connect(switcher.getOutput(), light.getB());
CIRCUIT.connect(light.getA(), battery.getMinus());
}
@Test
@ -43,7 +43,7 @@ public class BatterySwitcher1x1Test {
switcher.setState(state);
circuit.evaluateAndRender();
CIRCUIT.evaluateAndRender();
assertEquals(state, switcher.isState());
@ -51,10 +51,10 @@ public class BatterySwitcher1x1Test {
assertEquals(VOLTAGE, switcher.getCommon().getVoltage());
assertEquals(voltage, switcher.getOutput().getVoltage());
assertEquals(voltage, light.getPin1().getVoltage());
assertEquals(voltage, light.getVoltage());
assertEquals(voltage, light.getB().getVoltage());
assertEquals(voltage, light.getPotentialDifference());
assertEquals(0, light.getPin0().getVoltage());
assertEquals(0, light.getA().getVoltage());
assertEquals(0, battery.getMinus().getVoltage());
assertFalse(light.isDefect());

View File

@ -13,23 +13,23 @@ public class BatterySwitcher1x2Test {
private static final double VOLTAGE = 3;
private static final Circuit circuit = new Circuit();
private static final Circuit CIRCUIT = new Circuit();
private static final PartBattery battery = circuit.addPart(new PartBattery());
private static final PartBattery battery = CIRCUIT.addPart(new PartBattery());
private static final PartSwitch1x2 switcher = circuit.addPart(new PartSwitch1x2());
private static final PartSwitch1x2 switcher = CIRCUIT.addPart(new PartSwitch1x2());
private static final PartLight light0 = circuit.addPart(new PartLight());
private static final PartLight light0 = CIRCUIT.addPart(new PartLight());
private static final PartLight light1 = circuit.addPart(new PartLight());
private static final PartLight light1 = CIRCUIT.addPart(new PartLight());
@BeforeAll
public static void setUp() {
circuit.connect(battery.getPlus(), switcher.getCommon());
circuit.connect(switcher.getOutput0(), light0.getPin1());
circuit.connect(light0.getPin0(), battery.getMinus());
circuit.connect(switcher.getOutput1(), light1.getPin1());
circuit.connect(light1.getPin0(), battery.getMinus());
CIRCUIT.connect(battery.getPlus(), switcher.getCommon());
CIRCUIT.connect(switcher.getOutput0(), light0.getB());
CIRCUIT.connect(light0.getA(), battery.getMinus());
CIRCUIT.connect(switcher.getOutput1(), light1.getB());
CIRCUIT.connect(light1.getA(), battery.getMinus());
}
@Test
@ -48,7 +48,7 @@ public class BatterySwitcher1x2Test {
switcher.setState(state);
circuit.evaluateAndRender();
CIRCUIT.evaluateAndRender();
assertEquals(state, switcher.isState());
@ -56,15 +56,15 @@ public class BatterySwitcher1x2Test {
assertEquals(VOLTAGE, switcher.getCommon().getVoltage());
assertEquals(voltage0, switcher.getOutput0().getVoltage());
assertEquals(voltage0, light0.getPin1().getVoltage());
assertEquals(voltage0, light0.getVoltage());
assertEquals(voltage0, light0.getB().getVoltage());
assertEquals(voltage0, light0.getPotentialDifference());
assertEquals(voltage1, switcher.getOutput1().getVoltage());
assertEquals(voltage1, light1.getPin1().getVoltage());
assertEquals(voltage1, light1.getVoltage());
assertEquals(voltage1, light1.getB().getVoltage());
assertEquals(voltage1, light1.getPotentialDifference());
assertEquals(0, light0.getPin0().getVoltage());
assertEquals(0, light1.getPin0().getVoltage());
assertEquals(0, light0.getA().getVoltage());
assertEquals(0, light1.getA().getVoltage());
assertEquals(0, battery.getMinus().getVoltage());
assertFalse(light0.isDefect());

View File

@ -13,23 +13,23 @@ public class BatterySwitcher2x2Test {
private static final double VOLTAGE = 3;
private static final Circuit circuit = new Circuit();
private static final Circuit CIRCUIT = new Circuit();
private static final PartBattery battery = circuit.addPart(new PartBattery());
private static final PartBattery battery = CIRCUIT.addPart(new PartBattery());
private static final PartSwitch1x2 switcher0 = circuit.addPart(new PartSwitch1x2());
private static final PartSwitch1x2 switcher0 = CIRCUIT.addPart(new PartSwitch1x2());
private static final PartSwitch1x2 switcher1 = circuit.addPart(new PartSwitch1x2());
private static final PartSwitch1x2 switcher1 = CIRCUIT.addPart(new PartSwitch1x2());
private static final PartLight light = circuit.addPart(new PartLight());
private static final PartLight light = CIRCUIT.addPart(new PartLight());
@BeforeAll
public static void setUp() {
circuit.connect(battery.getPlus(), switcher0.getCommon());
circuit.connect(switcher0.getOutput0(), switcher1.getOutput0());
circuit.connect(switcher0.getOutput1(), switcher1.getOutput1());
circuit.connect(switcher1.getCommon(), light.getPin1());
circuit.connect(light.getPin0(), battery.getMinus());
CIRCUIT.connect(battery.getPlus(), switcher0.getCommon());
CIRCUIT.connect(switcher0.getOutput0(), switcher1.getOutput0());
CIRCUIT.connect(switcher0.getOutput1(), switcher1.getOutput1());
CIRCUIT.connect(switcher1.getCommon(), light.getB());
CIRCUIT.connect(light.getA(), battery.getMinus());
}
@Test
@ -60,7 +60,7 @@ public class BatterySwitcher2x2Test {
switcher0.setState(state0);
switcher1.setState(state1);
circuit.evaluateAndRender();
CIRCUIT.evaluateAndRender();
assertEquals(state0, switcher0.isState());
assertEquals(state1, switcher1.isState());
@ -75,10 +75,10 @@ public class BatterySwitcher2x2Test {
assertEquals(voltage1, switcher1.getOutput1().getVoltage());
assertEquals(voltage, switcher1.getCommon().getVoltage());
assertEquals(voltage, light.getPin1().getVoltage());
assertEquals(voltage, light.getVoltage());
assertEquals(voltage, light.getB().getVoltage());
assertEquals(voltage, light.getPotentialDifference());
assertEquals(0, light.getPin0().getVoltage());
assertEquals(0, light.getA().getVoltage());
assertEquals(0, battery.getMinus().getVoltage());
assertFalse(light.isDefect());

View File

@ -14,27 +14,27 @@ public class BatterySwitcherCrossTest {
private static final double VOLTAGE = 3;
private static final Circuit circuit = new Circuit();
private static final Circuit CIRCUIT = new Circuit();
private static final PartBattery battery = circuit.addPart(new PartBattery());
private static final PartBattery battery = CIRCUIT.addPart(new PartBattery());
private static final PartSwitch1x2 switcher0 = circuit.addPart(new PartSwitch1x2());
private static final PartSwitch1x2 switcher0 = CIRCUIT.addPart(new PartSwitch1x2());
private static final PartSwitchCross switcherX = circuit.addPart(new PartSwitchCross());
private static final PartSwitchCross switcherX = CIRCUIT.addPart(new PartSwitchCross());
private static final PartSwitch1x2 switcher1 = circuit.addPart(new PartSwitch1x2());
private static final PartSwitch1x2 switcher1 = CIRCUIT.addPart(new PartSwitch1x2());
private static final PartLight light = circuit.addPart(new PartLight());
private static final PartLight light = CIRCUIT.addPart(new PartLight());
@BeforeAll
public static void setUp() {
circuit.connect(battery.getPlus(), switcher0.getCommon());
circuit.connect(switcher0.getOutput0(), switcherX.getCommon0());
circuit.connect(switcher0.getOutput1(), switcherX.getCommon1());
circuit.connect(switcherX.getOutput0(), switcher1.getOutput0());
circuit.connect(switcherX.getOutput1(), switcher1.getOutput1());
circuit.connect(switcher1.getCommon(), light.getPin1());
circuit.connect(light.getPin0(), battery.getMinus());
CIRCUIT.connect(battery.getPlus(), switcher0.getCommon());
CIRCUIT.connect(switcher0.getOutput0(), switcherX.getCommon0());
CIRCUIT.connect(switcher0.getOutput1(), switcherX.getCommon1());
CIRCUIT.connect(switcherX.getOutput0(), switcher1.getOutput0());
CIRCUIT.connect(switcherX.getOutput1(), switcher1.getOutput1());
CIRCUIT.connect(switcher1.getCommon(), light.getB());
CIRCUIT.connect(light.getA(), battery.getMinus());
}
@Test
@ -88,7 +88,7 @@ public class BatterySwitcherCrossTest {
switcherX.setState(stateX);
switcher1.setState(state1);
circuit.evaluateAndRender();
CIRCUIT.evaluateAndRender();
assertEquals(state0, switcher0.isState());
assertEquals(stateX, switcherX.isState());
@ -110,10 +110,10 @@ public class BatterySwitcherCrossTest {
assertEquals(voltageX1, switcher1.getOutput1().getVoltage());
assertEquals(voltage, switcher1.getCommon().getVoltage());
assertEquals(voltage, light.getPin1().getVoltage());
assertEquals(voltage, light.getVoltage());
assertEquals(voltage, light.getB().getVoltage());
assertEquals(voltage, light.getPotentialDifference());
assertEquals(0, light.getPin0().getVoltage());
assertEquals(0, light.getA().getVoltage());
assertEquals(0, battery.getMinus().getVoltage());
assertFalse(light.isDefect());

View File

@ -13,15 +13,15 @@ import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CircuitServiceTest {
class CircuitCalculationServiceTest {
@Test
void serialization() throws IOException {
final Circuit circuit = new Circuit();
final PartBattery battery = circuit.addPart(new PartBattery());
final PartLight light = circuit.addPart(new PartLight());
circuit.connect(battery.getPlus(), light.getPin1());
circuit.connect(light.getPin0(), battery.getMinus());
circuit.connect(battery.getPlus(), light.getB());
circuit.connect(light.getA(), battery.getMinus());
check(circuit);
}

View File

@ -0,0 +1,40 @@
package de.ph87.electro.circuit.net;
import de.ph87.electro.circuit.Calculation;
import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.Wire;
import de.ph87.electro.circuit.part.Junction;
import de.ph87.electro.circuit.part.parts.PartBattery;
import de.ph87.electro.circuit.part.parts.PartLight;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
class CalculationServiceTest {
@Test
void test() {
final Circuit circuit = new Circuit();
final PartBattery battery = circuit.addPart(new PartBattery());
final PartLight light = circuit.addPart(new PartLight());
circuit.connect(battery.getMinus(), light.getA());
circuit.connect(battery.getPlus(), light.getB());
final Calculation calculation = new Calculation(circuit);
calculation.toString().lines().forEach(log::info);
circuit.streamParts().forEach(part -> {
log.info("");
log.info(part.toString());
for (final Junction junction : part.getJunctions()) {
log.info(" \"%s\" = %.2f V".formatted(junction.getName(), junction.getVoltage()));
for (final Wire wire : junction.getWires()) {
final Junction opposite = wire.getOpposite(junction);
log.info(" -> %s.%s = %.2f A".formatted(opposite.getOwner().getName(), opposite.getName(), wire.getCurrent(junction)));
}
}
});
log.info("");
}
}

View File

@ -0,0 +1,19 @@
package de.ph87.electro.circuit.net;
import de.ph87.electro.circuit.Calculation;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
class CalculationTest {
@Test
void test() {
final Calculation calculation = new Calculation(1);
calculation.addResistor(-1, 0, 15);
calculation.addCurrentSource(-1, 0, 4, 5);
calculation.solve();
calculation.toString().lines().forEach(log::info);
}
}