From ed8824cc3606bbc87645edc8e94a7bce9bbb8f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isa=C3=ADas=20Lima?= Date: Wed, 12 Jan 2022 09:36:03 -0300 Subject: [PATCH 1/3] Processing input file and creating objects in runtime --- main.py | 19 ++++++++- model/circuit.py | 3 ++ model/impedances.py | 48 ++++++++++++++++++--- model/node.py | 8 ++++ model/reader.py | 64 ++++++++++++++++++++++++++++ model/source.py | 16 +++++-- {controllers => model}/validation.py | 6 ++- 7 files changed, 151 insertions(+), 13 deletions(-) create mode 100644 model/reader.py rename {controllers => model}/validation.py (97%) diff --git a/main.py b/main.py index 6e785b3..864d245 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,17 @@ import pandas as pd import numpy as np -from controllers.validation import Validation +from model.validation import Validation +from model.reader import Reader + +from model.impedances import Resistor +from model.impedances import Inductor +from model.impedances import Capacitor + +from model.source import Voltage +from model.source import Current + +from model.node import Node def stop(message): print('Exiting simulation with message: ' + message) @@ -11,6 +21,7 @@ def stop(message): json = 'C:\emtpython\system.json' validator = Validation(json) +reader = Reader(json) # validating components @@ -42,9 +53,13 @@ def stop(message): if not idsChecked[0]: stop(idsChecked[1]) -# proccessing input data +# processing input data # transforming json in objects according to existing models +reader.readNodes() +reader.readCircuits() +reader.readSources() + # ending simulation stop('Simulation finished succesfully!') diff --git a/model/circuit.py b/model/circuit.py index 0101e0e..aa60cc7 100644 --- a/model/circuit.py +++ b/model/circuit.py @@ -20,3 +20,6 @@ def __init__(self, name, id, f_id, t_id): self.t_id = t_id # to node identificator # the get and set methods were left to Python to handle + + def log(self): + print('Circuit with id: ' + self.id + '; name: ' + self.name + '; from: ' + self.f_id + '; to: ' + self.t_id) diff --git a/model/impedances.py b/model/impedances.py index 906811b..ba44b61 100644 --- a/model/impedances.py +++ b/model/impedances.py @@ -4,22 +4,58 @@ An impedance is a circuit which opposes to the flow of current. ''' -from circuit import Circuit +from model.circuit import Circuit class Resistor(Circuit): def __init__(self, name, id, f_id, t_id, resistance): - super().__init__(self, name, id, f_id, t_id) + # data checking + if not isinstance(name, str) or not isinstance(id, str) or not isinstance(f_id, str) or not isinstance(t_id, str): + raise Exception('Parameters name, id, f_id and t_id must be strings ONLY.') + + self.name = name # freely user defined circuit public name + self.id = id # exclusive identificator (handled elsewhere) + self.f_id = f_id # from node identificator + self.t_id = t_id # to node identificator self.resistance = resistance -class Resistor(Circuit): + # method to create an instance from json object + @staticmethod + def json(key, value): + return Resistor(value['name'], key, value['f_id'], value['t_id'], value['resistance']) + +class Inductor(Circuit): def __init__(self, name, id, f_id, t_id, inductance): - super().__init__(self, name, id, f_id, t_id) + # data checking + if not isinstance(name, str) or not isinstance(id, str) or not isinstance(f_id, str) or not isinstance(t_id, str): + raise Exception('Parameters name, id, f_id and t_id must be strings ONLY.') + + self.name = name # freely user defined circuit public name + self.id = id # exclusive identificator (handled elsewhere) + self.f_id = f_id # from node identificator + self.t_id = t_id # to node identificator self.inductance = inductance -class Capacitance(Circuit): + # method to create an instance from json object + @staticmethod + def json(key, value): + return Inductor(value['name'], key, value['f_id'], value['t_id'], value['inductance']) + +class Capacitor(Circuit): def __init__(self, name, id, f_id, t_id, capacitance): - super().__init__(self, name, id, f_id, t_id) + # data checking + if not isinstance(name, str) or not isinstance(id, str) or not isinstance(f_id, str) or not isinstance(t_id, str): + raise Exception('Parameters name, id, f_id and t_id must be strings ONLY.') + + self.name = name # freely user defined circuit public name + self.id = id # exclusive identificator (handled elsewhere) + self.f_id = f_id # from node identificator + self.t_id = t_id # to node identificator self.capacitance = capacitance + + # method to create an instance from json object + @staticmethod + def json(key, value): + return Capacitor(value['name'], key, value['f_id'], value['t_id'], value['capacitance']) diff --git a/model/node.py b/model/node.py index fa060d6..9be57ba 100644 --- a/model/node.py +++ b/model/node.py @@ -16,9 +16,17 @@ def __init__(self, name, id, circuits = []): self.id = id # exclusive identificator (handled elsewhere) self.circuits = circuits # connected circuits identifiers + def log(self): + print('Node with id: ' + self.id + '; name: ' + self.name + '; circuits: ' + str(len(self.circuits))) + # the get and set methods were left for Python to handle # method to create a ground node @staticmethod def ground(): return Node('ground', '0') + + # method to create an instance from json object + @staticmethod + def json(key, value): + return Node(value['name'], key, value['circuits']) diff --git a/model/reader.py b/model/reader.py new file mode 100644 index 0000000..c2aeba8 --- /dev/null +++ b/model/reader.py @@ -0,0 +1,64 @@ +# Input json file reading in programs format + +import json as js + +from model.impedances import Resistor +from model.impedances import Inductor +from model.impedances import Capacitor + +from model.source import Voltage +from model.source import Current + +from model.node import Node + +class Reader: + + # the json file must be valid, the validator does this process + def __init__(self, json): + # the parameter json is the json file path + try: + with open(json, 'r') as f: + self.json = js.load(f) + except Exception as error: + print(error) + self.json = {} + self.data = {} # consolidated dictionary with converted objects + + def readNodes(self): + # reads the components in node + nodes = self.json['nodes'] + self.data['nodes'] = [] + for node in nodes: + self.data['nodes'].append(Node.json(node, nodes[node])) + print('Nodes processed: ') + for node in self.data['nodes']: + node.log() + + def readCircuits(self): + # reads the components in circuits + circuits = self.json['circuits'] + self.data['circuits'] = [] + for circuit in circuits: + if 'resistance' in circuits[circuit]: + self.data['circuits'].append(Resistor.json(circuit, circuits[circuit])) + if 'inductance' in circuits[circuit]: + self.data['circuits'].append(Inductor.json(circuit, circuits[circuit])) + if 'capacitance' in circuits[circuit]: + self.data['circuits'].append(Capacitor.json(circuit, circuits[circuit])) + print('Circuits processed: ') + for circuit in self.data['circuits']: + circuit.log() + + def readSources(self): + # reads the components in sources + frequency = self.json['parameters']['frequency'] + sources = self.json['sources'] + self.data['sources'] = [] + for source in sources: + if 'voltage' in sources[source]: + self.data['sources'].append(Voltage.json(source, sources[source], frequency)) + if 'current' in sources[source]: + self.data['sources'].append(Current.json(source, sources[source], frequency)) + print('Sources processed: ') + for source in self.data['sources']: + source.log() diff --git a/model/source.py b/model/source.py index be81312..088f162 100644 --- a/model/source.py +++ b/model/source.py @@ -5,13 +5,13 @@ difference and a current is able to flow actively. ''' -from circuit import Circuit +from model.circuit import Circuit class Voltage(Circuit): def __init__(self, name, id, f_id, t_id, voltage, angle, frequency): # data checking - if not isinstance(name, str) or not isinstance(id, str) or not isinstance(f_id, str) or not isinstance(t_id, str) or not isinstance(voltage, flt) or not isinstance(angle, flt) or not isinstance(frequency, flt): + if not isinstance(name, str) or not isinstance(id, str) or not isinstance(f_id, str) or not isinstance(t_id, str) or not isinstance(voltage, float) or not isinstance(angle, float) or not isinstance(frequency, float): raise Exception('Parameters name, id, f_id and t_id must be strings ONLY and parameters voltage, angle and frequency must be FLOAT only.') # data definition self.name = name # freely user defined circuit public name @@ -22,11 +22,16 @@ def __init__(self, name, id, f_id, t_id, voltage, angle, frequency): self.angle = angle # nominal angle in degrees self.frequency = frequency # frequency in Hertz + # method to create an instance from json object + @staticmethod + def json(key, value, frequency): + return Voltage(value['name'], key, value['f_id'], value['t_id'], value['voltage'], value['angle'], frequency) + class Current(Circuit): def __init__(self, name, id, f_id, t_id, current, angle, frequency): # data checking - if not isinstance(name, str) or not isinstance(id, str) or not isinstance(f_id, str) or not isinstance(t_id, str) or not isinstance(current, flt) or not isinstance(angle, flt) or not isinstance(frequency, flt): + if not isinstance(name, str) or not isinstance(id, str) or not isinstance(f_id, str) or not isinstance(t_id, str) or not isinstance(current, float) or not isinstance(angle, float) or not isinstance(frequency, float): raise Exception('Parameters name, id, f_id and t_id must be strings ONLY and parameters current, angle and frequency must be FLOAT only.') # data definition self.name = name # freely user defined circuit public name @@ -36,3 +41,8 @@ def __init__(self, name, id, f_id, t_id, current, angle, frequency): self.current = current # nominal PEAK current in Amps self.angle = angle # nominal angle in degrees self.frequency = frequency # frequency in Hertz + + # method to create an instance from json object + @staticmethod + def json(key, value, frequency): + return Current(value['name'], key, value['f_id'], value['t_id'], value['current'], value['angle'], frequency) diff --git a/controllers/validation.py b/model/validation.py similarity index 97% rename from controllers/validation.py rename to model/validation.py index 831321d..48f740c 100644 --- a/controllers/validation.py +++ b/model/validation.py @@ -7,8 +7,8 @@ class Validation: def __init__(self, json): # the parameter json is the json file path try: - f = open(json, 'r') - self.json = js.load(f) + with open(json, 'r') as f: + self.json = js.load(f) except Exception as error: print(error) self.json = {} @@ -38,6 +38,8 @@ def validateNodes(self): return (False, 'The field nodes is missing in the input file...') # nodes list empty or inexistent nodes = self.json['nodes'] for node in nodes: + if node == '0': + return (False, 'There is no need to create the ground node...') if 'name' not in nodes[node] or 'circuits' not in nodes[node]: return (False, 'One of the necessary fields of node is missing for node -> ' + node) # nodes fields missing if not isinstance(nodes[node]['name'], str): From 9ded0d43b0bc4417745faa6e0faaf6c299db5322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isa=C3=ADas=20Lima?= Date: Wed, 12 Jan 2022 10:15:30 -0300 Subject: [PATCH 2/3] System being processed, next step is steady state simulation --- main.py | 3 +++ model/reader.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 864d245..65736b1 100644 --- a/main.py +++ b/main.py @@ -60,6 +60,9 @@ def stop(message): reader.readNodes() reader.readCircuits() reader.readSources() +reader.convertSources() + +reader.log() # ending simulation stop('Simulation finished succesfully!') diff --git a/model/reader.py b/model/reader.py index c2aeba8..7d3d28c 100644 --- a/model/reader.py +++ b/model/reader.py @@ -11,6 +11,8 @@ from model.node import Node +Rth = 1.E-12 + class Reader: # the json file must be valid, the validator does this process @@ -61,4 +63,35 @@ def readSources(self): self.data['sources'].append(Current.json(source, sources[source], frequency)) print('Sources processed: ') for source in self.data['sources']: - source.log() + source.log() + + # preparing sources for simulation, converting all voltage sources into current sources + def convertSources(self): + for i in range(len(self.data['sources'])): + source = self.data['sources'][i] + if str(type(source).__name__) == 'Voltage': + source = Current(source.name, source.id, source.f_id, source.t_id, source.voltage / Rth, source.angle, source.frequency) + thevenin = Resistor('fictional thevenin', 'th <> ' + source.id, source.f_id, source.t_id, Rth) + self.data['circuits'].append(thevenin) + if source.f_id != '0': + for node in self.data['nodes']: + if node.id == source.f_id: + node.circuits.append(thevenin.id) + if source.t_id != '0': + for node in self.data['nodes']: + if node.id == source.t_id: + node.circuits.append(thevenin.id) + self.data['sources'][i] = source + # print(source.current) + print('Sources converted!') + # print(self.data) + + # presenting systems consolidated data + def log(self): + print('\nSystem data: ') + for node in self.data['nodes']: + node.log() + for circuit in self.data['circuits']: + circuit.log() + for source in self.data['sources']: + source.log() From 7fbe5693d6d84350bc607243346c655d970ab036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isa=C3=ADas=20Lima?= Date: Wed, 12 Jan 2022 11:18:45 -0300 Subject: [PATCH 3/3] Steady state solution running --- main.py | 9 ++++--- model/reader.py | 9 +++++-- model/solver.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ model/source.py | 7 +++++ system.json | 8 +++++- 5 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 model/solver.py diff --git a/main.py b/main.py index 65736b1..7d41564 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ from model.validation import Validation from model.reader import Reader +from model.solver import Solver from model.impedances import Resistor from model.impedances import Inductor @@ -24,7 +25,6 @@ def stop(message): reader = Reader(json) # validating components - nameValid = validator.validateName() parametersValid = validator.validateParameters() nodesValid = validator.validateNodes() @@ -47,7 +47,6 @@ def stop(message): stop(sourcesValid[1]) # verifing repeated connections - idsChecked = validator.checkIds() if not idsChecked[0]: @@ -56,7 +55,6 @@ def stop(message): # processing input data # transforming json in objects according to existing models - reader.readNodes() reader.readCircuits() reader.readSources() @@ -64,5 +62,10 @@ def stop(message): reader.log() +# solving the circuit +solver = Solver(reader.data) + +solver.steadyState() + # ending simulation stop('Simulation finished succesfully!') diff --git a/model/reader.py b/model/reader.py index 7d3d28c..0dba943 100644 --- a/model/reader.py +++ b/model/reader.py @@ -11,7 +11,7 @@ from model.node import Node -Rth = 1.E-12 +Rth = 1.E-30 class Reader: @@ -21,10 +21,14 @@ def __init__(self, json): try: with open(json, 'r') as f: self.json = js.load(f) + self.data = { + 'stop': self.json['parameters']['stop'], + 'dT': self.json['parameters']['dT'], + 'frequency': self.json['parameters']['frequency'] + } # consolidated dictionary with converted objects except Exception as error: print(error) self.json = {} - self.data = {} # consolidated dictionary with converted objects def readNodes(self): # reads the components in node @@ -89,6 +93,7 @@ def convertSources(self): # presenting systems consolidated data def log(self): print('\nSystem data: ') + print('Frequency: ' + str(self.data['frequency'])) for node in self.data['nodes']: node.log() for circuit in self.data['circuits']: diff --git a/model/solver.py b/model/solver.py new file mode 100644 index 0000000..adc6d0a --- /dev/null +++ b/model/solver.py @@ -0,0 +1,69 @@ +# this one effectivelly solves the circuit + +import numpy as np +import math + +class Solver: + + def __init__(self, data): + self.data = data + + # steady-state solution + def steadyState(self): + # creating G matrix and Z matrix + G = [] + nodes = self.data['nodes'] + circuits = self.data['circuits'] + for i in range(len(nodes)): + line = [] + for j in range(len(nodes)): + m = nodes[i].id + n = nodes[j].id + # if m == n, Gmn is the sum of the susceptances connected to node m/n + if m == n: + g = 0 + for circuit in circuits: + if circuit.f_id == m or circuit.t_id == m: + if str(type(circuit).__name__) == 'Resistor': + g = g + 1 / circuit.resistance + if str(type(circuit).__name__) == 'Inductor': + g = g + 1 / (1j * 2 * math.pi * self.data['frequency'] * circuit.inductance) + if str(type(circuit).__name__) == 'Capacitor': + g = g + (1j * 2 * math.pi * self.data['frequency'] * circuit.capacitance) + line.append(g) + else: + g = 0 + for circuit in circuits: + if (circuit.f_id == m and circuit.t_id == n) or (circuit.f_id == n and circuit.t_id == m): + if str(type(circuit).__name__) == 'Resistor': + g = g + 1 / circuit.resistance + if str(type(circuit).__name__) == 'Inductor': + g = g + 1 / (1j * 2 * math.pi * self.data['frequency'] * circuit.inductance) + if str(type(circuit).__name__) == 'Capacitor': + g = g + (1j * 2 * math.pi * self.data['frequency'] * circuit.capacitance) + line.append(-g) + G.append(line) + G = np.matrix(G) + Z = G.I + # creating I matrix + I = [] + sources = self.data['sources'] # assuming the conversion to current sources + for i in range(len(nodes)): + m = nodes[i].id + current = 0 + for source in sources: + if source.f_id == m: + current = current + source.phasor() + if source.t_id == m: + current = current - source.phasor() + I.append(current) + I = np.matrix(I) + I = I.transpose() + V = Z*I + voltages = [] + for line in V: + for v in line: + voltage = v[0, 0] + voltage = (math.sqrt(np.real(voltage) ** 2 + np.imag(voltage) ** 2), math.atan(np.imag(voltage) / np.real(voltage)) * 180 / math.pi) + voltages.append(voltage) + print(voltages) diff --git a/model/source.py b/model/source.py index 088f162..428c09a 100644 --- a/model/source.py +++ b/model/source.py @@ -6,6 +6,7 @@ ''' from model.circuit import Circuit +import math class Voltage(Circuit): @@ -22,6 +23,9 @@ def __init__(self, name, id, f_id, t_id, voltage, angle, frequency): self.angle = angle # nominal angle in degrees self.frequency = frequency # frequency in Hertz + def phasor(self): + return self.voltage * math.cos(self.angle * math.pi / 180.) + 1j * self.voltage * math.sin(self.angle * math.pi / 180.) + # method to create an instance from json object @staticmethod def json(key, value, frequency): @@ -42,6 +46,9 @@ def __init__(self, name, id, f_id, t_id, current, angle, frequency): self.angle = angle # nominal angle in degrees self.frequency = frequency # frequency in Hertz + def phasor(self): + return self.current * math.cos(self.angle * math.pi / 180.) + 1j * self.current * math.sin(self.angle * math.pi / 180.) + # method to create an instance from json object @staticmethod def json(key, value, frequency): diff --git a/system.json b/system.json index fb8b6e6..6248765 100644 --- a/system.json +++ b/system.json @@ -3,7 +3,7 @@ "parameters": { "stop": 10.0, "dT": 0.00001, - "frequency": 60.0 + "frequency": 1.0 }, "nodes": { "1": { @@ -27,6 +27,12 @@ "f_id": "2", "t_id": "0", "inductance": 1.0 + }, + "capacitor": { + "name": "capacitor", + "f_id": "2", + "t_id": "0", + "capacitance": 0.00001 } }, "sources": {