Circuit builder that calculates resistance/current/voltage
$begingroup$
I am somewhat new to programming. I've written a program that takes user input accepting voltages and resistors, and calculating total resistance/current/voltage in the circuit. I am looking for ways to improve my coding, whether it be improving its time/space complexity or making better comments to make it more maintainable and scalable (as I have plans to expand it to accept more circuit component types) or implementing more efficient algorithms. You can find the files on GitHub.
There are some additional files in the GitHub (pictures/README) that also help to explain the program.
Circuit.java
package circuit;
import java.util.ArrayList;
/**
* The circuit to which components can be added to.
*
* An object of this class exists in the program as a singleton object.
*
* It holds the ArrayList of components in the circuit.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Circuit {
/*Creates only one instance of a circuit.*/
private static Circuit instance = null;
protected static Circuit getInstance() {
if (instance == null) {
instance = new Circuit();
}
return instance;
}
/**Instance variable list to contain components.*/
private ArrayList<Component> components;
/**Private constructor ensures only one instance of circuit can be created.*/
private Circuit() {
this.components = new ArrayList<>();
}
/*Methods.*/
/** get method to get list of components
* @param none
* @return ArrayList<Component> components*/
protected ArrayList<Component> getComponents(){
return this.components;
}
/**Add component to circuit
* @param Component c.*/
protected void addComponent(Component c){
this.components.add(c);
}
/**Return information of all components in circuit
* @return String.*/
@Override
public String toString(){
String str="";
/*For each object in array.*/
for(Object obj : components){
/*If it is a resistor.*/
if (obj.getClass() == Resistor.class){
/*Downcast to original object type to use class toString() method.*/
str+= ("Resistor: "+(Resistor)obj).toString()+"n";
}
/*Another form of testing object class, if it is a voltage.*/
if (obj instanceof Voltage){
/*Downcast to original object type to use class toString() method.*/
str+= ("Voltage: "+(Voltage)obj).toString()+"n";
}
}
/*Remove last n produced from the above iteration.*/
str = str.substring(0,str.length()-1);
return str;
}
}
CircuitAnalysis.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
/** Assistant class to calculate circuit characteristics.
*
* Input requires reference ground node and the components in the circuit.
*
* I decided to compartmentalize this part of the program in a separate class to simplify the coding and maintenance, but when considering resource management it would likely just be methods within UserMain.
*
* The resistor reduction algorithm used by this class is to first reduce resistors that are in parallel between the same two nodes to a single equivalent resistor between those nodes, then to reduce any serial resistors
* to a single equivalent resistor between the two outer-nodes which will then create more parallel resistors between the same two nodes, and so on.
*
*
* @author Michael Sinclair.
* @version 2.304
* @since 29 January 2019.
*/
public class CircuitAnalysis {
/* instance variables */
private ArrayList<Node> nodeList;
private ArrayList<Component> components;
private int ground;
/* to calculate characteristics */
private double totalV;
private double totalR;
private int voltageSources;
/* to rewind resistor Id count for user to continue after calculations */
private int countResistors;
/** Only constructor for this class
*
* @param int ground
* @param ArrayList<Component> comps
* @param ArrayList<Node> nodes
*/
protected CircuitAnalysis(int ground, ArrayList<Component> comps, ArrayList<Node> nodes) {
/* initialize variables */
this.ground = ground;
this.totalR = 0.0;
this.totalV = 0.0;
this.voltageSources = 0;
countResistors = 0;
/* copy the ArrayLists so that the User can continue operations after calculation, and to enable node specific calculations based on original list */
this.components = new ArrayList<>(comps);
/* have to create new Node objects to avoid altering the input list - basically dereferencing the node objects from the input and creating clone objects of them with t he same id */
this.nodeList = new ArrayList<>();
for(Node node:nodes) {
nodeList.add(new Node(node.getId()));
}
/* now point copied components to the copied nodeList, and attach the copied components to the copied nodes */
for (Component comp:this.components) {
for(Node node:this.nodeList) {
/* if the component points to this iteration's node */
if(comp.getNode1().getId()==node.getId()) {
/* point it to the new copy object in this class nodeList */
comp.setNode1(node);
/* and connect it to the attached copy node */
node.connect(comp);
}
/* same for second node */
if(comp.getNode2().getId()==node.getId()) {
comp.setNode2(node);
node.connect(comp);
}
}
}
/* sort the resistor nodes of the copies, maintain the ordering for the original list that the user input for their resistors */
/* sort the ArrayList of components by node 1 and node 2 (smaller of both first) - note that by construction, voltages will always have ordered nodes */
for (int j = 0; j<components.size();j++) {
for (int i = 0; i<components.size()-1;i++) {
if (components.get(i).compare(components.get(i+1))>0) {
/* if component nodes are disordered, swap them */
Collections.swap(components, i, i+1);
}
}
}
}
/* methods */
/** No parameters or returns, automates circuit measurements by calling analyzeVoltage(),analyzeResistance(),printCharactersitics() and reduces resistor count for resistors created for circuit calculations */
protected void analyzeCircuit() {
/* find total voltage and count voltage sources */
this.analyzeVoltage();
/* find total resistance of the circuit */
this.analyzeResistance();
System.out.println("");
/* print out calculated circuit characteristics */
this.printCharacteristics();
/* rewind resistor count for user to continue altering circuit */
/* if for some (unknown) reason, the user did not enter any resistors do not alter the resistor count */
if(countResistors == 0) {
return;
}
/* for each resistor added, lower the global resistor id number to sync the number back with the user's circuit */
for(int i=0;i<countResistors;i++) {
Resistor.resnum--;
}
}
/**No parameters or returns, finds total voltage in the circuit - note that this program can currently only handle directly serial voltage (connected in series to each other) */
protected void analyzeVoltage() {
/* for each component */
for (int i = 0; i<components.size();i++) {
/* if it is a voltage */
if (components.get(i).getClass() == Voltage.class) {
/* get the voltage */
this.totalV+=((Voltage)(components.get(i))).getV();
/* count voltage sources */
this.voltageSources++;
}
}
}
/**No parameters or returns, finds total resistance in the circuit */
protected void analyzeResistance() {
/* while more than 1 resistor exists */
while(components.size()>this.voltageSources+1) {
/* reduce parallel resistors across the same nodes to one resistor */
this.analyzeParallelSameNode();
/* reduce serial resistors individually in the circuit */
this.analyzeSeriesIndividually();
}
/* now that there is only one resistor in the circuit iterate through the circuit */
for (int i = 0; i<components.size();i++) {
/* if it is a resistor */
if (components.get(i) instanceof Resistor) {
/* get the resistance - note this only executes once */
this.totalR+=((Resistor)components.get(i)).getR();
}
}
}
/**No parameters or returns, reduces same-node parallel resistors to a single equivalent resistor */
protected void analyzeParallelSameNode() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
ArrayList<Component> toConnect = new ArrayList<>();
/* for each node */
/* TODO explore possibility that only one for loop is needed for the nodeList - and simply compare second node */
for (int n = 0; n<nodeList.size();n++) {
/* find components connected to each other node */
for (int m = 0; m<nodeList.size();m++) {
/* components cannot have the same node on both sides & don't want to do the same two nodes twice */
if (n!=m && n<m) {
/* for each component */
for (int k = 0;k<components.size();k++) {
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* if it is a resistor */
if (components.get(k).getClass() == Resistor.class) {
/* if it is between the two nodes */
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* add it to temporary list */
temp.add(components.get(k));
}
}
}
}
/* if a parallel connection was found between node n and m*/
if(temp.size()>1) {
/* create equivalent parallel resistor */
Resistor equivalent = new Resistor(this.parallelResistors(temp),this.findNode(n),this.findNode(m));
/* for rewinding resistor id */
this.countResistors++;
/* queue it for connection */
toConnect.add(equivalent);
/* queue resistors that need to be removed */
for(Component remove:temp) {
toRemove.add(remove);
}
}
/* clear the list for future calculations */
temp.clear();
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
/* attach equivalent resistors */
for(Component comp:toConnect) {
components.add(comp);
comp.getNode1().connect(comp);
comp.getNode2().connect(comp);
}
}
/* No parameters or returns, reduces any two serially connected resistors individually */
protected void analyzeSeriesIndividually() {
ArrayList<Component> toAdd = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
Node firstNode = null;
Node secondNode = null;
/* can only perform this operation a single time before calling it again - resulted in errors created floating resistors that could not be reduced further otherwise */
boolean doOnce = false;
/* for each node */
for(Node node:nodeList) {
/* if there are 2 attachments that are both resistors */
if (node.getAttachments().size()==2 && node.getAttachments().get(0) instanceof Resistor && node.getAttachments().get(1) instanceof Resistor && !doOnce) {
/* find first and second node by Id - one must have a first node prior to the current node being tested and one must have a node after */
if(node.getAttachments().get(0).getNode1().getId()<node.getAttachments().get(1).getNode1().getId()) {
firstNode = node.getAttachments().get(0).getNode1();
secondNode = node.getAttachments().get(1).getNode2();
}
else {
firstNode = node.getAttachments().get(1).getNode1();
secondNode = node.getAttachments().get(0).getNode2();
}
/* if not already queued for removal */
if(!toRemove.contains(node.getAttachments().get(0))) {
if(!toRemove.contains(node.getAttachments().get(1))) {
toRemove.add(node.getAttachments().get(0));
toRemove.add(node.getAttachments().get(1));
toAdd.add(new Resistor(((Resistor)node.getAttachments().get(0)).getR()+((Resistor)node.getAttachments().get(1)).getR(),firstNode,secondNode));
/* for rewinding resistor id */
this.countResistors++;
}
}
/* prevent program from combining more than two serial resistors at the same time */
doOnce = true;
}
}
/* combine serial resistors individually - first remove them from the circuit */
for(Component remove:toRemove) {
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(remove);
}
/* then add the equivalent resistors */
for(Component addR:toAdd) {
addR.getNode1().connect(addR);
addR.getNode2().connect(addR);
components.add(addR);
}
}
/** Find ArrayList index - note ArrayList has built in remove with object parameter but I wanted to use this instead as I was encountering problems with the built in method
* @param ArrayList<Component> findList
* @param Component find
* @return int */
protected int findIndex(ArrayList<Component> findList, Component find) {
int i;
/* iterate through ArrayList until object is found */
for (i = 0;i<findList.size();i++) {
if(findList.contains(find)) {
break;
}
}
return i;
}
/** Determine if resistor already queued for removal, returns true to enable above loop if component is not already queued for removal
* @param Component resistor
* @param ArrayList<Component> toRemove
* @return boolean*/
protected boolean queuedRemoval(Component resistor, ArrayList<Component> toRemove){
/* for each component queued for removal */
for(Component component:toRemove) {
/* if the Id matches any resistor Id in the removal list, and for good measure check that it is a resistor */
if(component.getId()==resistor.getId() && component.getClass()==Resistor.class) {
/* return false to disable the above loop */
return false;
}
}
/* else return true */
return true;
}
/** Find node based on id
* @param int id
* @return Node*/
protected Node findNode(int id) {
/* value to store index */
int i = 0;
/* for each node */
for(Node node:nodeList) {
/* if it does not equal the desired node */
if(node.getId()!=id) {
/* increase the index */
i++;
}
/* if it does */
else {
/* stop searching */
break;
}
}
return nodeList.get(i);
}
/** Calculate parallel resistance from a list of resistors
* @param ArrayList<Component> resistors
* @return double*/
protected double parallelResistors(ArrayList<Component> resistors) {
double parallelR = 0.0;
if(resistors.size()==0) {
throw new IllegalArgumentException("Must input at least one resistor.");
}
for (Component res:resistors) {
/* quick check to make sure only resistances get added to the total */
if(res.getClass()==Resistor.class) {
parallelR+=1/(((Resistor)res).getR());
}
}
return 1/parallelR;
}
/**No parameters or returns, Print circuit Characteristics */
protected void printCharacteristics() {
System.out.println("Ground voltage is located at Node "+this.ground+".");
System.out.println("Total voltage in circuit is: "+this.totalV+ " Volts.");
System.out.println("Total resistance in circuit is: "+this.totalR+" Ohms.");
System.out.println("Total current is: "+this.totalV/this.totalR+" Amps.");
}
/* get methods for testing private instance variables */
/** get nodeList, no parameters
* @return ArrayList<Node>
*/
public ArrayList<Node> getNodeList(){
return this.nodeList;
}
/** gets the list of components, no parameters
*
* @return ArrayList<Component>
*/
public ArrayList<Component> getComponents(){
return this.components;
}
/** get voltage, no parameters
* @return double
*/
public double getV() {
return this.totalV;
}
/** gets the resistance of the circuit, no parameters
*
* @return double
*/
public double getR() {
return this.totalR;
}
/** gets the ground node id
*
* @return int
*/
public int getG() {
return this.ground;
}
/** METHOD NO LONGER IN USE - changed algorithm for solving circuit problem - storing it in case it is useful in future */
/* Reduce multiple serial resistors to a single equivalent resistor */
protected void analyzeSerialResistances() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
int nodalCase = 0;
/* for each node */
for(Node node1:nodeList) {
/* compare to other nodes */
for(Node node2:nodeList) {
/* if not the same node and looking forwards */
if(node1.getId()<node2.getId()) {
/* if both nodes only have 2 attachments */
if(node1.getAttachments().size()==2 && node2.getAttachments().size()==2) {
/* iterate through the attachments that are resistors */
for(int i = 0; i<2; i++) {
for(int j = 0; j<2; j++) {
/* if the components are not already queued for removal */
if(this.queuedRemoval(node1.getAttachments().get(i),toRemove) && this.queuedRemoval(node2.getAttachments().get((j+1)%2),toRemove)) {
/* if a common resistor is found between the nodes and both nodes only have 2 attachments, then the common resistor must be in series with the second nodes attached item */
if (node1.getAttachments().get(i).getId()==node2.getAttachments().get(j).getId() && node1.getAttachments().get(i) instanceof Resistor) {
/* if the second node's other attached item is also a resistor */
if(node2.getAttachments().get((j+1)%2) instanceof Resistor) {
/* queue them for equivalence calculation */
temp.add(node1.getAttachments().get(i));
temp.add(node2.getAttachments().get((j+1)%2));
/* find the common node */
if(temp.get(0).getNode1().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 1;
}
if(temp.get(0).getNode1().getId() == temp.get(1).getNode2().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 2;
}
if(temp.get(0).getNode2().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 3;
}
/* note chose to not use just plain else to verify the last condition is true, even though it is the only possible combination of common nodes left */
if(temp.get(0).getNode2().getId() == temp.get(1).getNode2().getId()) {
nodalCase = 4;
}
}
}
}
}
}
/* if series resistors were found */
if(temp.size()==2) {
/* queue resistors for removal */
toRemove.add(temp.get(0));
toRemove.add(temp.get(1));
Resistor equivalent = null;
/* queue equivalent resistor to be added */
if(nodalCase == 1) {
/* first nodal case - shared 1st/1st node so connect equivalent resistor between both 2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode2());
}
if(nodalCase == 2) {
/* second nodal case - shared 1st/2nd node so connect equivalent resistor between 2nd/1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode1());
}
if(nodalCase == 3) {
/* third nodal case - shared 2nd/1st node so connect equivalent resistor between 1st/2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode2());
}
/* chose not to use simple else for reason stated above */
if(nodalCase == 4) {
/* last nodal case - shared 2nd/2nd node so connect equivalent resistor between both 1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode1());
}
components.add(equivalent);
equivalent.getNode1().connect(equivalent);
equivalent.getNode2().connect(equivalent);
temp.clear();
}
}
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
}
}
Component.java
package circuit;
/**
* An abstract class to quantify common elements to components within the circuit.
*
* Mainly handles component Id features.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public abstract class Component {
/*Instance variables.*/
protected Node nodal1;
protected Node nodal2;
protected int id;
/** Superclass constructor
* @param Node node1
* @param Node node2
*/
protected Component(Node node1, Node node2){
this.nodal1 = node1;
this.nodal2 = node2;
}
/*Methods */
/*get/set methods*/
/** get first node, no parameters
*
* @return Node nodal1
*/
protected Node getNode1() {
return this.nodal1;
}
/** get second node, no parameters
*
* @return Node nodal2
*/
protected Node getNode2() {
return this.nodal2;
}
/** set first node, no return
*
* @param Node node1
*/
protected void setNode1(Node node1) {
this.nodal1 = node1;
}
/** set second node, no return
*
* @param Node node2
*/
protected void setNode2(Node node2) {
this.nodal2 = node2;
}
/** get component id, no parameters
*
* @return int id
*/
protected int getId() {
return this.id;
}
/** set component id, no return
*
* @param int i
*/
protected void setId(int i) {
this.id = i;
}
/** method for testing if connected through only 1 node for use in nodal analysis , returns true if components are connected through the first node and not the second
*
* @param Component other
* @return boolean
* */
protected boolean testNode(Component other) {
if (this.nodal1 == other.nodal1) {
if (this.nodal2 != other.nodal1) {
return true;
}
}
return false;
}
/**Return list of nodes connected to voltage source, no parameters
* @return Node list.
* */
protected Node getNodes(){
return new Node {this.nodal1 , this.nodal2};
}
/** define equals method, returns true if Ids are the same otherwise false
* @param Component other
* @return boolean
* */
protected boolean equals(Component other) {
if (this.getId() == other.getId()) {
return true;
}
else return false;
}
/** define compare method for sorting
* if the first node Id is smaller than the other first node, method returns a negative number and vice versa
* if the first node Id is the same, and the second node is smaller than the other second node, method returns a negative number and vice versa
* if both nodes are equal, method returns 0
* @param Component other
* @return int
* */
protected int compare(Component other) {
if (this.getNode1().getId() == other.getNode1().getId()) {
return this.getNode2().getId()-other.getNode2().getId();
}
else {
return this.getNode1().getId()-other.getNode1().getId();
}
}
/** make components override toString()
* @return String
*/
@Override
public abstract String toString();
/** desire a toString that displays different information
* @return String
* */
public abstract String toStringFinal();
}
Node.java
package circuit;
import java.util.ArrayList;
/**
* A node class, to connect circuit components.
*
* Contains an id that describes the node, as well as the voltage at the node (to be added), the current leaving the node (to be added) and an array list of components attached to the node.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Node {
/*Instance variables*/
private int id;
private double voltageAt;
private ArrayList<Component> attachments;
private double currentLeaving;
/**Assign an id to this node.
* @param nodal_id.*/
protected Node(int nodalId) {
this.id = nodalId;
this.voltageAt = 0.0;
this.attachments = new ArrayList<>();
this.currentLeaving = 0.0;
}
/*Methods*/
/**get id, no parameters
* @return int id
* */
protected int getId(){
return this.id;
}
/** set voltage at Node, no return
* @param double volts
* */
protected void setVoltage(double volts) {
this.voltageAt = volts;
}
/** get voltage at Node, no parameters
* @return double voltageAt
* */
protected double getVoltage() {
return this.voltageAt;
}
/** set current leaving Node, no return
* @param double current
* */
protected void setCurrent(double current) {
this.currentLeaving = current;
}
/** get current leaving Node, no parameters
* @return double currentLeaving
* */
protected double getCurrent() {
return this.currentLeaving;
}
/** connection a component to this node, methods for tracking component connections, no return
* @param Component component
* */
protected void connect(Component component) {
this.attachments.add(component);
}
/** disconnect a component from this node, methods for tracking component connections, no return
*
* @param Component component
*/
protected void disconnect(Component component) {
this.attachments.remove(component);
}
/** get the list of attachments that are attached to this node, no parameters
*
* @return ArrayList<Component> attachments
*/
protected ArrayList<Component> getAttachments(){
return this.attachments;
}
/**Display node id, overrides toString(), no parameters
* @return String.*/
@Override
public String toString() {
return ""+this.id;
}
/** method for displaying specific information about this node, no parameters
*
* @return String
*/
public String toStringSpecific() {
return "Node"+this.id+" Current Leaving: "+this.currentLeaving+" Amps and Voltage at node:" + this.voltageAt+" Volts.";
}
/** method for displaying the attachments connected to this node, mainly used for debugging, no parameters, displays a string version of the list of attachments
*
* @return String
*/
public String toStringAttachments() {
String str = "Node"+this.id;
for(int i=0;i<attachments.size();i++) {
str+=" "+attachments.get(i).toString();
}
return str;
}
}
NodeChecker.java
package circuit;
import java.util.ArrayList;
/** A helper class to simplify UserMain code.
*
* Evaluates whether nodes exist already as a component is added, and if not creates them within UserMain's nodeList ArrayList.
*
* Decided to compartmentalize this portion of the program to remove large duplicate code blocks and simplify UserMain.
*
* Would likely normally just make this a method within UserMain if resources and time were of concern in this program. Chose to make it a separate class to make program easier to view, for now.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class NodeChecker {
private Node node1;
private Node node2;
private ArrayList<Node> nodeList;
/** constructor for building this class
*
* @param Node nod1
* @param Node nod2
* @param ArrayList<Node> nodeList
*/
public NodeChecker(int nod1, int nod2, ArrayList<Node> nodeList){
this.nodeList = nodeList;
/*Check that component does not have the same nodes on both ends. Note would be part of input validation*/
if (nod1 == nod2){
throw new IllegalArgumentException("Nodes must be different for a single component.");
}
this.node1 = new Node(nod1);
this.node2 = new Node(nod2);
/*If these are the first two nodes, add them.*/
if (nodeList.isEmpty()){
nodeList.add(node1);
nodeList.add(node2);
}
int flag1 = 0;
int flag2 = 0;
/*If nodes do not exist, create them.*/
for (Node node : nodeList){
if (node.getId() == node1.getId()){
/*If found set flag and break.*/
flag1 = 1;
break;
}
}
for (Node node : nodeList){
if (node.getId() == node2.getId()){
/*If found set flag and break.*/
flag2 = 1;
break;
}
}
/*If not found.*/
if (flag1 == 0){
nodeList.add(node1);
}
if (flag2 == 0){
nodeList.add(node2);
}
}
/** get first node to check, no parameters
*
* @return Node node1
*/
protected Node getCheckedNode1() {
return this.node1;
}
/** get second node to check, no parameters
*
* @return Node node2
*/
protected Node getCheckedNode2() {
return this.node2;
}
/** method to find index for node 1 or node 2, depending on whether it is called with i = 1 or i = 2 (only two values that will do anything in this method as a component can only have 2 nodes)
* @param int i
* @return index1 or index2
* */
protected int findIndex(int i) {
if (i == 1) {
int index1 = 0;
for (Node node : nodeList){
if (node.getId() == node1.getId()){
break;
}
index1++;
}
return index1;
}
else {
int index2 = 0;
for (Node node : nodeList){
if (node.getId() == node2.getId()){
break;
}
index2++;
}
return index2;
}
}
}
Resistor.java
package circuit;
/**
* A resistor class with resistance that is connected to two different nodes.
*
* It contains a resistance value, a current through the resistor (to be added) which will correspond to voltage drop across the resistor.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
*
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class Resistor extends Component{
/*Instance variables.*/
private double resistance;
protected static int resnum = 1;
/* functionality will be added later */
private double current_through;
/**Constructor that checks that resistor is non-zero and non-negative, sets resistance and attaches nodes
* @param res.
* @param nod1
* @param nod2*/
protected Resistor(double res, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Ensure resistor is greater than 0, and not negative.*/
if (res <= threshold){
throw new IllegalArgumentException("Resistance must be greater than 0.");
}
/*Ensure the specified nodes exist for the resistor.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching resistor.");
}
/*Create the resistor variables.*/
this.resistance = res;
this.setId(Resistor.resnum);
Resistor.resnum++;
this.current_through=0.0;
}
/*Methods.*/
/** get the resistance, no parameters
*
* @return double resistance
*/
protected double getR() {
return this.resistance;
}
/** functionality will be added later, sets current through this resistor, no return
* @param double i_c
* */
protected void set_current(double i_c){
this.current_through = i_c;
}
/** functionality will be added later, gets current through this resistor, no parameters
*
* @return double current_through
*/
protected double get_current(){
return this.current_through;
}
/**Return the information of the resistor
* @return String.*/
@Override
public String toString(){
return "R"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
/** method to get specific information about a resistor, a toString() that does not display the resistor Id, used when not wanting to display the id, no parameters
* @return String
* */
public String toStringFinal() {
return "Req "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
}
UserMain.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/**
* Main function that creates a circuit, and takes input from user to add resistors or voltage sources to the circuit, and display components within the circuit.
*
* Plan to add functionality that will check that the user has input a complete circuit (complete path from node 0 to node 0 through the circuit)
*
* Plan to add functionality that would allow inductors and capacitors to the circuit, likely using rectangular form complex calculations, and possibly phasors.
*
* Plan to add functionality that will allow voltage sources to be placed anywhere within the circuit.
*
* Plan to add functionality that will calculate voltages at each node and current leaving each node.
*
* Plan to add functionality to process Y-Delta transformations for resistors that can't be serial or parallel calculated.
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class UserMain {
/*Instance variables.*/
/* Need to take input from user */
protected static Scanner user;
/* Need dynamic node list to ensure only one node exists per node id */
protected static ArrayList<Node> nodeList;
/** Constructor to initialize instance variables, no parameters */
protected UserMain(){
UserMain.user = new Scanner(System.in);
UserMain.nodeList = new ArrayList<>();
}
/**Main method that interacts with user
* @param args.
* */
public static void main(String args){
/* Create objects in main */
Circuit cir = Circuit.getInstance();
@SuppressWarnings("unused")
UserMain instance = new UserMain();
/*Instruct user on how to use program.*/
System.out.println("Welcome to the circuit builder program.");
System.out.println("Input 'add' to add components into the circuit.");
System.out.println("Input 'edit' to remove components from the circuit.");
System.out.println("Input 'display' to display components currently in the circuit.");
System.out.println("Input 'calculate' to determine total resistance and current in circuit.");
System.out.println("Input 'end' to end the program.");
System.out.println("");
System.out.println("Input resistors (R) and voltage sources (V) into the circuit by the following syntax:");
System.out.println("R/V X Y Z");
System.out.println("R indicates a resistor and V indicates a voltage source.");
System.out.println("X is an integer indicating the first node attached to component.");
System.out.println("Y is an integer indicating the second node attached to component.");
System.out.println("Z a double indicating the resistance in Ohms or Voltage in volts.");
System.out.println("For example: R 1 2 10 will add a resistor connected to nodes 1 and 2 with a resistance of 10 Ohms.");
System.out.println("");
System.out.println("Rules:");
System.out.println("Voltage/Resistor values must be non-zero and Resistor values must also be non-negative. Voltage polarity is directed to increasing node Id.");
System.out.println("Calculation function will assume that nodes are ordered and sequential from 0 to N-1 where N is the total number of nodes.");
System.out.println("Voltage sources cannot be placed in parallel with eachother.");
System.out.println("");
System.out.println("V2.303 Notes:");
System.out.println("Resistors must be connected serially or in parallel. This program does not currently support connections that are neither.");
System.out.println("Currently the program only supports purely directly serial voltage sources, one of which must be between nodes 0 and 1.");
System.out.println("Voltages may not be connected in parallel with resistors.");
System.out.println("Currently it is the user's responsibility to enter a complete circuit.");
System.out.println("");
/* Request user input with input verification */
String input = null;
while(true) {
try {
/* test inputs */
input = UserMain.user.nextLine();
if(input.equals("add")) {
break;
}
if(input.equals("edit")) {
break;
}
if(input.equals("display")) {
break;
}
if(input.equals("calculate")) {
break;
}
if(input.equals("end")) {
break;
}
/* if not a viable input, allow user to retry */
throw new IllegalArgumentException("Enter a valid input.");
} catch (Exception e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Retry:");
}
}
/* While the program is not requested to end */
while (!"end".equals(input)){
/* if adding a component */
if ("add".equals(input)) {
/* request details with input verification */
System.out.println("Add a resistor or a voltage.");
input = UserMain.user.nextLine();
while(true){
try {
String testCase = input.split(" ");
/* if not the proper number of data entities in order to test further down */
if(testCase.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R/V X Y Z.");
}
/* otherwise allow program to proceed */
break;
} catch (IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Try again:");
input = UserMain.user.nextLine();
}
}
/* If resistor is being added */
if ((input.charAt(0) == 'r'||input.charAt(0) == 'R')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double rVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
rVal = Double.parseDouble(inputSplit[3]);
/* if not a resistor entered */
if (!testLetter.equals("r")) {
if(!testLetter.equals("R")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a resistor.");
}
}
/* no negative resistances - testing against a double so do not test against exactly 0 due to imprecision in floating point numbers */
if(rVal < 0.00001){
throw new IllegalArgumentException("You enterred a resistance of "+rVal+". Resistances must be positive and non-zero.");
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create add resistor to circuit.*/
Resistor res = new Resistor(rVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(res);
/* track connections through nodes */
nodeList.get(index1).connect(res);
nodeList.get(index2).connect(res);
System.out.println("Added Resistor: "+res.toString());
}
/* If voltage source is being added */
else if ((input.charAt(0) == 'v'||input.charAt(0) == 'V')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double vVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
vVal = Double.parseDouble(inputSplit[3]);
/* if not a voltage entered */
if (!testLetter.equals("v")) {
if(!testLetter.equals("V")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a voltage.");
}
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create and add voltage source to circuit.*/
Voltage vol = new Voltage(vVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(vol);
/* track connections through nodes */
nodeList.get(index1).connect(vol);
nodeList.get(index2).connect(vol);
System.out.println("Voltage added: "+vol.toString());
}
/* catch other bad inputs */
else {
System.out.println("Invalid input. Enter a voltage source or resistor with the following syntax R/V X Y Z. Try again:");
input = UserMain.user.nextLine();
}
}
/* option to remove components */
else if ("edit".equals(input)){
System.out.println("Which component would you like to remove? Enter only the unique identifier with no spaces (Ex. R1 or V2):");
/* store values */
input = UserMain.user.nextLine();
/* store input */
char question = null;
/* initialize Letter with a dummy value */
char Letter = '';
String Number = "";
/* test user input */
while(true) {
try {
/* store each character separately */
question = input.toCharArray();
/* if the first character entered is not in fact a character */
if(!Character.isLetter(question[0])) {
/* instruct user on error and to retry */
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
Letter = question[0];
/* find the Id of the requested value */
for (int j = 1; j<question.length;j++){
Number += question[j];
}
/* if not an integer, this will throw a NumberFormatException */
Integer.parseInt(Number);
/* if a voltage or resistor are not selected */
if(Letter!='r') {
if(Letter!='R') {
if(Letter!='v') {
if(Letter!='V') {
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
}
}
}
/* if the Number string does not contain at least one character */
if(Number.length()<1) {
throw new IllegalArgumentException("Must enter the unique Id of the component you wish to remove.");
}
/* if no exceptions are thrown */
break;
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Enter only the Letter (R or V) and the number of the component you wish to remove. Try again:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
}
}
/* if resistor requested */
if (Letter == 'r' || Letter == 'R') {
boolean flag = false;
Resistor Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a resistor */
if(cir.getComponents().get(i) instanceof Resistor){
Check = (Resistor)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a resistor and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
/* stop searching */
flag = true;
break;
}
}
}
if (!flag) {
/* if it was not found*/
System.out.println("Resistor not found.");
}
}
/* if voltage requested */
else if (Letter == 'v' || Letter == 'V') {
boolean flag = false;
Voltage Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a voltage */
if(cir.getComponents().get(i) instanceof Voltage){
Check = (Voltage)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a voltage and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
flag = true;
break;
}
}
}
/* if it was not found*/
if (!flag) {
System.out.println("Voltage not found.");
}
}
/* if bad input */
else System.out.println("Input component not recognized.");
}
/*If 'display' is input - print out the circuit components.*/
else if ("display".equals(input)){
/* if there are components */
if(cir.getComponents().size()>0) {
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
}
/* otherwise - needed to avoid trying to print an empty array */
else {
System.out.println("No Components have been added yet.");
}
}
/* calculate Total Current/Voltage */
else if ("calculate".equals(input)) {
/* if there are components in the circuit */
if(cir.getComponents().size()!=0) {
/* get ground voltage */
System.out.println("");
System.out.println("Where is the ground voltage? Enter the unique node ID number only.");
input = UserMain.user.nextLine();
/* input verification - ground functionality to be added later */
int ground;
while(true) {
try {
ground = Integer.parseInt(input);
break;
} catch (NumberFormatException e) {
System.out.println("Invalid input. Enter only the node ID (an integer value):");
input = UserMain.user.nextLine();
}
}
System.out.println("");
System.out.println("Calculating:");
/*Display ordered components */
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
/* perform the circuit analysis */
CircuitAnalysis Calculate = new CircuitAnalysis(ground, cir.getComponents(), nodeList);
Calculate.analyzeCircuit();
/* clear the old calculate object */
Calculate = null;
/* instruct user to continue altering circuit */
System.out.println("");
System.out.println("You may continue to operate on the circuit. Enter a new input command.");
}
/* if no components in the circuit - needed to avoid trying to operate on an empty circuit (empty array) */
else {
System.out.println("Must have components in circuit before calculating.");
}
}
/* loop back for invalid inputs */
else{
System.out.println("Invalid input. Enter a valid command as specified in the instructions.");
}
/*Request next instruction.*/
input = UserMain.user.nextLine();
}
/* Below shows that if two components are connected to the same node,
* they are in fact connected to exactly the same node (object) and not
* just nodes with the same id. In other words, nodes
* only exist as single objects.*/
/*System.out.println("Printing node list to show no duplicate nodes exist.");
for (Node node : nodeList){
System.out.println(node.toString());
}*/
/*Program end.*/
System.out.println("All Done");
}
}
Voltage.java
package circuit;
/**
* A voltage source class that supplies voltage to the circuit and that is connected to two different nodes.
*
* It contains a voltage value.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Voltage extends Component{
/*Instance variables.*/
private double voltage;
protected static int vnum = 1;
/**Constructor checks that voltage is non-zero, sets voltage and attaches two nodes with consistent polarity
* @param double v.
* @param Node nod1
* @param Node nod2*/
protected Voltage(double v, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Check that voltage is non-zero.*/
if (Math.abs(v) <= threshold){
throw new IllegalArgumentException("Voltage must be greater than 0.");
}
/*Check that both nodes exist.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching voltage source.");
}
this.voltage = v;
this.setId(Voltage.vnum);
Voltage.vnum++;
/*Need a consistent directionality in the circuit, defined as in the direction of increasing node numbers.*/
/*For example V 2 1 1.0 is equivalent to V 1 2 -1.0.*/
if (this.nodal1.getId()>this.nodal2.getId()){
Node temp = this.getNode1();
this.nodal1 = this.nodal2;
this.nodal2 = temp;
this.voltage = -this.voltage;
}
}
/*Methods.*/
/** method to get voltage of voltage source, no parameters
*
* @return
*/
protected double getV() {
return this.voltage;
}
/** method to set voltage of voltage source, no return
*
* @param double v
*/
protected void setV(double v) {
this.voltage = v;
}
/**Print information about voltage source, overrides toString()
* @return String.*/
@Override
public String toString(){
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
/** required override for resistor subclass, but not needed for voltage sources, so simply mimics toString() */
public String toStringFinal() {
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
}
java
New contributor
$endgroup$
add a comment |
$begingroup$
I am somewhat new to programming. I've written a program that takes user input accepting voltages and resistors, and calculating total resistance/current/voltage in the circuit. I am looking for ways to improve my coding, whether it be improving its time/space complexity or making better comments to make it more maintainable and scalable (as I have plans to expand it to accept more circuit component types) or implementing more efficient algorithms. You can find the files on GitHub.
There are some additional files in the GitHub (pictures/README) that also help to explain the program.
Circuit.java
package circuit;
import java.util.ArrayList;
/**
* The circuit to which components can be added to.
*
* An object of this class exists in the program as a singleton object.
*
* It holds the ArrayList of components in the circuit.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Circuit {
/*Creates only one instance of a circuit.*/
private static Circuit instance = null;
protected static Circuit getInstance() {
if (instance == null) {
instance = new Circuit();
}
return instance;
}
/**Instance variable list to contain components.*/
private ArrayList<Component> components;
/**Private constructor ensures only one instance of circuit can be created.*/
private Circuit() {
this.components = new ArrayList<>();
}
/*Methods.*/
/** get method to get list of components
* @param none
* @return ArrayList<Component> components*/
protected ArrayList<Component> getComponents(){
return this.components;
}
/**Add component to circuit
* @param Component c.*/
protected void addComponent(Component c){
this.components.add(c);
}
/**Return information of all components in circuit
* @return String.*/
@Override
public String toString(){
String str="";
/*For each object in array.*/
for(Object obj : components){
/*If it is a resistor.*/
if (obj.getClass() == Resistor.class){
/*Downcast to original object type to use class toString() method.*/
str+= ("Resistor: "+(Resistor)obj).toString()+"n";
}
/*Another form of testing object class, if it is a voltage.*/
if (obj instanceof Voltage){
/*Downcast to original object type to use class toString() method.*/
str+= ("Voltage: "+(Voltage)obj).toString()+"n";
}
}
/*Remove last n produced from the above iteration.*/
str = str.substring(0,str.length()-1);
return str;
}
}
CircuitAnalysis.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
/** Assistant class to calculate circuit characteristics.
*
* Input requires reference ground node and the components in the circuit.
*
* I decided to compartmentalize this part of the program in a separate class to simplify the coding and maintenance, but when considering resource management it would likely just be methods within UserMain.
*
* The resistor reduction algorithm used by this class is to first reduce resistors that are in parallel between the same two nodes to a single equivalent resistor between those nodes, then to reduce any serial resistors
* to a single equivalent resistor between the two outer-nodes which will then create more parallel resistors between the same two nodes, and so on.
*
*
* @author Michael Sinclair.
* @version 2.304
* @since 29 January 2019.
*/
public class CircuitAnalysis {
/* instance variables */
private ArrayList<Node> nodeList;
private ArrayList<Component> components;
private int ground;
/* to calculate characteristics */
private double totalV;
private double totalR;
private int voltageSources;
/* to rewind resistor Id count for user to continue after calculations */
private int countResistors;
/** Only constructor for this class
*
* @param int ground
* @param ArrayList<Component> comps
* @param ArrayList<Node> nodes
*/
protected CircuitAnalysis(int ground, ArrayList<Component> comps, ArrayList<Node> nodes) {
/* initialize variables */
this.ground = ground;
this.totalR = 0.0;
this.totalV = 0.0;
this.voltageSources = 0;
countResistors = 0;
/* copy the ArrayLists so that the User can continue operations after calculation, and to enable node specific calculations based on original list */
this.components = new ArrayList<>(comps);
/* have to create new Node objects to avoid altering the input list - basically dereferencing the node objects from the input and creating clone objects of them with t he same id */
this.nodeList = new ArrayList<>();
for(Node node:nodes) {
nodeList.add(new Node(node.getId()));
}
/* now point copied components to the copied nodeList, and attach the copied components to the copied nodes */
for (Component comp:this.components) {
for(Node node:this.nodeList) {
/* if the component points to this iteration's node */
if(comp.getNode1().getId()==node.getId()) {
/* point it to the new copy object in this class nodeList */
comp.setNode1(node);
/* and connect it to the attached copy node */
node.connect(comp);
}
/* same for second node */
if(comp.getNode2().getId()==node.getId()) {
comp.setNode2(node);
node.connect(comp);
}
}
}
/* sort the resistor nodes of the copies, maintain the ordering for the original list that the user input for their resistors */
/* sort the ArrayList of components by node 1 and node 2 (smaller of both first) - note that by construction, voltages will always have ordered nodes */
for (int j = 0; j<components.size();j++) {
for (int i = 0; i<components.size()-1;i++) {
if (components.get(i).compare(components.get(i+1))>0) {
/* if component nodes are disordered, swap them */
Collections.swap(components, i, i+1);
}
}
}
}
/* methods */
/** No parameters or returns, automates circuit measurements by calling analyzeVoltage(),analyzeResistance(),printCharactersitics() and reduces resistor count for resistors created for circuit calculations */
protected void analyzeCircuit() {
/* find total voltage and count voltage sources */
this.analyzeVoltage();
/* find total resistance of the circuit */
this.analyzeResistance();
System.out.println("");
/* print out calculated circuit characteristics */
this.printCharacteristics();
/* rewind resistor count for user to continue altering circuit */
/* if for some (unknown) reason, the user did not enter any resistors do not alter the resistor count */
if(countResistors == 0) {
return;
}
/* for each resistor added, lower the global resistor id number to sync the number back with the user's circuit */
for(int i=0;i<countResistors;i++) {
Resistor.resnum--;
}
}
/**No parameters or returns, finds total voltage in the circuit - note that this program can currently only handle directly serial voltage (connected in series to each other) */
protected void analyzeVoltage() {
/* for each component */
for (int i = 0; i<components.size();i++) {
/* if it is a voltage */
if (components.get(i).getClass() == Voltage.class) {
/* get the voltage */
this.totalV+=((Voltage)(components.get(i))).getV();
/* count voltage sources */
this.voltageSources++;
}
}
}
/**No parameters or returns, finds total resistance in the circuit */
protected void analyzeResistance() {
/* while more than 1 resistor exists */
while(components.size()>this.voltageSources+1) {
/* reduce parallel resistors across the same nodes to one resistor */
this.analyzeParallelSameNode();
/* reduce serial resistors individually in the circuit */
this.analyzeSeriesIndividually();
}
/* now that there is only one resistor in the circuit iterate through the circuit */
for (int i = 0; i<components.size();i++) {
/* if it is a resistor */
if (components.get(i) instanceof Resistor) {
/* get the resistance - note this only executes once */
this.totalR+=((Resistor)components.get(i)).getR();
}
}
}
/**No parameters or returns, reduces same-node parallel resistors to a single equivalent resistor */
protected void analyzeParallelSameNode() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
ArrayList<Component> toConnect = new ArrayList<>();
/* for each node */
/* TODO explore possibility that only one for loop is needed for the nodeList - and simply compare second node */
for (int n = 0; n<nodeList.size();n++) {
/* find components connected to each other node */
for (int m = 0; m<nodeList.size();m++) {
/* components cannot have the same node on both sides & don't want to do the same two nodes twice */
if (n!=m && n<m) {
/* for each component */
for (int k = 0;k<components.size();k++) {
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* if it is a resistor */
if (components.get(k).getClass() == Resistor.class) {
/* if it is between the two nodes */
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* add it to temporary list */
temp.add(components.get(k));
}
}
}
}
/* if a parallel connection was found between node n and m*/
if(temp.size()>1) {
/* create equivalent parallel resistor */
Resistor equivalent = new Resistor(this.parallelResistors(temp),this.findNode(n),this.findNode(m));
/* for rewinding resistor id */
this.countResistors++;
/* queue it for connection */
toConnect.add(equivalent);
/* queue resistors that need to be removed */
for(Component remove:temp) {
toRemove.add(remove);
}
}
/* clear the list for future calculations */
temp.clear();
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
/* attach equivalent resistors */
for(Component comp:toConnect) {
components.add(comp);
comp.getNode1().connect(comp);
comp.getNode2().connect(comp);
}
}
/* No parameters or returns, reduces any two serially connected resistors individually */
protected void analyzeSeriesIndividually() {
ArrayList<Component> toAdd = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
Node firstNode = null;
Node secondNode = null;
/* can only perform this operation a single time before calling it again - resulted in errors created floating resistors that could not be reduced further otherwise */
boolean doOnce = false;
/* for each node */
for(Node node:nodeList) {
/* if there are 2 attachments that are both resistors */
if (node.getAttachments().size()==2 && node.getAttachments().get(0) instanceof Resistor && node.getAttachments().get(1) instanceof Resistor && !doOnce) {
/* find first and second node by Id - one must have a first node prior to the current node being tested and one must have a node after */
if(node.getAttachments().get(0).getNode1().getId()<node.getAttachments().get(1).getNode1().getId()) {
firstNode = node.getAttachments().get(0).getNode1();
secondNode = node.getAttachments().get(1).getNode2();
}
else {
firstNode = node.getAttachments().get(1).getNode1();
secondNode = node.getAttachments().get(0).getNode2();
}
/* if not already queued for removal */
if(!toRemove.contains(node.getAttachments().get(0))) {
if(!toRemove.contains(node.getAttachments().get(1))) {
toRemove.add(node.getAttachments().get(0));
toRemove.add(node.getAttachments().get(1));
toAdd.add(new Resistor(((Resistor)node.getAttachments().get(0)).getR()+((Resistor)node.getAttachments().get(1)).getR(),firstNode,secondNode));
/* for rewinding resistor id */
this.countResistors++;
}
}
/* prevent program from combining more than two serial resistors at the same time */
doOnce = true;
}
}
/* combine serial resistors individually - first remove them from the circuit */
for(Component remove:toRemove) {
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(remove);
}
/* then add the equivalent resistors */
for(Component addR:toAdd) {
addR.getNode1().connect(addR);
addR.getNode2().connect(addR);
components.add(addR);
}
}
/** Find ArrayList index - note ArrayList has built in remove with object parameter but I wanted to use this instead as I was encountering problems with the built in method
* @param ArrayList<Component> findList
* @param Component find
* @return int */
protected int findIndex(ArrayList<Component> findList, Component find) {
int i;
/* iterate through ArrayList until object is found */
for (i = 0;i<findList.size();i++) {
if(findList.contains(find)) {
break;
}
}
return i;
}
/** Determine if resistor already queued for removal, returns true to enable above loop if component is not already queued for removal
* @param Component resistor
* @param ArrayList<Component> toRemove
* @return boolean*/
protected boolean queuedRemoval(Component resistor, ArrayList<Component> toRemove){
/* for each component queued for removal */
for(Component component:toRemove) {
/* if the Id matches any resistor Id in the removal list, and for good measure check that it is a resistor */
if(component.getId()==resistor.getId() && component.getClass()==Resistor.class) {
/* return false to disable the above loop */
return false;
}
}
/* else return true */
return true;
}
/** Find node based on id
* @param int id
* @return Node*/
protected Node findNode(int id) {
/* value to store index */
int i = 0;
/* for each node */
for(Node node:nodeList) {
/* if it does not equal the desired node */
if(node.getId()!=id) {
/* increase the index */
i++;
}
/* if it does */
else {
/* stop searching */
break;
}
}
return nodeList.get(i);
}
/** Calculate parallel resistance from a list of resistors
* @param ArrayList<Component> resistors
* @return double*/
protected double parallelResistors(ArrayList<Component> resistors) {
double parallelR = 0.0;
if(resistors.size()==0) {
throw new IllegalArgumentException("Must input at least one resistor.");
}
for (Component res:resistors) {
/* quick check to make sure only resistances get added to the total */
if(res.getClass()==Resistor.class) {
parallelR+=1/(((Resistor)res).getR());
}
}
return 1/parallelR;
}
/**No parameters or returns, Print circuit Characteristics */
protected void printCharacteristics() {
System.out.println("Ground voltage is located at Node "+this.ground+".");
System.out.println("Total voltage in circuit is: "+this.totalV+ " Volts.");
System.out.println("Total resistance in circuit is: "+this.totalR+" Ohms.");
System.out.println("Total current is: "+this.totalV/this.totalR+" Amps.");
}
/* get methods for testing private instance variables */
/** get nodeList, no parameters
* @return ArrayList<Node>
*/
public ArrayList<Node> getNodeList(){
return this.nodeList;
}
/** gets the list of components, no parameters
*
* @return ArrayList<Component>
*/
public ArrayList<Component> getComponents(){
return this.components;
}
/** get voltage, no parameters
* @return double
*/
public double getV() {
return this.totalV;
}
/** gets the resistance of the circuit, no parameters
*
* @return double
*/
public double getR() {
return this.totalR;
}
/** gets the ground node id
*
* @return int
*/
public int getG() {
return this.ground;
}
/** METHOD NO LONGER IN USE - changed algorithm for solving circuit problem - storing it in case it is useful in future */
/* Reduce multiple serial resistors to a single equivalent resistor */
protected void analyzeSerialResistances() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
int nodalCase = 0;
/* for each node */
for(Node node1:nodeList) {
/* compare to other nodes */
for(Node node2:nodeList) {
/* if not the same node and looking forwards */
if(node1.getId()<node2.getId()) {
/* if both nodes only have 2 attachments */
if(node1.getAttachments().size()==2 && node2.getAttachments().size()==2) {
/* iterate through the attachments that are resistors */
for(int i = 0; i<2; i++) {
for(int j = 0; j<2; j++) {
/* if the components are not already queued for removal */
if(this.queuedRemoval(node1.getAttachments().get(i),toRemove) && this.queuedRemoval(node2.getAttachments().get((j+1)%2),toRemove)) {
/* if a common resistor is found between the nodes and both nodes only have 2 attachments, then the common resistor must be in series with the second nodes attached item */
if (node1.getAttachments().get(i).getId()==node2.getAttachments().get(j).getId() && node1.getAttachments().get(i) instanceof Resistor) {
/* if the second node's other attached item is also a resistor */
if(node2.getAttachments().get((j+1)%2) instanceof Resistor) {
/* queue them for equivalence calculation */
temp.add(node1.getAttachments().get(i));
temp.add(node2.getAttachments().get((j+1)%2));
/* find the common node */
if(temp.get(0).getNode1().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 1;
}
if(temp.get(0).getNode1().getId() == temp.get(1).getNode2().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 2;
}
if(temp.get(0).getNode2().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 3;
}
/* note chose to not use just plain else to verify the last condition is true, even though it is the only possible combination of common nodes left */
if(temp.get(0).getNode2().getId() == temp.get(1).getNode2().getId()) {
nodalCase = 4;
}
}
}
}
}
}
/* if series resistors were found */
if(temp.size()==2) {
/* queue resistors for removal */
toRemove.add(temp.get(0));
toRemove.add(temp.get(1));
Resistor equivalent = null;
/* queue equivalent resistor to be added */
if(nodalCase == 1) {
/* first nodal case - shared 1st/1st node so connect equivalent resistor between both 2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode2());
}
if(nodalCase == 2) {
/* second nodal case - shared 1st/2nd node so connect equivalent resistor between 2nd/1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode1());
}
if(nodalCase == 3) {
/* third nodal case - shared 2nd/1st node so connect equivalent resistor between 1st/2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode2());
}
/* chose not to use simple else for reason stated above */
if(nodalCase == 4) {
/* last nodal case - shared 2nd/2nd node so connect equivalent resistor between both 1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode1());
}
components.add(equivalent);
equivalent.getNode1().connect(equivalent);
equivalent.getNode2().connect(equivalent);
temp.clear();
}
}
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
}
}
Component.java
package circuit;
/**
* An abstract class to quantify common elements to components within the circuit.
*
* Mainly handles component Id features.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public abstract class Component {
/*Instance variables.*/
protected Node nodal1;
protected Node nodal2;
protected int id;
/** Superclass constructor
* @param Node node1
* @param Node node2
*/
protected Component(Node node1, Node node2){
this.nodal1 = node1;
this.nodal2 = node2;
}
/*Methods */
/*get/set methods*/
/** get first node, no parameters
*
* @return Node nodal1
*/
protected Node getNode1() {
return this.nodal1;
}
/** get second node, no parameters
*
* @return Node nodal2
*/
protected Node getNode2() {
return this.nodal2;
}
/** set first node, no return
*
* @param Node node1
*/
protected void setNode1(Node node1) {
this.nodal1 = node1;
}
/** set second node, no return
*
* @param Node node2
*/
protected void setNode2(Node node2) {
this.nodal2 = node2;
}
/** get component id, no parameters
*
* @return int id
*/
protected int getId() {
return this.id;
}
/** set component id, no return
*
* @param int i
*/
protected void setId(int i) {
this.id = i;
}
/** method for testing if connected through only 1 node for use in nodal analysis , returns true if components are connected through the first node and not the second
*
* @param Component other
* @return boolean
* */
protected boolean testNode(Component other) {
if (this.nodal1 == other.nodal1) {
if (this.nodal2 != other.nodal1) {
return true;
}
}
return false;
}
/**Return list of nodes connected to voltage source, no parameters
* @return Node list.
* */
protected Node getNodes(){
return new Node {this.nodal1 , this.nodal2};
}
/** define equals method, returns true if Ids are the same otherwise false
* @param Component other
* @return boolean
* */
protected boolean equals(Component other) {
if (this.getId() == other.getId()) {
return true;
}
else return false;
}
/** define compare method for sorting
* if the first node Id is smaller than the other first node, method returns a negative number and vice versa
* if the first node Id is the same, and the second node is smaller than the other second node, method returns a negative number and vice versa
* if both nodes are equal, method returns 0
* @param Component other
* @return int
* */
protected int compare(Component other) {
if (this.getNode1().getId() == other.getNode1().getId()) {
return this.getNode2().getId()-other.getNode2().getId();
}
else {
return this.getNode1().getId()-other.getNode1().getId();
}
}
/** make components override toString()
* @return String
*/
@Override
public abstract String toString();
/** desire a toString that displays different information
* @return String
* */
public abstract String toStringFinal();
}
Node.java
package circuit;
import java.util.ArrayList;
/**
* A node class, to connect circuit components.
*
* Contains an id that describes the node, as well as the voltage at the node (to be added), the current leaving the node (to be added) and an array list of components attached to the node.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Node {
/*Instance variables*/
private int id;
private double voltageAt;
private ArrayList<Component> attachments;
private double currentLeaving;
/**Assign an id to this node.
* @param nodal_id.*/
protected Node(int nodalId) {
this.id = nodalId;
this.voltageAt = 0.0;
this.attachments = new ArrayList<>();
this.currentLeaving = 0.0;
}
/*Methods*/
/**get id, no parameters
* @return int id
* */
protected int getId(){
return this.id;
}
/** set voltage at Node, no return
* @param double volts
* */
protected void setVoltage(double volts) {
this.voltageAt = volts;
}
/** get voltage at Node, no parameters
* @return double voltageAt
* */
protected double getVoltage() {
return this.voltageAt;
}
/** set current leaving Node, no return
* @param double current
* */
protected void setCurrent(double current) {
this.currentLeaving = current;
}
/** get current leaving Node, no parameters
* @return double currentLeaving
* */
protected double getCurrent() {
return this.currentLeaving;
}
/** connection a component to this node, methods for tracking component connections, no return
* @param Component component
* */
protected void connect(Component component) {
this.attachments.add(component);
}
/** disconnect a component from this node, methods for tracking component connections, no return
*
* @param Component component
*/
protected void disconnect(Component component) {
this.attachments.remove(component);
}
/** get the list of attachments that are attached to this node, no parameters
*
* @return ArrayList<Component> attachments
*/
protected ArrayList<Component> getAttachments(){
return this.attachments;
}
/**Display node id, overrides toString(), no parameters
* @return String.*/
@Override
public String toString() {
return ""+this.id;
}
/** method for displaying specific information about this node, no parameters
*
* @return String
*/
public String toStringSpecific() {
return "Node"+this.id+" Current Leaving: "+this.currentLeaving+" Amps and Voltage at node:" + this.voltageAt+" Volts.";
}
/** method for displaying the attachments connected to this node, mainly used for debugging, no parameters, displays a string version of the list of attachments
*
* @return String
*/
public String toStringAttachments() {
String str = "Node"+this.id;
for(int i=0;i<attachments.size();i++) {
str+=" "+attachments.get(i).toString();
}
return str;
}
}
NodeChecker.java
package circuit;
import java.util.ArrayList;
/** A helper class to simplify UserMain code.
*
* Evaluates whether nodes exist already as a component is added, and if not creates them within UserMain's nodeList ArrayList.
*
* Decided to compartmentalize this portion of the program to remove large duplicate code blocks and simplify UserMain.
*
* Would likely normally just make this a method within UserMain if resources and time were of concern in this program. Chose to make it a separate class to make program easier to view, for now.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class NodeChecker {
private Node node1;
private Node node2;
private ArrayList<Node> nodeList;
/** constructor for building this class
*
* @param Node nod1
* @param Node nod2
* @param ArrayList<Node> nodeList
*/
public NodeChecker(int nod1, int nod2, ArrayList<Node> nodeList){
this.nodeList = nodeList;
/*Check that component does not have the same nodes on both ends. Note would be part of input validation*/
if (nod1 == nod2){
throw new IllegalArgumentException("Nodes must be different for a single component.");
}
this.node1 = new Node(nod1);
this.node2 = new Node(nod2);
/*If these are the first two nodes, add them.*/
if (nodeList.isEmpty()){
nodeList.add(node1);
nodeList.add(node2);
}
int flag1 = 0;
int flag2 = 0;
/*If nodes do not exist, create them.*/
for (Node node : nodeList){
if (node.getId() == node1.getId()){
/*If found set flag and break.*/
flag1 = 1;
break;
}
}
for (Node node : nodeList){
if (node.getId() == node2.getId()){
/*If found set flag and break.*/
flag2 = 1;
break;
}
}
/*If not found.*/
if (flag1 == 0){
nodeList.add(node1);
}
if (flag2 == 0){
nodeList.add(node2);
}
}
/** get first node to check, no parameters
*
* @return Node node1
*/
protected Node getCheckedNode1() {
return this.node1;
}
/** get second node to check, no parameters
*
* @return Node node2
*/
protected Node getCheckedNode2() {
return this.node2;
}
/** method to find index for node 1 or node 2, depending on whether it is called with i = 1 or i = 2 (only two values that will do anything in this method as a component can only have 2 nodes)
* @param int i
* @return index1 or index2
* */
protected int findIndex(int i) {
if (i == 1) {
int index1 = 0;
for (Node node : nodeList){
if (node.getId() == node1.getId()){
break;
}
index1++;
}
return index1;
}
else {
int index2 = 0;
for (Node node : nodeList){
if (node.getId() == node2.getId()){
break;
}
index2++;
}
return index2;
}
}
}
Resistor.java
package circuit;
/**
* A resistor class with resistance that is connected to two different nodes.
*
* It contains a resistance value, a current through the resistor (to be added) which will correspond to voltage drop across the resistor.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
*
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class Resistor extends Component{
/*Instance variables.*/
private double resistance;
protected static int resnum = 1;
/* functionality will be added later */
private double current_through;
/**Constructor that checks that resistor is non-zero and non-negative, sets resistance and attaches nodes
* @param res.
* @param nod1
* @param nod2*/
protected Resistor(double res, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Ensure resistor is greater than 0, and not negative.*/
if (res <= threshold){
throw new IllegalArgumentException("Resistance must be greater than 0.");
}
/*Ensure the specified nodes exist for the resistor.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching resistor.");
}
/*Create the resistor variables.*/
this.resistance = res;
this.setId(Resistor.resnum);
Resistor.resnum++;
this.current_through=0.0;
}
/*Methods.*/
/** get the resistance, no parameters
*
* @return double resistance
*/
protected double getR() {
return this.resistance;
}
/** functionality will be added later, sets current through this resistor, no return
* @param double i_c
* */
protected void set_current(double i_c){
this.current_through = i_c;
}
/** functionality will be added later, gets current through this resistor, no parameters
*
* @return double current_through
*/
protected double get_current(){
return this.current_through;
}
/**Return the information of the resistor
* @return String.*/
@Override
public String toString(){
return "R"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
/** method to get specific information about a resistor, a toString() that does not display the resistor Id, used when not wanting to display the id, no parameters
* @return String
* */
public String toStringFinal() {
return "Req "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
}
UserMain.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/**
* Main function that creates a circuit, and takes input from user to add resistors or voltage sources to the circuit, and display components within the circuit.
*
* Plan to add functionality that will check that the user has input a complete circuit (complete path from node 0 to node 0 through the circuit)
*
* Plan to add functionality that would allow inductors and capacitors to the circuit, likely using rectangular form complex calculations, and possibly phasors.
*
* Plan to add functionality that will allow voltage sources to be placed anywhere within the circuit.
*
* Plan to add functionality that will calculate voltages at each node and current leaving each node.
*
* Plan to add functionality to process Y-Delta transformations for resistors that can't be serial or parallel calculated.
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class UserMain {
/*Instance variables.*/
/* Need to take input from user */
protected static Scanner user;
/* Need dynamic node list to ensure only one node exists per node id */
protected static ArrayList<Node> nodeList;
/** Constructor to initialize instance variables, no parameters */
protected UserMain(){
UserMain.user = new Scanner(System.in);
UserMain.nodeList = new ArrayList<>();
}
/**Main method that interacts with user
* @param args.
* */
public static void main(String args){
/* Create objects in main */
Circuit cir = Circuit.getInstance();
@SuppressWarnings("unused")
UserMain instance = new UserMain();
/*Instruct user on how to use program.*/
System.out.println("Welcome to the circuit builder program.");
System.out.println("Input 'add' to add components into the circuit.");
System.out.println("Input 'edit' to remove components from the circuit.");
System.out.println("Input 'display' to display components currently in the circuit.");
System.out.println("Input 'calculate' to determine total resistance and current in circuit.");
System.out.println("Input 'end' to end the program.");
System.out.println("");
System.out.println("Input resistors (R) and voltage sources (V) into the circuit by the following syntax:");
System.out.println("R/V X Y Z");
System.out.println("R indicates a resistor and V indicates a voltage source.");
System.out.println("X is an integer indicating the first node attached to component.");
System.out.println("Y is an integer indicating the second node attached to component.");
System.out.println("Z a double indicating the resistance in Ohms or Voltage in volts.");
System.out.println("For example: R 1 2 10 will add a resistor connected to nodes 1 and 2 with a resistance of 10 Ohms.");
System.out.println("");
System.out.println("Rules:");
System.out.println("Voltage/Resistor values must be non-zero and Resistor values must also be non-negative. Voltage polarity is directed to increasing node Id.");
System.out.println("Calculation function will assume that nodes are ordered and sequential from 0 to N-1 where N is the total number of nodes.");
System.out.println("Voltage sources cannot be placed in parallel with eachother.");
System.out.println("");
System.out.println("V2.303 Notes:");
System.out.println("Resistors must be connected serially or in parallel. This program does not currently support connections that are neither.");
System.out.println("Currently the program only supports purely directly serial voltage sources, one of which must be between nodes 0 and 1.");
System.out.println("Voltages may not be connected in parallel with resistors.");
System.out.println("Currently it is the user's responsibility to enter a complete circuit.");
System.out.println("");
/* Request user input with input verification */
String input = null;
while(true) {
try {
/* test inputs */
input = UserMain.user.nextLine();
if(input.equals("add")) {
break;
}
if(input.equals("edit")) {
break;
}
if(input.equals("display")) {
break;
}
if(input.equals("calculate")) {
break;
}
if(input.equals("end")) {
break;
}
/* if not a viable input, allow user to retry */
throw new IllegalArgumentException("Enter a valid input.");
} catch (Exception e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Retry:");
}
}
/* While the program is not requested to end */
while (!"end".equals(input)){
/* if adding a component */
if ("add".equals(input)) {
/* request details with input verification */
System.out.println("Add a resistor or a voltage.");
input = UserMain.user.nextLine();
while(true){
try {
String testCase = input.split(" ");
/* if not the proper number of data entities in order to test further down */
if(testCase.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R/V X Y Z.");
}
/* otherwise allow program to proceed */
break;
} catch (IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Try again:");
input = UserMain.user.nextLine();
}
}
/* If resistor is being added */
if ((input.charAt(0) == 'r'||input.charAt(0) == 'R')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double rVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
rVal = Double.parseDouble(inputSplit[3]);
/* if not a resistor entered */
if (!testLetter.equals("r")) {
if(!testLetter.equals("R")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a resistor.");
}
}
/* no negative resistances - testing against a double so do not test against exactly 0 due to imprecision in floating point numbers */
if(rVal < 0.00001){
throw new IllegalArgumentException("You enterred a resistance of "+rVal+". Resistances must be positive and non-zero.");
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create add resistor to circuit.*/
Resistor res = new Resistor(rVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(res);
/* track connections through nodes */
nodeList.get(index1).connect(res);
nodeList.get(index2).connect(res);
System.out.println("Added Resistor: "+res.toString());
}
/* If voltage source is being added */
else if ((input.charAt(0) == 'v'||input.charAt(0) == 'V')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double vVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
vVal = Double.parseDouble(inputSplit[3]);
/* if not a voltage entered */
if (!testLetter.equals("v")) {
if(!testLetter.equals("V")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a voltage.");
}
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create and add voltage source to circuit.*/
Voltage vol = new Voltage(vVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(vol);
/* track connections through nodes */
nodeList.get(index1).connect(vol);
nodeList.get(index2).connect(vol);
System.out.println("Voltage added: "+vol.toString());
}
/* catch other bad inputs */
else {
System.out.println("Invalid input. Enter a voltage source or resistor with the following syntax R/V X Y Z. Try again:");
input = UserMain.user.nextLine();
}
}
/* option to remove components */
else if ("edit".equals(input)){
System.out.println("Which component would you like to remove? Enter only the unique identifier with no spaces (Ex. R1 or V2):");
/* store values */
input = UserMain.user.nextLine();
/* store input */
char question = null;
/* initialize Letter with a dummy value */
char Letter = '';
String Number = "";
/* test user input */
while(true) {
try {
/* store each character separately */
question = input.toCharArray();
/* if the first character entered is not in fact a character */
if(!Character.isLetter(question[0])) {
/* instruct user on error and to retry */
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
Letter = question[0];
/* find the Id of the requested value */
for (int j = 1; j<question.length;j++){
Number += question[j];
}
/* if not an integer, this will throw a NumberFormatException */
Integer.parseInt(Number);
/* if a voltage or resistor are not selected */
if(Letter!='r') {
if(Letter!='R') {
if(Letter!='v') {
if(Letter!='V') {
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
}
}
}
/* if the Number string does not contain at least one character */
if(Number.length()<1) {
throw new IllegalArgumentException("Must enter the unique Id of the component you wish to remove.");
}
/* if no exceptions are thrown */
break;
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Enter only the Letter (R or V) and the number of the component you wish to remove. Try again:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
}
}
/* if resistor requested */
if (Letter == 'r' || Letter == 'R') {
boolean flag = false;
Resistor Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a resistor */
if(cir.getComponents().get(i) instanceof Resistor){
Check = (Resistor)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a resistor and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
/* stop searching */
flag = true;
break;
}
}
}
if (!flag) {
/* if it was not found*/
System.out.println("Resistor not found.");
}
}
/* if voltage requested */
else if (Letter == 'v' || Letter == 'V') {
boolean flag = false;
Voltage Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a voltage */
if(cir.getComponents().get(i) instanceof Voltage){
Check = (Voltage)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a voltage and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
flag = true;
break;
}
}
}
/* if it was not found*/
if (!flag) {
System.out.println("Voltage not found.");
}
}
/* if bad input */
else System.out.println("Input component not recognized.");
}
/*If 'display' is input - print out the circuit components.*/
else if ("display".equals(input)){
/* if there are components */
if(cir.getComponents().size()>0) {
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
}
/* otherwise - needed to avoid trying to print an empty array */
else {
System.out.println("No Components have been added yet.");
}
}
/* calculate Total Current/Voltage */
else if ("calculate".equals(input)) {
/* if there are components in the circuit */
if(cir.getComponents().size()!=0) {
/* get ground voltage */
System.out.println("");
System.out.println("Where is the ground voltage? Enter the unique node ID number only.");
input = UserMain.user.nextLine();
/* input verification - ground functionality to be added later */
int ground;
while(true) {
try {
ground = Integer.parseInt(input);
break;
} catch (NumberFormatException e) {
System.out.println("Invalid input. Enter only the node ID (an integer value):");
input = UserMain.user.nextLine();
}
}
System.out.println("");
System.out.println("Calculating:");
/*Display ordered components */
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
/* perform the circuit analysis */
CircuitAnalysis Calculate = new CircuitAnalysis(ground, cir.getComponents(), nodeList);
Calculate.analyzeCircuit();
/* clear the old calculate object */
Calculate = null;
/* instruct user to continue altering circuit */
System.out.println("");
System.out.println("You may continue to operate on the circuit. Enter a new input command.");
}
/* if no components in the circuit - needed to avoid trying to operate on an empty circuit (empty array) */
else {
System.out.println("Must have components in circuit before calculating.");
}
}
/* loop back for invalid inputs */
else{
System.out.println("Invalid input. Enter a valid command as specified in the instructions.");
}
/*Request next instruction.*/
input = UserMain.user.nextLine();
}
/* Below shows that if two components are connected to the same node,
* they are in fact connected to exactly the same node (object) and not
* just nodes with the same id. In other words, nodes
* only exist as single objects.*/
/*System.out.println("Printing node list to show no duplicate nodes exist.");
for (Node node : nodeList){
System.out.println(node.toString());
}*/
/*Program end.*/
System.out.println("All Done");
}
}
Voltage.java
package circuit;
/**
* A voltage source class that supplies voltage to the circuit and that is connected to two different nodes.
*
* It contains a voltage value.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Voltage extends Component{
/*Instance variables.*/
private double voltage;
protected static int vnum = 1;
/**Constructor checks that voltage is non-zero, sets voltage and attaches two nodes with consistent polarity
* @param double v.
* @param Node nod1
* @param Node nod2*/
protected Voltage(double v, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Check that voltage is non-zero.*/
if (Math.abs(v) <= threshold){
throw new IllegalArgumentException("Voltage must be greater than 0.");
}
/*Check that both nodes exist.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching voltage source.");
}
this.voltage = v;
this.setId(Voltage.vnum);
Voltage.vnum++;
/*Need a consistent directionality in the circuit, defined as in the direction of increasing node numbers.*/
/*For example V 2 1 1.0 is equivalent to V 1 2 -1.0.*/
if (this.nodal1.getId()>this.nodal2.getId()){
Node temp = this.getNode1();
this.nodal1 = this.nodal2;
this.nodal2 = temp;
this.voltage = -this.voltage;
}
}
/*Methods.*/
/** method to get voltage of voltage source, no parameters
*
* @return
*/
protected double getV() {
return this.voltage;
}
/** method to set voltage of voltage source, no return
*
* @param double v
*/
protected void setV(double v) {
this.voltage = v;
}
/**Print information about voltage source, overrides toString()
* @return String.*/
@Override
public String toString(){
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
/** required override for resistor subclass, but not needed for voltage sources, so simply mimics toString() */
public String toStringFinal() {
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
}
java
New contributor
$endgroup$
add a comment |
$begingroup$
I am somewhat new to programming. I've written a program that takes user input accepting voltages and resistors, and calculating total resistance/current/voltage in the circuit. I am looking for ways to improve my coding, whether it be improving its time/space complexity or making better comments to make it more maintainable and scalable (as I have plans to expand it to accept more circuit component types) or implementing more efficient algorithms. You can find the files on GitHub.
There are some additional files in the GitHub (pictures/README) that also help to explain the program.
Circuit.java
package circuit;
import java.util.ArrayList;
/**
* The circuit to which components can be added to.
*
* An object of this class exists in the program as a singleton object.
*
* It holds the ArrayList of components in the circuit.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Circuit {
/*Creates only one instance of a circuit.*/
private static Circuit instance = null;
protected static Circuit getInstance() {
if (instance == null) {
instance = new Circuit();
}
return instance;
}
/**Instance variable list to contain components.*/
private ArrayList<Component> components;
/**Private constructor ensures only one instance of circuit can be created.*/
private Circuit() {
this.components = new ArrayList<>();
}
/*Methods.*/
/** get method to get list of components
* @param none
* @return ArrayList<Component> components*/
protected ArrayList<Component> getComponents(){
return this.components;
}
/**Add component to circuit
* @param Component c.*/
protected void addComponent(Component c){
this.components.add(c);
}
/**Return information of all components in circuit
* @return String.*/
@Override
public String toString(){
String str="";
/*For each object in array.*/
for(Object obj : components){
/*If it is a resistor.*/
if (obj.getClass() == Resistor.class){
/*Downcast to original object type to use class toString() method.*/
str+= ("Resistor: "+(Resistor)obj).toString()+"n";
}
/*Another form of testing object class, if it is a voltage.*/
if (obj instanceof Voltage){
/*Downcast to original object type to use class toString() method.*/
str+= ("Voltage: "+(Voltage)obj).toString()+"n";
}
}
/*Remove last n produced from the above iteration.*/
str = str.substring(0,str.length()-1);
return str;
}
}
CircuitAnalysis.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
/** Assistant class to calculate circuit characteristics.
*
* Input requires reference ground node and the components in the circuit.
*
* I decided to compartmentalize this part of the program in a separate class to simplify the coding and maintenance, but when considering resource management it would likely just be methods within UserMain.
*
* The resistor reduction algorithm used by this class is to first reduce resistors that are in parallel between the same two nodes to a single equivalent resistor between those nodes, then to reduce any serial resistors
* to a single equivalent resistor between the two outer-nodes which will then create more parallel resistors between the same two nodes, and so on.
*
*
* @author Michael Sinclair.
* @version 2.304
* @since 29 January 2019.
*/
public class CircuitAnalysis {
/* instance variables */
private ArrayList<Node> nodeList;
private ArrayList<Component> components;
private int ground;
/* to calculate characteristics */
private double totalV;
private double totalR;
private int voltageSources;
/* to rewind resistor Id count for user to continue after calculations */
private int countResistors;
/** Only constructor for this class
*
* @param int ground
* @param ArrayList<Component> comps
* @param ArrayList<Node> nodes
*/
protected CircuitAnalysis(int ground, ArrayList<Component> comps, ArrayList<Node> nodes) {
/* initialize variables */
this.ground = ground;
this.totalR = 0.0;
this.totalV = 0.0;
this.voltageSources = 0;
countResistors = 0;
/* copy the ArrayLists so that the User can continue operations after calculation, and to enable node specific calculations based on original list */
this.components = new ArrayList<>(comps);
/* have to create new Node objects to avoid altering the input list - basically dereferencing the node objects from the input and creating clone objects of them with t he same id */
this.nodeList = new ArrayList<>();
for(Node node:nodes) {
nodeList.add(new Node(node.getId()));
}
/* now point copied components to the copied nodeList, and attach the copied components to the copied nodes */
for (Component comp:this.components) {
for(Node node:this.nodeList) {
/* if the component points to this iteration's node */
if(comp.getNode1().getId()==node.getId()) {
/* point it to the new copy object in this class nodeList */
comp.setNode1(node);
/* and connect it to the attached copy node */
node.connect(comp);
}
/* same for second node */
if(comp.getNode2().getId()==node.getId()) {
comp.setNode2(node);
node.connect(comp);
}
}
}
/* sort the resistor nodes of the copies, maintain the ordering for the original list that the user input for their resistors */
/* sort the ArrayList of components by node 1 and node 2 (smaller of both first) - note that by construction, voltages will always have ordered nodes */
for (int j = 0; j<components.size();j++) {
for (int i = 0; i<components.size()-1;i++) {
if (components.get(i).compare(components.get(i+1))>0) {
/* if component nodes are disordered, swap them */
Collections.swap(components, i, i+1);
}
}
}
}
/* methods */
/** No parameters or returns, automates circuit measurements by calling analyzeVoltage(),analyzeResistance(),printCharactersitics() and reduces resistor count for resistors created for circuit calculations */
protected void analyzeCircuit() {
/* find total voltage and count voltage sources */
this.analyzeVoltage();
/* find total resistance of the circuit */
this.analyzeResistance();
System.out.println("");
/* print out calculated circuit characteristics */
this.printCharacteristics();
/* rewind resistor count for user to continue altering circuit */
/* if for some (unknown) reason, the user did not enter any resistors do not alter the resistor count */
if(countResistors == 0) {
return;
}
/* for each resistor added, lower the global resistor id number to sync the number back with the user's circuit */
for(int i=0;i<countResistors;i++) {
Resistor.resnum--;
}
}
/**No parameters or returns, finds total voltage in the circuit - note that this program can currently only handle directly serial voltage (connected in series to each other) */
protected void analyzeVoltage() {
/* for each component */
for (int i = 0; i<components.size();i++) {
/* if it is a voltage */
if (components.get(i).getClass() == Voltage.class) {
/* get the voltage */
this.totalV+=((Voltage)(components.get(i))).getV();
/* count voltage sources */
this.voltageSources++;
}
}
}
/**No parameters or returns, finds total resistance in the circuit */
protected void analyzeResistance() {
/* while more than 1 resistor exists */
while(components.size()>this.voltageSources+1) {
/* reduce parallel resistors across the same nodes to one resistor */
this.analyzeParallelSameNode();
/* reduce serial resistors individually in the circuit */
this.analyzeSeriesIndividually();
}
/* now that there is only one resistor in the circuit iterate through the circuit */
for (int i = 0; i<components.size();i++) {
/* if it is a resistor */
if (components.get(i) instanceof Resistor) {
/* get the resistance - note this only executes once */
this.totalR+=((Resistor)components.get(i)).getR();
}
}
}
/**No parameters or returns, reduces same-node parallel resistors to a single equivalent resistor */
protected void analyzeParallelSameNode() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
ArrayList<Component> toConnect = new ArrayList<>();
/* for each node */
/* TODO explore possibility that only one for loop is needed for the nodeList - and simply compare second node */
for (int n = 0; n<nodeList.size();n++) {
/* find components connected to each other node */
for (int m = 0; m<nodeList.size();m++) {
/* components cannot have the same node on both sides & don't want to do the same two nodes twice */
if (n!=m && n<m) {
/* for each component */
for (int k = 0;k<components.size();k++) {
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* if it is a resistor */
if (components.get(k).getClass() == Resistor.class) {
/* if it is between the two nodes */
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* add it to temporary list */
temp.add(components.get(k));
}
}
}
}
/* if a parallel connection was found between node n and m*/
if(temp.size()>1) {
/* create equivalent parallel resistor */
Resistor equivalent = new Resistor(this.parallelResistors(temp),this.findNode(n),this.findNode(m));
/* for rewinding resistor id */
this.countResistors++;
/* queue it for connection */
toConnect.add(equivalent);
/* queue resistors that need to be removed */
for(Component remove:temp) {
toRemove.add(remove);
}
}
/* clear the list for future calculations */
temp.clear();
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
/* attach equivalent resistors */
for(Component comp:toConnect) {
components.add(comp);
comp.getNode1().connect(comp);
comp.getNode2().connect(comp);
}
}
/* No parameters or returns, reduces any two serially connected resistors individually */
protected void analyzeSeriesIndividually() {
ArrayList<Component> toAdd = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
Node firstNode = null;
Node secondNode = null;
/* can only perform this operation a single time before calling it again - resulted in errors created floating resistors that could not be reduced further otherwise */
boolean doOnce = false;
/* for each node */
for(Node node:nodeList) {
/* if there are 2 attachments that are both resistors */
if (node.getAttachments().size()==2 && node.getAttachments().get(0) instanceof Resistor && node.getAttachments().get(1) instanceof Resistor && !doOnce) {
/* find first and second node by Id - one must have a first node prior to the current node being tested and one must have a node after */
if(node.getAttachments().get(0).getNode1().getId()<node.getAttachments().get(1).getNode1().getId()) {
firstNode = node.getAttachments().get(0).getNode1();
secondNode = node.getAttachments().get(1).getNode2();
}
else {
firstNode = node.getAttachments().get(1).getNode1();
secondNode = node.getAttachments().get(0).getNode2();
}
/* if not already queued for removal */
if(!toRemove.contains(node.getAttachments().get(0))) {
if(!toRemove.contains(node.getAttachments().get(1))) {
toRemove.add(node.getAttachments().get(0));
toRemove.add(node.getAttachments().get(1));
toAdd.add(new Resistor(((Resistor)node.getAttachments().get(0)).getR()+((Resistor)node.getAttachments().get(1)).getR(),firstNode,secondNode));
/* for rewinding resistor id */
this.countResistors++;
}
}
/* prevent program from combining more than two serial resistors at the same time */
doOnce = true;
}
}
/* combine serial resistors individually - first remove them from the circuit */
for(Component remove:toRemove) {
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(remove);
}
/* then add the equivalent resistors */
for(Component addR:toAdd) {
addR.getNode1().connect(addR);
addR.getNode2().connect(addR);
components.add(addR);
}
}
/** Find ArrayList index - note ArrayList has built in remove with object parameter but I wanted to use this instead as I was encountering problems with the built in method
* @param ArrayList<Component> findList
* @param Component find
* @return int */
protected int findIndex(ArrayList<Component> findList, Component find) {
int i;
/* iterate through ArrayList until object is found */
for (i = 0;i<findList.size();i++) {
if(findList.contains(find)) {
break;
}
}
return i;
}
/** Determine if resistor already queued for removal, returns true to enable above loop if component is not already queued for removal
* @param Component resistor
* @param ArrayList<Component> toRemove
* @return boolean*/
protected boolean queuedRemoval(Component resistor, ArrayList<Component> toRemove){
/* for each component queued for removal */
for(Component component:toRemove) {
/* if the Id matches any resistor Id in the removal list, and for good measure check that it is a resistor */
if(component.getId()==resistor.getId() && component.getClass()==Resistor.class) {
/* return false to disable the above loop */
return false;
}
}
/* else return true */
return true;
}
/** Find node based on id
* @param int id
* @return Node*/
protected Node findNode(int id) {
/* value to store index */
int i = 0;
/* for each node */
for(Node node:nodeList) {
/* if it does not equal the desired node */
if(node.getId()!=id) {
/* increase the index */
i++;
}
/* if it does */
else {
/* stop searching */
break;
}
}
return nodeList.get(i);
}
/** Calculate parallel resistance from a list of resistors
* @param ArrayList<Component> resistors
* @return double*/
protected double parallelResistors(ArrayList<Component> resistors) {
double parallelR = 0.0;
if(resistors.size()==0) {
throw new IllegalArgumentException("Must input at least one resistor.");
}
for (Component res:resistors) {
/* quick check to make sure only resistances get added to the total */
if(res.getClass()==Resistor.class) {
parallelR+=1/(((Resistor)res).getR());
}
}
return 1/parallelR;
}
/**No parameters or returns, Print circuit Characteristics */
protected void printCharacteristics() {
System.out.println("Ground voltage is located at Node "+this.ground+".");
System.out.println("Total voltage in circuit is: "+this.totalV+ " Volts.");
System.out.println("Total resistance in circuit is: "+this.totalR+" Ohms.");
System.out.println("Total current is: "+this.totalV/this.totalR+" Amps.");
}
/* get methods for testing private instance variables */
/** get nodeList, no parameters
* @return ArrayList<Node>
*/
public ArrayList<Node> getNodeList(){
return this.nodeList;
}
/** gets the list of components, no parameters
*
* @return ArrayList<Component>
*/
public ArrayList<Component> getComponents(){
return this.components;
}
/** get voltage, no parameters
* @return double
*/
public double getV() {
return this.totalV;
}
/** gets the resistance of the circuit, no parameters
*
* @return double
*/
public double getR() {
return this.totalR;
}
/** gets the ground node id
*
* @return int
*/
public int getG() {
return this.ground;
}
/** METHOD NO LONGER IN USE - changed algorithm for solving circuit problem - storing it in case it is useful in future */
/* Reduce multiple serial resistors to a single equivalent resistor */
protected void analyzeSerialResistances() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
int nodalCase = 0;
/* for each node */
for(Node node1:nodeList) {
/* compare to other nodes */
for(Node node2:nodeList) {
/* if not the same node and looking forwards */
if(node1.getId()<node2.getId()) {
/* if both nodes only have 2 attachments */
if(node1.getAttachments().size()==2 && node2.getAttachments().size()==2) {
/* iterate through the attachments that are resistors */
for(int i = 0; i<2; i++) {
for(int j = 0; j<2; j++) {
/* if the components are not already queued for removal */
if(this.queuedRemoval(node1.getAttachments().get(i),toRemove) && this.queuedRemoval(node2.getAttachments().get((j+1)%2),toRemove)) {
/* if a common resistor is found between the nodes and both nodes only have 2 attachments, then the common resistor must be in series with the second nodes attached item */
if (node1.getAttachments().get(i).getId()==node2.getAttachments().get(j).getId() && node1.getAttachments().get(i) instanceof Resistor) {
/* if the second node's other attached item is also a resistor */
if(node2.getAttachments().get((j+1)%2) instanceof Resistor) {
/* queue them for equivalence calculation */
temp.add(node1.getAttachments().get(i));
temp.add(node2.getAttachments().get((j+1)%2));
/* find the common node */
if(temp.get(0).getNode1().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 1;
}
if(temp.get(0).getNode1().getId() == temp.get(1).getNode2().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 2;
}
if(temp.get(0).getNode2().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 3;
}
/* note chose to not use just plain else to verify the last condition is true, even though it is the only possible combination of common nodes left */
if(temp.get(0).getNode2().getId() == temp.get(1).getNode2().getId()) {
nodalCase = 4;
}
}
}
}
}
}
/* if series resistors were found */
if(temp.size()==2) {
/* queue resistors for removal */
toRemove.add(temp.get(0));
toRemove.add(temp.get(1));
Resistor equivalent = null;
/* queue equivalent resistor to be added */
if(nodalCase == 1) {
/* first nodal case - shared 1st/1st node so connect equivalent resistor between both 2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode2());
}
if(nodalCase == 2) {
/* second nodal case - shared 1st/2nd node so connect equivalent resistor between 2nd/1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode1());
}
if(nodalCase == 3) {
/* third nodal case - shared 2nd/1st node so connect equivalent resistor between 1st/2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode2());
}
/* chose not to use simple else for reason stated above */
if(nodalCase == 4) {
/* last nodal case - shared 2nd/2nd node so connect equivalent resistor between both 1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode1());
}
components.add(equivalent);
equivalent.getNode1().connect(equivalent);
equivalent.getNode2().connect(equivalent);
temp.clear();
}
}
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
}
}
Component.java
package circuit;
/**
* An abstract class to quantify common elements to components within the circuit.
*
* Mainly handles component Id features.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public abstract class Component {
/*Instance variables.*/
protected Node nodal1;
protected Node nodal2;
protected int id;
/** Superclass constructor
* @param Node node1
* @param Node node2
*/
protected Component(Node node1, Node node2){
this.nodal1 = node1;
this.nodal2 = node2;
}
/*Methods */
/*get/set methods*/
/** get first node, no parameters
*
* @return Node nodal1
*/
protected Node getNode1() {
return this.nodal1;
}
/** get second node, no parameters
*
* @return Node nodal2
*/
protected Node getNode2() {
return this.nodal2;
}
/** set first node, no return
*
* @param Node node1
*/
protected void setNode1(Node node1) {
this.nodal1 = node1;
}
/** set second node, no return
*
* @param Node node2
*/
protected void setNode2(Node node2) {
this.nodal2 = node2;
}
/** get component id, no parameters
*
* @return int id
*/
protected int getId() {
return this.id;
}
/** set component id, no return
*
* @param int i
*/
protected void setId(int i) {
this.id = i;
}
/** method for testing if connected through only 1 node for use in nodal analysis , returns true if components are connected through the first node and not the second
*
* @param Component other
* @return boolean
* */
protected boolean testNode(Component other) {
if (this.nodal1 == other.nodal1) {
if (this.nodal2 != other.nodal1) {
return true;
}
}
return false;
}
/**Return list of nodes connected to voltage source, no parameters
* @return Node list.
* */
protected Node getNodes(){
return new Node {this.nodal1 , this.nodal2};
}
/** define equals method, returns true if Ids are the same otherwise false
* @param Component other
* @return boolean
* */
protected boolean equals(Component other) {
if (this.getId() == other.getId()) {
return true;
}
else return false;
}
/** define compare method for sorting
* if the first node Id is smaller than the other first node, method returns a negative number and vice versa
* if the first node Id is the same, and the second node is smaller than the other second node, method returns a negative number and vice versa
* if both nodes are equal, method returns 0
* @param Component other
* @return int
* */
protected int compare(Component other) {
if (this.getNode1().getId() == other.getNode1().getId()) {
return this.getNode2().getId()-other.getNode2().getId();
}
else {
return this.getNode1().getId()-other.getNode1().getId();
}
}
/** make components override toString()
* @return String
*/
@Override
public abstract String toString();
/** desire a toString that displays different information
* @return String
* */
public abstract String toStringFinal();
}
Node.java
package circuit;
import java.util.ArrayList;
/**
* A node class, to connect circuit components.
*
* Contains an id that describes the node, as well as the voltage at the node (to be added), the current leaving the node (to be added) and an array list of components attached to the node.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Node {
/*Instance variables*/
private int id;
private double voltageAt;
private ArrayList<Component> attachments;
private double currentLeaving;
/**Assign an id to this node.
* @param nodal_id.*/
protected Node(int nodalId) {
this.id = nodalId;
this.voltageAt = 0.0;
this.attachments = new ArrayList<>();
this.currentLeaving = 0.0;
}
/*Methods*/
/**get id, no parameters
* @return int id
* */
protected int getId(){
return this.id;
}
/** set voltage at Node, no return
* @param double volts
* */
protected void setVoltage(double volts) {
this.voltageAt = volts;
}
/** get voltage at Node, no parameters
* @return double voltageAt
* */
protected double getVoltage() {
return this.voltageAt;
}
/** set current leaving Node, no return
* @param double current
* */
protected void setCurrent(double current) {
this.currentLeaving = current;
}
/** get current leaving Node, no parameters
* @return double currentLeaving
* */
protected double getCurrent() {
return this.currentLeaving;
}
/** connection a component to this node, methods for tracking component connections, no return
* @param Component component
* */
protected void connect(Component component) {
this.attachments.add(component);
}
/** disconnect a component from this node, methods for tracking component connections, no return
*
* @param Component component
*/
protected void disconnect(Component component) {
this.attachments.remove(component);
}
/** get the list of attachments that are attached to this node, no parameters
*
* @return ArrayList<Component> attachments
*/
protected ArrayList<Component> getAttachments(){
return this.attachments;
}
/**Display node id, overrides toString(), no parameters
* @return String.*/
@Override
public String toString() {
return ""+this.id;
}
/** method for displaying specific information about this node, no parameters
*
* @return String
*/
public String toStringSpecific() {
return "Node"+this.id+" Current Leaving: "+this.currentLeaving+" Amps and Voltage at node:" + this.voltageAt+" Volts.";
}
/** method for displaying the attachments connected to this node, mainly used for debugging, no parameters, displays a string version of the list of attachments
*
* @return String
*/
public String toStringAttachments() {
String str = "Node"+this.id;
for(int i=0;i<attachments.size();i++) {
str+=" "+attachments.get(i).toString();
}
return str;
}
}
NodeChecker.java
package circuit;
import java.util.ArrayList;
/** A helper class to simplify UserMain code.
*
* Evaluates whether nodes exist already as a component is added, and if not creates them within UserMain's nodeList ArrayList.
*
* Decided to compartmentalize this portion of the program to remove large duplicate code blocks and simplify UserMain.
*
* Would likely normally just make this a method within UserMain if resources and time were of concern in this program. Chose to make it a separate class to make program easier to view, for now.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class NodeChecker {
private Node node1;
private Node node2;
private ArrayList<Node> nodeList;
/** constructor for building this class
*
* @param Node nod1
* @param Node nod2
* @param ArrayList<Node> nodeList
*/
public NodeChecker(int nod1, int nod2, ArrayList<Node> nodeList){
this.nodeList = nodeList;
/*Check that component does not have the same nodes on both ends. Note would be part of input validation*/
if (nod1 == nod2){
throw new IllegalArgumentException("Nodes must be different for a single component.");
}
this.node1 = new Node(nod1);
this.node2 = new Node(nod2);
/*If these are the first two nodes, add them.*/
if (nodeList.isEmpty()){
nodeList.add(node1);
nodeList.add(node2);
}
int flag1 = 0;
int flag2 = 0;
/*If nodes do not exist, create them.*/
for (Node node : nodeList){
if (node.getId() == node1.getId()){
/*If found set flag and break.*/
flag1 = 1;
break;
}
}
for (Node node : nodeList){
if (node.getId() == node2.getId()){
/*If found set flag and break.*/
flag2 = 1;
break;
}
}
/*If not found.*/
if (flag1 == 0){
nodeList.add(node1);
}
if (flag2 == 0){
nodeList.add(node2);
}
}
/** get first node to check, no parameters
*
* @return Node node1
*/
protected Node getCheckedNode1() {
return this.node1;
}
/** get second node to check, no parameters
*
* @return Node node2
*/
protected Node getCheckedNode2() {
return this.node2;
}
/** method to find index for node 1 or node 2, depending on whether it is called with i = 1 or i = 2 (only two values that will do anything in this method as a component can only have 2 nodes)
* @param int i
* @return index1 or index2
* */
protected int findIndex(int i) {
if (i == 1) {
int index1 = 0;
for (Node node : nodeList){
if (node.getId() == node1.getId()){
break;
}
index1++;
}
return index1;
}
else {
int index2 = 0;
for (Node node : nodeList){
if (node.getId() == node2.getId()){
break;
}
index2++;
}
return index2;
}
}
}
Resistor.java
package circuit;
/**
* A resistor class with resistance that is connected to two different nodes.
*
* It contains a resistance value, a current through the resistor (to be added) which will correspond to voltage drop across the resistor.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
*
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class Resistor extends Component{
/*Instance variables.*/
private double resistance;
protected static int resnum = 1;
/* functionality will be added later */
private double current_through;
/**Constructor that checks that resistor is non-zero and non-negative, sets resistance and attaches nodes
* @param res.
* @param nod1
* @param nod2*/
protected Resistor(double res, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Ensure resistor is greater than 0, and not negative.*/
if (res <= threshold){
throw new IllegalArgumentException("Resistance must be greater than 0.");
}
/*Ensure the specified nodes exist for the resistor.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching resistor.");
}
/*Create the resistor variables.*/
this.resistance = res;
this.setId(Resistor.resnum);
Resistor.resnum++;
this.current_through=0.0;
}
/*Methods.*/
/** get the resistance, no parameters
*
* @return double resistance
*/
protected double getR() {
return this.resistance;
}
/** functionality will be added later, sets current through this resistor, no return
* @param double i_c
* */
protected void set_current(double i_c){
this.current_through = i_c;
}
/** functionality will be added later, gets current through this resistor, no parameters
*
* @return double current_through
*/
protected double get_current(){
return this.current_through;
}
/**Return the information of the resistor
* @return String.*/
@Override
public String toString(){
return "R"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
/** method to get specific information about a resistor, a toString() that does not display the resistor Id, used when not wanting to display the id, no parameters
* @return String
* */
public String toStringFinal() {
return "Req "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
}
UserMain.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/**
* Main function that creates a circuit, and takes input from user to add resistors or voltage sources to the circuit, and display components within the circuit.
*
* Plan to add functionality that will check that the user has input a complete circuit (complete path from node 0 to node 0 through the circuit)
*
* Plan to add functionality that would allow inductors and capacitors to the circuit, likely using rectangular form complex calculations, and possibly phasors.
*
* Plan to add functionality that will allow voltage sources to be placed anywhere within the circuit.
*
* Plan to add functionality that will calculate voltages at each node and current leaving each node.
*
* Plan to add functionality to process Y-Delta transformations for resistors that can't be serial or parallel calculated.
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class UserMain {
/*Instance variables.*/
/* Need to take input from user */
protected static Scanner user;
/* Need dynamic node list to ensure only one node exists per node id */
protected static ArrayList<Node> nodeList;
/** Constructor to initialize instance variables, no parameters */
protected UserMain(){
UserMain.user = new Scanner(System.in);
UserMain.nodeList = new ArrayList<>();
}
/**Main method that interacts with user
* @param args.
* */
public static void main(String args){
/* Create objects in main */
Circuit cir = Circuit.getInstance();
@SuppressWarnings("unused")
UserMain instance = new UserMain();
/*Instruct user on how to use program.*/
System.out.println("Welcome to the circuit builder program.");
System.out.println("Input 'add' to add components into the circuit.");
System.out.println("Input 'edit' to remove components from the circuit.");
System.out.println("Input 'display' to display components currently in the circuit.");
System.out.println("Input 'calculate' to determine total resistance and current in circuit.");
System.out.println("Input 'end' to end the program.");
System.out.println("");
System.out.println("Input resistors (R) and voltage sources (V) into the circuit by the following syntax:");
System.out.println("R/V X Y Z");
System.out.println("R indicates a resistor and V indicates a voltage source.");
System.out.println("X is an integer indicating the first node attached to component.");
System.out.println("Y is an integer indicating the second node attached to component.");
System.out.println("Z a double indicating the resistance in Ohms or Voltage in volts.");
System.out.println("For example: R 1 2 10 will add a resistor connected to nodes 1 and 2 with a resistance of 10 Ohms.");
System.out.println("");
System.out.println("Rules:");
System.out.println("Voltage/Resistor values must be non-zero and Resistor values must also be non-negative. Voltage polarity is directed to increasing node Id.");
System.out.println("Calculation function will assume that nodes are ordered and sequential from 0 to N-1 where N is the total number of nodes.");
System.out.println("Voltage sources cannot be placed in parallel with eachother.");
System.out.println("");
System.out.println("V2.303 Notes:");
System.out.println("Resistors must be connected serially or in parallel. This program does not currently support connections that are neither.");
System.out.println("Currently the program only supports purely directly serial voltage sources, one of which must be between nodes 0 and 1.");
System.out.println("Voltages may not be connected in parallel with resistors.");
System.out.println("Currently it is the user's responsibility to enter a complete circuit.");
System.out.println("");
/* Request user input with input verification */
String input = null;
while(true) {
try {
/* test inputs */
input = UserMain.user.nextLine();
if(input.equals("add")) {
break;
}
if(input.equals("edit")) {
break;
}
if(input.equals("display")) {
break;
}
if(input.equals("calculate")) {
break;
}
if(input.equals("end")) {
break;
}
/* if not a viable input, allow user to retry */
throw new IllegalArgumentException("Enter a valid input.");
} catch (Exception e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Retry:");
}
}
/* While the program is not requested to end */
while (!"end".equals(input)){
/* if adding a component */
if ("add".equals(input)) {
/* request details with input verification */
System.out.println("Add a resistor or a voltage.");
input = UserMain.user.nextLine();
while(true){
try {
String testCase = input.split(" ");
/* if not the proper number of data entities in order to test further down */
if(testCase.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R/V X Y Z.");
}
/* otherwise allow program to proceed */
break;
} catch (IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Try again:");
input = UserMain.user.nextLine();
}
}
/* If resistor is being added */
if ((input.charAt(0) == 'r'||input.charAt(0) == 'R')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double rVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
rVal = Double.parseDouble(inputSplit[3]);
/* if not a resistor entered */
if (!testLetter.equals("r")) {
if(!testLetter.equals("R")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a resistor.");
}
}
/* no negative resistances - testing against a double so do not test against exactly 0 due to imprecision in floating point numbers */
if(rVal < 0.00001){
throw new IllegalArgumentException("You enterred a resistance of "+rVal+". Resistances must be positive and non-zero.");
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create add resistor to circuit.*/
Resistor res = new Resistor(rVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(res);
/* track connections through nodes */
nodeList.get(index1).connect(res);
nodeList.get(index2).connect(res);
System.out.println("Added Resistor: "+res.toString());
}
/* If voltage source is being added */
else if ((input.charAt(0) == 'v'||input.charAt(0) == 'V')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double vVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
vVal = Double.parseDouble(inputSplit[3]);
/* if not a voltage entered */
if (!testLetter.equals("v")) {
if(!testLetter.equals("V")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a voltage.");
}
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create and add voltage source to circuit.*/
Voltage vol = new Voltage(vVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(vol);
/* track connections through nodes */
nodeList.get(index1).connect(vol);
nodeList.get(index2).connect(vol);
System.out.println("Voltage added: "+vol.toString());
}
/* catch other bad inputs */
else {
System.out.println("Invalid input. Enter a voltage source or resistor with the following syntax R/V X Y Z. Try again:");
input = UserMain.user.nextLine();
}
}
/* option to remove components */
else if ("edit".equals(input)){
System.out.println("Which component would you like to remove? Enter only the unique identifier with no spaces (Ex. R1 or V2):");
/* store values */
input = UserMain.user.nextLine();
/* store input */
char question = null;
/* initialize Letter with a dummy value */
char Letter = '';
String Number = "";
/* test user input */
while(true) {
try {
/* store each character separately */
question = input.toCharArray();
/* if the first character entered is not in fact a character */
if(!Character.isLetter(question[0])) {
/* instruct user on error and to retry */
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
Letter = question[0];
/* find the Id of the requested value */
for (int j = 1; j<question.length;j++){
Number += question[j];
}
/* if not an integer, this will throw a NumberFormatException */
Integer.parseInt(Number);
/* if a voltage or resistor are not selected */
if(Letter!='r') {
if(Letter!='R') {
if(Letter!='v') {
if(Letter!='V') {
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
}
}
}
/* if the Number string does not contain at least one character */
if(Number.length()<1) {
throw new IllegalArgumentException("Must enter the unique Id of the component you wish to remove.");
}
/* if no exceptions are thrown */
break;
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Enter only the Letter (R or V) and the number of the component you wish to remove. Try again:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
}
}
/* if resistor requested */
if (Letter == 'r' || Letter == 'R') {
boolean flag = false;
Resistor Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a resistor */
if(cir.getComponents().get(i) instanceof Resistor){
Check = (Resistor)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a resistor and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
/* stop searching */
flag = true;
break;
}
}
}
if (!flag) {
/* if it was not found*/
System.out.println("Resistor not found.");
}
}
/* if voltage requested */
else if (Letter == 'v' || Letter == 'V') {
boolean flag = false;
Voltage Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a voltage */
if(cir.getComponents().get(i) instanceof Voltage){
Check = (Voltage)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a voltage and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
flag = true;
break;
}
}
}
/* if it was not found*/
if (!flag) {
System.out.println("Voltage not found.");
}
}
/* if bad input */
else System.out.println("Input component not recognized.");
}
/*If 'display' is input - print out the circuit components.*/
else if ("display".equals(input)){
/* if there are components */
if(cir.getComponents().size()>0) {
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
}
/* otherwise - needed to avoid trying to print an empty array */
else {
System.out.println("No Components have been added yet.");
}
}
/* calculate Total Current/Voltage */
else if ("calculate".equals(input)) {
/* if there are components in the circuit */
if(cir.getComponents().size()!=0) {
/* get ground voltage */
System.out.println("");
System.out.println("Where is the ground voltage? Enter the unique node ID number only.");
input = UserMain.user.nextLine();
/* input verification - ground functionality to be added later */
int ground;
while(true) {
try {
ground = Integer.parseInt(input);
break;
} catch (NumberFormatException e) {
System.out.println("Invalid input. Enter only the node ID (an integer value):");
input = UserMain.user.nextLine();
}
}
System.out.println("");
System.out.println("Calculating:");
/*Display ordered components */
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
/* perform the circuit analysis */
CircuitAnalysis Calculate = new CircuitAnalysis(ground, cir.getComponents(), nodeList);
Calculate.analyzeCircuit();
/* clear the old calculate object */
Calculate = null;
/* instruct user to continue altering circuit */
System.out.println("");
System.out.println("You may continue to operate on the circuit. Enter a new input command.");
}
/* if no components in the circuit - needed to avoid trying to operate on an empty circuit (empty array) */
else {
System.out.println("Must have components in circuit before calculating.");
}
}
/* loop back for invalid inputs */
else{
System.out.println("Invalid input. Enter a valid command as specified in the instructions.");
}
/*Request next instruction.*/
input = UserMain.user.nextLine();
}
/* Below shows that if two components are connected to the same node,
* they are in fact connected to exactly the same node (object) and not
* just nodes with the same id. In other words, nodes
* only exist as single objects.*/
/*System.out.println("Printing node list to show no duplicate nodes exist.");
for (Node node : nodeList){
System.out.println(node.toString());
}*/
/*Program end.*/
System.out.println("All Done");
}
}
Voltage.java
package circuit;
/**
* A voltage source class that supplies voltage to the circuit and that is connected to two different nodes.
*
* It contains a voltage value.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Voltage extends Component{
/*Instance variables.*/
private double voltage;
protected static int vnum = 1;
/**Constructor checks that voltage is non-zero, sets voltage and attaches two nodes with consistent polarity
* @param double v.
* @param Node nod1
* @param Node nod2*/
protected Voltage(double v, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Check that voltage is non-zero.*/
if (Math.abs(v) <= threshold){
throw new IllegalArgumentException("Voltage must be greater than 0.");
}
/*Check that both nodes exist.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching voltage source.");
}
this.voltage = v;
this.setId(Voltage.vnum);
Voltage.vnum++;
/*Need a consistent directionality in the circuit, defined as in the direction of increasing node numbers.*/
/*For example V 2 1 1.0 is equivalent to V 1 2 -1.0.*/
if (this.nodal1.getId()>this.nodal2.getId()){
Node temp = this.getNode1();
this.nodal1 = this.nodal2;
this.nodal2 = temp;
this.voltage = -this.voltage;
}
}
/*Methods.*/
/** method to get voltage of voltage source, no parameters
*
* @return
*/
protected double getV() {
return this.voltage;
}
/** method to set voltage of voltage source, no return
*
* @param double v
*/
protected void setV(double v) {
this.voltage = v;
}
/**Print information about voltage source, overrides toString()
* @return String.*/
@Override
public String toString(){
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
/** required override for resistor subclass, but not needed for voltage sources, so simply mimics toString() */
public String toStringFinal() {
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
}
java
New contributor
$endgroup$
I am somewhat new to programming. I've written a program that takes user input accepting voltages and resistors, and calculating total resistance/current/voltage in the circuit. I am looking for ways to improve my coding, whether it be improving its time/space complexity or making better comments to make it more maintainable and scalable (as I have plans to expand it to accept more circuit component types) or implementing more efficient algorithms. You can find the files on GitHub.
There are some additional files in the GitHub (pictures/README) that also help to explain the program.
Circuit.java
package circuit;
import java.util.ArrayList;
/**
* The circuit to which components can be added to.
*
* An object of this class exists in the program as a singleton object.
*
* It holds the ArrayList of components in the circuit.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Circuit {
/*Creates only one instance of a circuit.*/
private static Circuit instance = null;
protected static Circuit getInstance() {
if (instance == null) {
instance = new Circuit();
}
return instance;
}
/**Instance variable list to contain components.*/
private ArrayList<Component> components;
/**Private constructor ensures only one instance of circuit can be created.*/
private Circuit() {
this.components = new ArrayList<>();
}
/*Methods.*/
/** get method to get list of components
* @param none
* @return ArrayList<Component> components*/
protected ArrayList<Component> getComponents(){
return this.components;
}
/**Add component to circuit
* @param Component c.*/
protected void addComponent(Component c){
this.components.add(c);
}
/**Return information of all components in circuit
* @return String.*/
@Override
public String toString(){
String str="";
/*For each object in array.*/
for(Object obj : components){
/*If it is a resistor.*/
if (obj.getClass() == Resistor.class){
/*Downcast to original object type to use class toString() method.*/
str+= ("Resistor: "+(Resistor)obj).toString()+"n";
}
/*Another form of testing object class, if it is a voltage.*/
if (obj instanceof Voltage){
/*Downcast to original object type to use class toString() method.*/
str+= ("Voltage: "+(Voltage)obj).toString()+"n";
}
}
/*Remove last n produced from the above iteration.*/
str = str.substring(0,str.length()-1);
return str;
}
}
CircuitAnalysis.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
/** Assistant class to calculate circuit characteristics.
*
* Input requires reference ground node and the components in the circuit.
*
* I decided to compartmentalize this part of the program in a separate class to simplify the coding and maintenance, but when considering resource management it would likely just be methods within UserMain.
*
* The resistor reduction algorithm used by this class is to first reduce resistors that are in parallel between the same two nodes to a single equivalent resistor between those nodes, then to reduce any serial resistors
* to a single equivalent resistor between the two outer-nodes which will then create more parallel resistors between the same two nodes, and so on.
*
*
* @author Michael Sinclair.
* @version 2.304
* @since 29 January 2019.
*/
public class CircuitAnalysis {
/* instance variables */
private ArrayList<Node> nodeList;
private ArrayList<Component> components;
private int ground;
/* to calculate characteristics */
private double totalV;
private double totalR;
private int voltageSources;
/* to rewind resistor Id count for user to continue after calculations */
private int countResistors;
/** Only constructor for this class
*
* @param int ground
* @param ArrayList<Component> comps
* @param ArrayList<Node> nodes
*/
protected CircuitAnalysis(int ground, ArrayList<Component> comps, ArrayList<Node> nodes) {
/* initialize variables */
this.ground = ground;
this.totalR = 0.0;
this.totalV = 0.0;
this.voltageSources = 0;
countResistors = 0;
/* copy the ArrayLists so that the User can continue operations after calculation, and to enable node specific calculations based on original list */
this.components = new ArrayList<>(comps);
/* have to create new Node objects to avoid altering the input list - basically dereferencing the node objects from the input and creating clone objects of them with t he same id */
this.nodeList = new ArrayList<>();
for(Node node:nodes) {
nodeList.add(new Node(node.getId()));
}
/* now point copied components to the copied nodeList, and attach the copied components to the copied nodes */
for (Component comp:this.components) {
for(Node node:this.nodeList) {
/* if the component points to this iteration's node */
if(comp.getNode1().getId()==node.getId()) {
/* point it to the new copy object in this class nodeList */
comp.setNode1(node);
/* and connect it to the attached copy node */
node.connect(comp);
}
/* same for second node */
if(comp.getNode2().getId()==node.getId()) {
comp.setNode2(node);
node.connect(comp);
}
}
}
/* sort the resistor nodes of the copies, maintain the ordering for the original list that the user input for their resistors */
/* sort the ArrayList of components by node 1 and node 2 (smaller of both first) - note that by construction, voltages will always have ordered nodes */
for (int j = 0; j<components.size();j++) {
for (int i = 0; i<components.size()-1;i++) {
if (components.get(i).compare(components.get(i+1))>0) {
/* if component nodes are disordered, swap them */
Collections.swap(components, i, i+1);
}
}
}
}
/* methods */
/** No parameters or returns, automates circuit measurements by calling analyzeVoltage(),analyzeResistance(),printCharactersitics() and reduces resistor count for resistors created for circuit calculations */
protected void analyzeCircuit() {
/* find total voltage and count voltage sources */
this.analyzeVoltage();
/* find total resistance of the circuit */
this.analyzeResistance();
System.out.println("");
/* print out calculated circuit characteristics */
this.printCharacteristics();
/* rewind resistor count for user to continue altering circuit */
/* if for some (unknown) reason, the user did not enter any resistors do not alter the resistor count */
if(countResistors == 0) {
return;
}
/* for each resistor added, lower the global resistor id number to sync the number back with the user's circuit */
for(int i=0;i<countResistors;i++) {
Resistor.resnum--;
}
}
/**No parameters or returns, finds total voltage in the circuit - note that this program can currently only handle directly serial voltage (connected in series to each other) */
protected void analyzeVoltage() {
/* for each component */
for (int i = 0; i<components.size();i++) {
/* if it is a voltage */
if (components.get(i).getClass() == Voltage.class) {
/* get the voltage */
this.totalV+=((Voltage)(components.get(i))).getV();
/* count voltage sources */
this.voltageSources++;
}
}
}
/**No parameters or returns, finds total resistance in the circuit */
protected void analyzeResistance() {
/* while more than 1 resistor exists */
while(components.size()>this.voltageSources+1) {
/* reduce parallel resistors across the same nodes to one resistor */
this.analyzeParallelSameNode();
/* reduce serial resistors individually in the circuit */
this.analyzeSeriesIndividually();
}
/* now that there is only one resistor in the circuit iterate through the circuit */
for (int i = 0; i<components.size();i++) {
/* if it is a resistor */
if (components.get(i) instanceof Resistor) {
/* get the resistance - note this only executes once */
this.totalR+=((Resistor)components.get(i)).getR();
}
}
}
/**No parameters or returns, reduces same-node parallel resistors to a single equivalent resistor */
protected void analyzeParallelSameNode() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
ArrayList<Component> toConnect = new ArrayList<>();
/* for each node */
/* TODO explore possibility that only one for loop is needed for the nodeList - and simply compare second node */
for (int n = 0; n<nodeList.size();n++) {
/* find components connected to each other node */
for (int m = 0; m<nodeList.size();m++) {
/* components cannot have the same node on both sides & don't want to do the same two nodes twice */
if (n!=m && n<m) {
/* for each component */
for (int k = 0;k<components.size();k++) {
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* if it is a resistor */
if (components.get(k).getClass() == Resistor.class) {
/* if it is between the two nodes */
if(components.get(k).getNode1().getId() == n && components.get(k).getNode2().getId() == m) {
/* add it to temporary list */
temp.add(components.get(k));
}
}
}
}
/* if a parallel connection was found between node n and m*/
if(temp.size()>1) {
/* create equivalent parallel resistor */
Resistor equivalent = new Resistor(this.parallelResistors(temp),this.findNode(n),this.findNode(m));
/* for rewinding resistor id */
this.countResistors++;
/* queue it for connection */
toConnect.add(equivalent);
/* queue resistors that need to be removed */
for(Component remove:temp) {
toRemove.add(remove);
}
}
/* clear the list for future calculations */
temp.clear();
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
/* attach equivalent resistors */
for(Component comp:toConnect) {
components.add(comp);
comp.getNode1().connect(comp);
comp.getNode2().connect(comp);
}
}
/* No parameters or returns, reduces any two serially connected resistors individually */
protected void analyzeSeriesIndividually() {
ArrayList<Component> toAdd = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
Node firstNode = null;
Node secondNode = null;
/* can only perform this operation a single time before calling it again - resulted in errors created floating resistors that could not be reduced further otherwise */
boolean doOnce = false;
/* for each node */
for(Node node:nodeList) {
/* if there are 2 attachments that are both resistors */
if (node.getAttachments().size()==2 && node.getAttachments().get(0) instanceof Resistor && node.getAttachments().get(1) instanceof Resistor && !doOnce) {
/* find first and second node by Id - one must have a first node prior to the current node being tested and one must have a node after */
if(node.getAttachments().get(0).getNode1().getId()<node.getAttachments().get(1).getNode1().getId()) {
firstNode = node.getAttachments().get(0).getNode1();
secondNode = node.getAttachments().get(1).getNode2();
}
else {
firstNode = node.getAttachments().get(1).getNode1();
secondNode = node.getAttachments().get(0).getNode2();
}
/* if not already queued for removal */
if(!toRemove.contains(node.getAttachments().get(0))) {
if(!toRemove.contains(node.getAttachments().get(1))) {
toRemove.add(node.getAttachments().get(0));
toRemove.add(node.getAttachments().get(1));
toAdd.add(new Resistor(((Resistor)node.getAttachments().get(0)).getR()+((Resistor)node.getAttachments().get(1)).getR(),firstNode,secondNode));
/* for rewinding resistor id */
this.countResistors++;
}
}
/* prevent program from combining more than two serial resistors at the same time */
doOnce = true;
}
}
/* combine serial resistors individually - first remove them from the circuit */
for(Component remove:toRemove) {
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(remove);
}
/* then add the equivalent resistors */
for(Component addR:toAdd) {
addR.getNode1().connect(addR);
addR.getNode2().connect(addR);
components.add(addR);
}
}
/** Find ArrayList index - note ArrayList has built in remove with object parameter but I wanted to use this instead as I was encountering problems with the built in method
* @param ArrayList<Component> findList
* @param Component find
* @return int */
protected int findIndex(ArrayList<Component> findList, Component find) {
int i;
/* iterate through ArrayList until object is found */
for (i = 0;i<findList.size();i++) {
if(findList.contains(find)) {
break;
}
}
return i;
}
/** Determine if resistor already queued for removal, returns true to enable above loop if component is not already queued for removal
* @param Component resistor
* @param ArrayList<Component> toRemove
* @return boolean*/
protected boolean queuedRemoval(Component resistor, ArrayList<Component> toRemove){
/* for each component queued for removal */
for(Component component:toRemove) {
/* if the Id matches any resistor Id in the removal list, and for good measure check that it is a resistor */
if(component.getId()==resistor.getId() && component.getClass()==Resistor.class) {
/* return false to disable the above loop */
return false;
}
}
/* else return true */
return true;
}
/** Find node based on id
* @param int id
* @return Node*/
protected Node findNode(int id) {
/* value to store index */
int i = 0;
/* for each node */
for(Node node:nodeList) {
/* if it does not equal the desired node */
if(node.getId()!=id) {
/* increase the index */
i++;
}
/* if it does */
else {
/* stop searching */
break;
}
}
return nodeList.get(i);
}
/** Calculate parallel resistance from a list of resistors
* @param ArrayList<Component> resistors
* @return double*/
protected double parallelResistors(ArrayList<Component> resistors) {
double parallelR = 0.0;
if(resistors.size()==0) {
throw new IllegalArgumentException("Must input at least one resistor.");
}
for (Component res:resistors) {
/* quick check to make sure only resistances get added to the total */
if(res.getClass()==Resistor.class) {
parallelR+=1/(((Resistor)res).getR());
}
}
return 1/parallelR;
}
/**No parameters or returns, Print circuit Characteristics */
protected void printCharacteristics() {
System.out.println("Ground voltage is located at Node "+this.ground+".");
System.out.println("Total voltage in circuit is: "+this.totalV+ " Volts.");
System.out.println("Total resistance in circuit is: "+this.totalR+" Ohms.");
System.out.println("Total current is: "+this.totalV/this.totalR+" Amps.");
}
/* get methods for testing private instance variables */
/** get nodeList, no parameters
* @return ArrayList<Node>
*/
public ArrayList<Node> getNodeList(){
return this.nodeList;
}
/** gets the list of components, no parameters
*
* @return ArrayList<Component>
*/
public ArrayList<Component> getComponents(){
return this.components;
}
/** get voltage, no parameters
* @return double
*/
public double getV() {
return this.totalV;
}
/** gets the resistance of the circuit, no parameters
*
* @return double
*/
public double getR() {
return this.totalR;
}
/** gets the ground node id
*
* @return int
*/
public int getG() {
return this.ground;
}
/** METHOD NO LONGER IN USE - changed algorithm for solving circuit problem - storing it in case it is useful in future */
/* Reduce multiple serial resistors to a single equivalent resistor */
protected void analyzeSerialResistances() {
ArrayList<Component> temp = new ArrayList<>();
ArrayList<Component> toRemove = new ArrayList<>();
int nodalCase = 0;
/* for each node */
for(Node node1:nodeList) {
/* compare to other nodes */
for(Node node2:nodeList) {
/* if not the same node and looking forwards */
if(node1.getId()<node2.getId()) {
/* if both nodes only have 2 attachments */
if(node1.getAttachments().size()==2 && node2.getAttachments().size()==2) {
/* iterate through the attachments that are resistors */
for(int i = 0; i<2; i++) {
for(int j = 0; j<2; j++) {
/* if the components are not already queued for removal */
if(this.queuedRemoval(node1.getAttachments().get(i),toRemove) && this.queuedRemoval(node2.getAttachments().get((j+1)%2),toRemove)) {
/* if a common resistor is found between the nodes and both nodes only have 2 attachments, then the common resistor must be in series with the second nodes attached item */
if (node1.getAttachments().get(i).getId()==node2.getAttachments().get(j).getId() && node1.getAttachments().get(i) instanceof Resistor) {
/* if the second node's other attached item is also a resistor */
if(node2.getAttachments().get((j+1)%2) instanceof Resistor) {
/* queue them for equivalence calculation */
temp.add(node1.getAttachments().get(i));
temp.add(node2.getAttachments().get((j+1)%2));
/* find the common node */
if(temp.get(0).getNode1().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 1;
}
if(temp.get(0).getNode1().getId() == temp.get(1).getNode2().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 2;
}
if(temp.get(0).getNode2().getId() == temp.get(1).getNode1().getId()) {
/* queue equivalent resistor nodes to be the non-common nodes */
nodalCase = 3;
}
/* note chose to not use just plain else to verify the last condition is true, even though it is the only possible combination of common nodes left */
if(temp.get(0).getNode2().getId() == temp.get(1).getNode2().getId()) {
nodalCase = 4;
}
}
}
}
}
}
/* if series resistors were found */
if(temp.size()==2) {
/* queue resistors for removal */
toRemove.add(temp.get(0));
toRemove.add(temp.get(1));
Resistor equivalent = null;
/* queue equivalent resistor to be added */
if(nodalCase == 1) {
/* first nodal case - shared 1st/1st node so connect equivalent resistor between both 2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode2());
}
if(nodalCase == 2) {
/* second nodal case - shared 1st/2nd node so connect equivalent resistor between 2nd/1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode2(),temp.get(1).getNode1());
}
if(nodalCase == 3) {
/* third nodal case - shared 2nd/1st node so connect equivalent resistor between 1st/2nd nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode2());
}
/* chose not to use simple else for reason stated above */
if(nodalCase == 4) {
/* last nodal case - shared 2nd/2nd node so connect equivalent resistor between both 1st nodes */
equivalent = new Resistor(((Resistor)temp.get(0)).getR()+((Resistor)temp.get(1)).getR(),temp.get(0).getNode1(),temp.get(1).getNode1());
}
components.add(equivalent);
equivalent.getNode1().connect(equivalent);
equivalent.getNode2().connect(equivalent);
temp.clear();
}
}
}
}
}
/* remove resistors to be replaced by single equivalent resistor */
/* if there are items to be removed */
if(toRemove.size()>0) {
/* for each component to be removed */
for (Component remove:toRemove) {
/* for each component */
for(int i = 0; i <components.size();i++) {
/* if the component is a resistor and it is in the list of resistors to be removed */
if(components.get(i).getId()==remove.getId()&&components.get(i) instanceof Resistor) {
/* remove it from components */
remove.getNode1().disconnect(remove);
remove.getNode2().disconnect(remove);
components.remove(i);
/* need to consider that components has shrunk by 1 */
i--;
}
}
}
}
}
}
Component.java
package circuit;
/**
* An abstract class to quantify common elements to components within the circuit.
*
* Mainly handles component Id features.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public abstract class Component {
/*Instance variables.*/
protected Node nodal1;
protected Node nodal2;
protected int id;
/** Superclass constructor
* @param Node node1
* @param Node node2
*/
protected Component(Node node1, Node node2){
this.nodal1 = node1;
this.nodal2 = node2;
}
/*Methods */
/*get/set methods*/
/** get first node, no parameters
*
* @return Node nodal1
*/
protected Node getNode1() {
return this.nodal1;
}
/** get second node, no parameters
*
* @return Node nodal2
*/
protected Node getNode2() {
return this.nodal2;
}
/** set first node, no return
*
* @param Node node1
*/
protected void setNode1(Node node1) {
this.nodal1 = node1;
}
/** set second node, no return
*
* @param Node node2
*/
protected void setNode2(Node node2) {
this.nodal2 = node2;
}
/** get component id, no parameters
*
* @return int id
*/
protected int getId() {
return this.id;
}
/** set component id, no return
*
* @param int i
*/
protected void setId(int i) {
this.id = i;
}
/** method for testing if connected through only 1 node for use in nodal analysis , returns true if components are connected through the first node and not the second
*
* @param Component other
* @return boolean
* */
protected boolean testNode(Component other) {
if (this.nodal1 == other.nodal1) {
if (this.nodal2 != other.nodal1) {
return true;
}
}
return false;
}
/**Return list of nodes connected to voltage source, no parameters
* @return Node list.
* */
protected Node getNodes(){
return new Node {this.nodal1 , this.nodal2};
}
/** define equals method, returns true if Ids are the same otherwise false
* @param Component other
* @return boolean
* */
protected boolean equals(Component other) {
if (this.getId() == other.getId()) {
return true;
}
else return false;
}
/** define compare method for sorting
* if the first node Id is smaller than the other first node, method returns a negative number and vice versa
* if the first node Id is the same, and the second node is smaller than the other second node, method returns a negative number and vice versa
* if both nodes are equal, method returns 0
* @param Component other
* @return int
* */
protected int compare(Component other) {
if (this.getNode1().getId() == other.getNode1().getId()) {
return this.getNode2().getId()-other.getNode2().getId();
}
else {
return this.getNode1().getId()-other.getNode1().getId();
}
}
/** make components override toString()
* @return String
*/
@Override
public abstract String toString();
/** desire a toString that displays different information
* @return String
* */
public abstract String toStringFinal();
}
Node.java
package circuit;
import java.util.ArrayList;
/**
* A node class, to connect circuit components.
*
* Contains an id that describes the node, as well as the voltage at the node (to be added), the current leaving the node (to be added) and an array list of components attached to the node.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Node {
/*Instance variables*/
private int id;
private double voltageAt;
private ArrayList<Component> attachments;
private double currentLeaving;
/**Assign an id to this node.
* @param nodal_id.*/
protected Node(int nodalId) {
this.id = nodalId;
this.voltageAt = 0.0;
this.attachments = new ArrayList<>();
this.currentLeaving = 0.0;
}
/*Methods*/
/**get id, no parameters
* @return int id
* */
protected int getId(){
return this.id;
}
/** set voltage at Node, no return
* @param double volts
* */
protected void setVoltage(double volts) {
this.voltageAt = volts;
}
/** get voltage at Node, no parameters
* @return double voltageAt
* */
protected double getVoltage() {
return this.voltageAt;
}
/** set current leaving Node, no return
* @param double current
* */
protected void setCurrent(double current) {
this.currentLeaving = current;
}
/** get current leaving Node, no parameters
* @return double currentLeaving
* */
protected double getCurrent() {
return this.currentLeaving;
}
/** connection a component to this node, methods for tracking component connections, no return
* @param Component component
* */
protected void connect(Component component) {
this.attachments.add(component);
}
/** disconnect a component from this node, methods for tracking component connections, no return
*
* @param Component component
*/
protected void disconnect(Component component) {
this.attachments.remove(component);
}
/** get the list of attachments that are attached to this node, no parameters
*
* @return ArrayList<Component> attachments
*/
protected ArrayList<Component> getAttachments(){
return this.attachments;
}
/**Display node id, overrides toString(), no parameters
* @return String.*/
@Override
public String toString() {
return ""+this.id;
}
/** method for displaying specific information about this node, no parameters
*
* @return String
*/
public String toStringSpecific() {
return "Node"+this.id+" Current Leaving: "+this.currentLeaving+" Amps and Voltage at node:" + this.voltageAt+" Volts.";
}
/** method for displaying the attachments connected to this node, mainly used for debugging, no parameters, displays a string version of the list of attachments
*
* @return String
*/
public String toStringAttachments() {
String str = "Node"+this.id;
for(int i=0;i<attachments.size();i++) {
str+=" "+attachments.get(i).toString();
}
return str;
}
}
NodeChecker.java
package circuit;
import java.util.ArrayList;
/** A helper class to simplify UserMain code.
*
* Evaluates whether nodes exist already as a component is added, and if not creates them within UserMain's nodeList ArrayList.
*
* Decided to compartmentalize this portion of the program to remove large duplicate code blocks and simplify UserMain.
*
* Would likely normally just make this a method within UserMain if resources and time were of concern in this program. Chose to make it a separate class to make program easier to view, for now.
*
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class NodeChecker {
private Node node1;
private Node node2;
private ArrayList<Node> nodeList;
/** constructor for building this class
*
* @param Node nod1
* @param Node nod2
* @param ArrayList<Node> nodeList
*/
public NodeChecker(int nod1, int nod2, ArrayList<Node> nodeList){
this.nodeList = nodeList;
/*Check that component does not have the same nodes on both ends. Note would be part of input validation*/
if (nod1 == nod2){
throw new IllegalArgumentException("Nodes must be different for a single component.");
}
this.node1 = new Node(nod1);
this.node2 = new Node(nod2);
/*If these are the first two nodes, add them.*/
if (nodeList.isEmpty()){
nodeList.add(node1);
nodeList.add(node2);
}
int flag1 = 0;
int flag2 = 0;
/*If nodes do not exist, create them.*/
for (Node node : nodeList){
if (node.getId() == node1.getId()){
/*If found set flag and break.*/
flag1 = 1;
break;
}
}
for (Node node : nodeList){
if (node.getId() == node2.getId()){
/*If found set flag and break.*/
flag2 = 1;
break;
}
}
/*If not found.*/
if (flag1 == 0){
nodeList.add(node1);
}
if (flag2 == 0){
nodeList.add(node2);
}
}
/** get first node to check, no parameters
*
* @return Node node1
*/
protected Node getCheckedNode1() {
return this.node1;
}
/** get second node to check, no parameters
*
* @return Node node2
*/
protected Node getCheckedNode2() {
return this.node2;
}
/** method to find index for node 1 or node 2, depending on whether it is called with i = 1 or i = 2 (only two values that will do anything in this method as a component can only have 2 nodes)
* @param int i
* @return index1 or index2
* */
protected int findIndex(int i) {
if (i == 1) {
int index1 = 0;
for (Node node : nodeList){
if (node.getId() == node1.getId()){
break;
}
index1++;
}
return index1;
}
else {
int index2 = 0;
for (Node node : nodeList){
if (node.getId() == node2.getId()){
break;
}
index2++;
}
return index2;
}
}
}
Resistor.java
package circuit;
/**
* A resistor class with resistance that is connected to two different nodes.
*
* It contains a resistance value, a current through the resistor (to be added) which will correspond to voltage drop across the resistor.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
*
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class Resistor extends Component{
/*Instance variables.*/
private double resistance;
protected static int resnum = 1;
/* functionality will be added later */
private double current_through;
/**Constructor that checks that resistor is non-zero and non-negative, sets resistance and attaches nodes
* @param res.
* @param nod1
* @param nod2*/
protected Resistor(double res, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Ensure resistor is greater than 0, and not negative.*/
if (res <= threshold){
throw new IllegalArgumentException("Resistance must be greater than 0.");
}
/*Ensure the specified nodes exist for the resistor.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching resistor.");
}
/*Create the resistor variables.*/
this.resistance = res;
this.setId(Resistor.resnum);
Resistor.resnum++;
this.current_through=0.0;
}
/*Methods.*/
/** get the resistance, no parameters
*
* @return double resistance
*/
protected double getR() {
return this.resistance;
}
/** functionality will be added later, sets current through this resistor, no return
* @param double i_c
* */
protected void set_current(double i_c){
this.current_through = i_c;
}
/** functionality will be added later, gets current through this resistor, no parameters
*
* @return double current_through
*/
protected double get_current(){
return this.current_through;
}
/**Return the information of the resistor
* @return String.*/
@Override
public String toString(){
return "R"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
/** method to get specific information about a resistor, a toString() that does not display the resistor Id, used when not wanting to display the id, no parameters
* @return String
* */
public String toStringFinal() {
return "Req "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.resistance+" Ohms";
}
}
UserMain.java
package circuit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/**
* Main function that creates a circuit, and takes input from user to add resistors or voltage sources to the circuit, and display components within the circuit.
*
* Plan to add functionality that will check that the user has input a complete circuit (complete path from node 0 to node 0 through the circuit)
*
* Plan to add functionality that would allow inductors and capacitors to the circuit, likely using rectangular form complex calculations, and possibly phasors.
*
* Plan to add functionality that will allow voltage sources to be placed anywhere within the circuit.
*
* Plan to add functionality that will calculate voltages at each node and current leaving each node.
*
* Plan to add functionality to process Y-Delta transformations for resistors that can't be serial or parallel calculated.
*
* @author Michael Sinclair.
* @version 2.303
* @since 29 January 2019.
*/
public class UserMain {
/*Instance variables.*/
/* Need to take input from user */
protected static Scanner user;
/* Need dynamic node list to ensure only one node exists per node id */
protected static ArrayList<Node> nodeList;
/** Constructor to initialize instance variables, no parameters */
protected UserMain(){
UserMain.user = new Scanner(System.in);
UserMain.nodeList = new ArrayList<>();
}
/**Main method that interacts with user
* @param args.
* */
public static void main(String args){
/* Create objects in main */
Circuit cir = Circuit.getInstance();
@SuppressWarnings("unused")
UserMain instance = new UserMain();
/*Instruct user on how to use program.*/
System.out.println("Welcome to the circuit builder program.");
System.out.println("Input 'add' to add components into the circuit.");
System.out.println("Input 'edit' to remove components from the circuit.");
System.out.println("Input 'display' to display components currently in the circuit.");
System.out.println("Input 'calculate' to determine total resistance and current in circuit.");
System.out.println("Input 'end' to end the program.");
System.out.println("");
System.out.println("Input resistors (R) and voltage sources (V) into the circuit by the following syntax:");
System.out.println("R/V X Y Z");
System.out.println("R indicates a resistor and V indicates a voltage source.");
System.out.println("X is an integer indicating the first node attached to component.");
System.out.println("Y is an integer indicating the second node attached to component.");
System.out.println("Z a double indicating the resistance in Ohms or Voltage in volts.");
System.out.println("For example: R 1 2 10 will add a resistor connected to nodes 1 and 2 with a resistance of 10 Ohms.");
System.out.println("");
System.out.println("Rules:");
System.out.println("Voltage/Resistor values must be non-zero and Resistor values must also be non-negative. Voltage polarity is directed to increasing node Id.");
System.out.println("Calculation function will assume that nodes are ordered and sequential from 0 to N-1 where N is the total number of nodes.");
System.out.println("Voltage sources cannot be placed in parallel with eachother.");
System.out.println("");
System.out.println("V2.303 Notes:");
System.out.println("Resistors must be connected serially or in parallel. This program does not currently support connections that are neither.");
System.out.println("Currently the program only supports purely directly serial voltage sources, one of which must be between nodes 0 and 1.");
System.out.println("Voltages may not be connected in parallel with resistors.");
System.out.println("Currently it is the user's responsibility to enter a complete circuit.");
System.out.println("");
/* Request user input with input verification */
String input = null;
while(true) {
try {
/* test inputs */
input = UserMain.user.nextLine();
if(input.equals("add")) {
break;
}
if(input.equals("edit")) {
break;
}
if(input.equals("display")) {
break;
}
if(input.equals("calculate")) {
break;
}
if(input.equals("end")) {
break;
}
/* if not a viable input, allow user to retry */
throw new IllegalArgumentException("Enter a valid input.");
} catch (Exception e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Retry:");
}
}
/* While the program is not requested to end */
while (!"end".equals(input)){
/* if adding a component */
if ("add".equals(input)) {
/* request details with input verification */
System.out.println("Add a resistor or a voltage.");
input = UserMain.user.nextLine();
while(true){
try {
String testCase = input.split(" ");
/* if not the proper number of data entities in order to test further down */
if(testCase.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R/V X Y Z.");
}
/* otherwise allow program to proceed */
break;
} catch (IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Try again:");
input = UserMain.user.nextLine();
}
}
/* If resistor is being added */
if ((input.charAt(0) == 'r'||input.charAt(0) == 'R')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double rVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
rVal = Double.parseDouble(inputSplit[3]);
/* if not a resistor entered */
if (!testLetter.equals("r")) {
if(!testLetter.equals("R")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a resistor.");
}
}
/* no negative resistances - testing against a double so do not test against exactly 0 due to imprecision in floating point numbers */
if(rVal < 0.00001){
throw new IllegalArgumentException("You enterred a resistance of "+rVal+". Resistances must be positive and non-zero.");
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Resistor syntax is R X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create add resistor to circuit.*/
Resistor res = new Resistor(rVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(res);
/* track connections through nodes */
nodeList.get(index1).connect(res);
nodeList.get(index2).connect(res);
System.out.println("Added Resistor: "+res.toString());
}
/* If voltage source is being added */
else if ((input.charAt(0) == 'v'||input.charAt(0) == 'V')&&input.charAt(1)==' '){
int firstNode = 0;
int secondNode=0;
double vVal=0.0;
/* Split input into various fields with input validation */
while(true) {
try {
String inputSplit = input.split(" ");
/* if not the proper number of data entities */
if(inputSplit.length!=4) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("Input must be R X Y Z.");
}
/* store the data */
String testLetter = inputSplit[0];
firstNode = Integer.parseInt(inputSplit[1]);
secondNode = Integer.parseInt(inputSplit[2]);
vVal = Double.parseDouble(inputSplit[3]);
/* if not a voltage entered */
if (!testLetter.equals("v")) {
if(!testLetter.equals("V")) {
/* throw exception to keep user within loop */
throw new IllegalArgumentException("You must enter a voltage.");
}
}
/* component must be connected to two different nodes */
if(firstNode == secondNode) {
throw new IllegalArgumentException("Components must be connected to two different nodes.");
}
/* only reached if no exceptions are thrown */
break;
/* note could just catch all exceptions since the retry message is the same, but that is bad practice */
} catch (NumberFormatException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
input = UserMain.user.nextLine();
}
}
/* create nodes if they do not already exist*/
NodeChecker evaluate = new NodeChecker(firstNode,secondNode,nodeList);
@SuppressWarnings("unused")
Node node1 = evaluate.getCheckedNode1();
@SuppressWarnings("unused")
Node node2 = evaluate.getCheckedNode2();
/*Find list index now that the node is definitely in the array.*/
int index1 = evaluate.findIndex(1);
int index2 = evaluate.findIndex(2);
/*Create and add voltage source to circuit.*/
Voltage vol = new Voltage(vVal,nodeList.get(index1),nodeList.get(index2));
cir.addComponent(vol);
/* track connections through nodes */
nodeList.get(index1).connect(vol);
nodeList.get(index2).connect(vol);
System.out.println("Voltage added: "+vol.toString());
}
/* catch other bad inputs */
else {
System.out.println("Invalid input. Enter a voltage source or resistor with the following syntax R/V X Y Z. Try again:");
input = UserMain.user.nextLine();
}
}
/* option to remove components */
else if ("edit".equals(input)){
System.out.println("Which component would you like to remove? Enter only the unique identifier with no spaces (Ex. R1 or V2):");
/* store values */
input = UserMain.user.nextLine();
/* store input */
char question = null;
/* initialize Letter with a dummy value */
char Letter = '';
String Number = "";
/* test user input */
while(true) {
try {
/* store each character separately */
question = input.toCharArray();
/* if the first character entered is not in fact a character */
if(!Character.isLetter(question[0])) {
/* instruct user on error and to retry */
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
Letter = question[0];
/* find the Id of the requested value */
for (int j = 1; j<question.length;j++){
Number += question[j];
}
/* if not an integer, this will throw a NumberFormatException */
Integer.parseInt(Number);
/* if a voltage or resistor are not selected */
if(Letter!='r') {
if(Letter!='R') {
if(Letter!='v') {
if(Letter!='V') {
throw new IllegalArgumentException("Select a resistor with 'R' or a voltage with 'V'.");
}
}
}
}
/* if the Number string does not contain at least one character */
if(Number.length()<1) {
throw new IllegalArgumentException("Must enter the unique Id of the component you wish to remove.");
}
/* if no exceptions are thrown */
break;
} catch(IllegalArgumentException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Enter only the Letter (R or V) and the number of the component you wish to remove. Try again:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
} catch (ArrayIndexOutOfBoundsException e) {
/* instruct user on error and to retry */
System.out.println(e);
System.out.println("Invalid input. Voltage syntax is V X Y Z. Input a resistor:");
/* clear the Number string or else previous values will still be held within the string */
Number = "";
input = UserMain.user.nextLine();
}
}
/* if resistor requested */
if (Letter == 'r' || Letter == 'R') {
boolean flag = false;
Resistor Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a resistor */
if(cir.getComponents().get(i) instanceof Resistor){
Check = (Resistor)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a resistor and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
/* stop searching */
flag = true;
break;
}
}
}
if (!flag) {
/* if it was not found*/
System.out.println("Resistor not found.");
}
}
/* if voltage requested */
else if (Letter == 'v' || Letter == 'V') {
boolean flag = false;
Voltage Check=null;
/*check if it is in the list */
for (int i = 0; i <cir.getComponents().size();i++){
/* if that component is a voltage */
if(cir.getComponents().get(i) instanceof Voltage){
Check = (Voltage)cir.getComponents().get(i);
if (Check.getId() == Integer.parseInt(Number)){
/* if it is a voltage and in the list, remove it */
cir.getComponents().get(i).getNode1().disconnect(cir.getComponents().get(i));
cir.getComponents().get(i).getNode2().disconnect(cir.getComponents().get(i));
cir.getComponents().remove(i);
System.out.println("Removed component.");
flag = true;
break;
}
}
}
/* if it was not found*/
if (!flag) {
System.out.println("Voltage not found.");
}
}
/* if bad input */
else System.out.println("Input component not recognized.");
}
/*If 'display' is input - print out the circuit components.*/
else if ("display".equals(input)){
/* if there are components */
if(cir.getComponents().size()>0) {
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
}
/* otherwise - needed to avoid trying to print an empty array */
else {
System.out.println("No Components have been added yet.");
}
}
/* calculate Total Current/Voltage */
else if ("calculate".equals(input)) {
/* if there are components in the circuit */
if(cir.getComponents().size()!=0) {
/* get ground voltage */
System.out.println("");
System.out.println("Where is the ground voltage? Enter the unique node ID number only.");
input = UserMain.user.nextLine();
/* input verification - ground functionality to be added later */
int ground;
while(true) {
try {
ground = Integer.parseInt(input);
break;
} catch (NumberFormatException e) {
System.out.println("Invalid input. Enter only the node ID (an integer value):");
input = UserMain.user.nextLine();
}
}
System.out.println("");
System.out.println("Calculating:");
/*Display ordered components */
System.out.println("");
System.out.println("Components in circuit are:");
System.out.println(cir.toString());
System.out.println("");
/* perform the circuit analysis */
CircuitAnalysis Calculate = new CircuitAnalysis(ground, cir.getComponents(), nodeList);
Calculate.analyzeCircuit();
/* clear the old calculate object */
Calculate = null;
/* instruct user to continue altering circuit */
System.out.println("");
System.out.println("You may continue to operate on the circuit. Enter a new input command.");
}
/* if no components in the circuit - needed to avoid trying to operate on an empty circuit (empty array) */
else {
System.out.println("Must have components in circuit before calculating.");
}
}
/* loop back for invalid inputs */
else{
System.out.println("Invalid input. Enter a valid command as specified in the instructions.");
}
/*Request next instruction.*/
input = UserMain.user.nextLine();
}
/* Below shows that if two components are connected to the same node,
* they are in fact connected to exactly the same node (object) and not
* just nodes with the same id. In other words, nodes
* only exist as single objects.*/
/*System.out.println("Printing node list to show no duplicate nodes exist.");
for (Node node : nodeList){
System.out.println(node.toString());
}*/
/*Program end.*/
System.out.println("All Done");
}
}
Voltage.java
package circuit;
/**
* A voltage source class that supplies voltage to the circuit and that is connected to two different nodes.
*
* It contains a voltage value.
*
* It also contains an inherited Id as well as two connected nodes from the Component class.
*
* @author Michael Sinclair.
* @version 2.302
* @since 27 January 2019.
*/
public class Voltage extends Component{
/*Instance variables.*/
private double voltage;
protected static int vnum = 1;
/**Constructor checks that voltage is non-zero, sets voltage and attaches two nodes with consistent polarity
* @param double v.
* @param Node nod1
* @param Node nod2*/
protected Voltage(double v, Node nod1, Node nod2) {
super(nod1,nod2);
double threshold = 0.00001;
/*Check that voltage is non-zero.*/
if (Math.abs(v) <= threshold){
throw new IllegalArgumentException("Voltage must be greater than 0.");
}
/*Check that both nodes exist.*/
if (nod1 == null || nod2 == null){
throw new IllegalArgumentException("Nodes must both exist before attaching voltage source.");
}
this.voltage = v;
this.setId(Voltage.vnum);
Voltage.vnum++;
/*Need a consistent directionality in the circuit, defined as in the direction of increasing node numbers.*/
/*For example V 2 1 1.0 is equivalent to V 1 2 -1.0.*/
if (this.nodal1.getId()>this.nodal2.getId()){
Node temp = this.getNode1();
this.nodal1 = this.nodal2;
this.nodal2 = temp;
this.voltage = -this.voltage;
}
}
/*Methods.*/
/** method to get voltage of voltage source, no parameters
*
* @return
*/
protected double getV() {
return this.voltage;
}
/** method to set voltage of voltage source, no return
*
* @param double v
*/
protected void setV(double v) {
this.voltage = v;
}
/**Print information about voltage source, overrides toString()
* @return String.*/
@Override
public String toString(){
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
/** required override for resistor subclass, but not needed for voltage sources, so simply mimics toString() */
public String toStringFinal() {
return "V"+this.getId()+" "+this.getNodes()[0]+" "+this.getNodes()[1]+" "+this.voltage+" Volts DC";
}
}
java
java
New contributor
New contributor
edited 10 mins ago
Jamal♦
30.3k11117227
30.3k11117227
New contributor
asked 11 hours ago
Michael SinclairMichael Sinclair
1
1
New contributor
New contributor
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Michael Sinclair is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212762%2fcircuit-builder-that-calculates-resistance-current-voltage%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Michael Sinclair is a new contributor. Be nice, and check out our Code of Conduct.
Michael Sinclair is a new contributor. Be nice, and check out our Code of Conduct.
Michael Sinclair is a new contributor. Be nice, and check out our Code of Conduct.
Michael Sinclair is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212762%2fcircuit-builder-that-calculates-resistance-current-voltage%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown