Skip to content

Commit

Permalink
DOC - Add documentation, edit SPICE.json fixed minor changes in the GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
mroncera committed May 6, 2024
1 parent f6e02a8 commit a135a47
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 80 deletions.
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"number_of_plots": 3,
"number_of_plots": 2,
"param_proportion": 2,
"plot_proportion" : 4,
"default_file" : "default.json"
Expand Down
108 changes: 108 additions & 0 deletions contributing.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# Table of Contents

- [How to add a new calculation strategy](#How-to-add-a-new-calculation-strategy)
- [Preliminary Checks](#Preliminary-Checks)
- [Adding New Parameters](#Adding-New-Parameters)
- [Strategy Implementation](#Strategy-Implementation)
- [Method Specifications](#Method-Specifications)
- [Dependencies](#Dependencies)
- [Calculate method](#Calculate-method)
- [How to add a new parameter](#How-to-add-a-new-parameter)
- [Unit Conversion](#Unit-Conversion)
- [Linting](#Linting)
- [Git Guidelines](#Git-Guidelines)
- [Branching Strategy](#Branching-Strategy)
- [Commit Message Format](#Commit-Message-Format)
- [Types](#Types)
- [Best Practices for Pull Requests](#Best-Practices-for-Pull-Requests)
- [SPICE](#SPICE)

# How to add a new calculation strategy

Expand Down Expand Up @@ -209,4 +227,94 @@ For more detailed examples and best practices on commit messages, refer to [this

By following these guidelines, you contribute to the efficiency and clarity of the project, making it easier for others to review your contributions and maintain the project's health.

# SPICE

Simulation Program with Integrated Circuit Emphasis (SPICE) is a general-purpose analog electronic circuit simulator. It is a powerful tool for simulating and analyzing circuits, providing valuable insights into circuit behavior and performance. SPICE is widely used in the electronics industry for designing and testing circuits, making it an essential tool for electrical engineers and circuit designers.

In PLASMAG, we integrate spice simulations to allow the user to create their own models and have more accurate results.

## How to add a new SPICE model

### 1. Prepare the JSON Configuration for the SPICE Module

Each SPICE module is defined in a JSON file, which outlines the circuit characteristics along with associated parameters and calculation strategies. Here are the steps to prepare this file:

1. **Module Key Creation:**
Add a new key in the JSON file (e.g., **spice_new_model**) that will act as a unique identifier for the new module.

2. **Defining Module Attributes:**
- **name:** The name of the module, such as "New OP AMP Model".
- **description:** A detailed description of the module, used for the user interface.
- **image:** The filename of the image representing the circuit (must be pre-added to the relevant images directory).

3. **Parameters:**
Define all necessary parameters for the module. Each parameter should include:
- **default, min, max:** Default, minimum, and maximum values.
- **description:** A description of the parameter for the user interface.
- **input_unit and target_unit:** Input and conversion units, if applicable.

4. **Strategies:**
- Add references to specific calculation strategies used by this module in the `src/model/strategies/strategy_lib/` directory.
- Each strategy must have a **name** and a **file** pointing to the Python script implementing the strategy.

#### Example JSON Section for a New SPICE Module

```json
"spice_new_model" : {
"name" : "New OP AMP Model",
"description" : "An advanced test circuit for the spice simulator",
"image" : "new_op_amp_model.png",
"parameters" : {
"R1": {
"default": 500,
"min": 10,
"max": 10000,
"description": "Spice test resistance",
"input_unit": "ohm",
"target_unit": "ohm"
}
},
"strategies" : {
"SPICE_impedance" : {
"name" : "SPICE Impedance",
"file" : "src/model/strategies/strategy_lib/SPICE.py"
}
}
}

```

### 2. Implement Calculation Strategies

For each strategy listed in the JSON configuration:

- Create a Python class in the specified file within `src/model/strategies/strategy_lib/`.
- Each class should inherit from the **CalculationStrategy** class and implement necessary methods such as **calculate** and **get_dependencies**.
- The calculate method should handle the specific SPICE simulation logic based on input parameters and dependencies defined in the module.


### 3. Test and Validate

Once the new module is configured and the strategies are implemented:

- Test the module extensively to ensure that it works as expected with the SPICE simulator. You can try the script outside of PLASMAG
in the `if __name__ == "__main__"`, with mock data.
- Validate that all parameters and calculations produce accurate and reliable results.

### 4. Update User Documentation and Interface

- Add any necessary documentation to help users understand how to use the new SPICE module.

### 5. Commit Changes

Follow the project's Git guidelines to commit and push the new module to the repository:

- Create a feature branch, e.g., feature/spice_new_model.
- Commit your changes with a clear message describing the addition of the new SPICE module.
- Open a pull request for review.

### 5. MISC

- Make sure to return the results in SI units for consistency. X-axis values should be in Hz, or seconds for time-domain simulations.
- If the X-axis is in the time domain, make sure to return the time vector as the first column of the tensor, with the following label : "Time"
- If you are returning the Gain and the Phase, make sure to return a linear Gain, and the phase in radians. The first column should be the frequency values, the second column the gain, and the third column the phase, with the following labels : "Frequency", "Gain", "Phase"
53 changes: 3 additions & 50 deletions data/SPICE.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@
"name" : "SPICE test",
"file" : "src/model/strategies/strategy_lib/SPICE.py"
}

}
},
"spice_test2" : {
"name" : "Spice test OP AMP v2",
"Spice Impedance SCM" : {
"name" : "Spice Impedance SCM",
"description" : "A simple test circuit for the spice simulator",
"image" : "RLC_circuit.png",
"parameters" : {
Expand All @@ -102,60 +103,12 @@
"description": "Spice resistance test",
"input_unit": "",
"target_unit": ""
},
"R2": {
"default": 2000,
"min": 1,
"max": 10000,
"description": "Spice resistance test",
"input_unit": "",
"target_unit": ""
},
"R3": {
"default": 3000,
"min": 1,
"max": 10000,
"description": "Spice resistance test",
"input_unit": "",
"target_unit": ""
},
"R4": {
"default": 4000,
"min": 1,
"max": 10000,
"description": "Spice resistance test",
"input_unit": "",
"target_unit": ""
},
"R5": {
"default": 5000,
"min": 1,
"max": 10000,
"description": "Spice resistance test",
"input_unit": "",
"target_unit": ""
},
"spice_resistance_test" : {
"default": 1,
"min": 1,
"max": 10000,
"description": "Spice resistance test",
"input_unit": "",
"target_unit": ""
}
},
"strategies" : {
"SPICE_impedance": {
"name": "SPICE_impedance",
"file": "src/model/strategies/strategy_lib/SPICE.py"
},
"SPICE_op_Amp_transcient": {
"name": "SPICE_op_Amp_transcient",
"file": "src/model/strategies/strategy_lib/SPICE.py"
},
"SPICE_op_Amp_gain": {
"name": "SPICE_op_Amp_gain",
"file": "src/model/strategies/strategy_lib/SPICE.py"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: PLASMAG
name: PLASMAG_22
channels:
- conda-forge
- defaults
Expand Down
146 changes: 118 additions & 28 deletions src/model/strategies/strategy_lib/SPICE.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,71 @@ def calculate(self, dependencies: dict, parameters: InputParameters):
def get_dependencies():
return ['frequency_vector', "f_start", "f_stop", "spice_resistance_test", "temperature", "R1", "R2", "R3", "R4", "R5"]

class SPICE_op_Amp_noise(CalculationStrategy):
def calculate(self, dependencies: dict, parameters: InputParameters):
temperature = parameters.data['temperature']
f_start = parameters.data['f_start']
f_stop = parameters.data['f_stop']

R1 = parameters.data['R1']
R2 = parameters.data['R2']
R3 = parameters.data['R3']
R4 = parameters.data['R4']
R5 = parameters.data['R5']

frequency_vector = dependencies['frequency_vector']['data']
logger = Logging.setup_logging()

# Convert temperature to degrees Celsius
temperature = temperature - 273.15

opAMP = 'src/model/strategies/strategy_lib/spice_lib/uA741.lib'

##*********************************************
## Circuit Netlist
circuit = Circuit('Op-amp circuits - Noise Analysis for Non-inverting Amplifier')
circuit.include(opAMP)

circuit.V(2, '+Vcc', circuit.gnd, 15 @ u_V)
circuit.V(3, '-Vcc', circuit.gnd, -15 @ u_V)

circuit.X(1, 'uA741', 'input', 'v-', '+Vcc', '-Vcc', 'out')

circuit.R(1, 'v-', circuit.gnd, R1@ u_Ω)
circuit.R(2, 'v-', 'x', R2@ u_Ω)
circuit.R(3, 'x', 'out', R3@ u_Ω)
circuit.R(4, 'x', circuit.gnd, R4@ u_Ω)
circuit.R('L', 'out', circuit.gnd, R5@ u_Ω)

##*********************************************
## Simulation: Noise Analysis
simulator = circuit.simulator(temperature=temperature, nominal_temperature=25)
analysis = simulator.noise(output_node='out', input_node='input', start_frequency=f_start @ u_Hz,
stop_frequency=f_stop @ u_Hz, number_of_points=len(frequency_vector),
variation='lin')



# Retrieve noise data for each frequency
frequency = np.array(analysis.frequency)
output_noise_voltage = np.array(analysis.noise_output_voltage_density)

# Interpolate the results onto the frequency vector if necessary
interpolated_noise = np.interp(frequency_vector, frequency, output_noise_voltage)

result = np.column_stack((frequency_vector, interpolated_noise))

return {
"data": result,
"labels": ["Frequency", "Output Noise Voltage Density"],
"units": ["Hz", "V/√Hz"]
}

@staticmethod
def get_dependencies():
return ['frequency_vector', "f_start", "f_stop", "temperature", "R1", "R2", "R3", "R4", "R5"]


class SPICE_op_Amp_transcient(CalculationStrategy):
def calculate(self, dependencies: dict, parameters: InputParameters):
temperature = parameters.data['temperature']
Expand Down Expand Up @@ -299,40 +364,65 @@ def get_dependencies():
f_stop = 1000000
circuit = Circuit(' Imp JUICE')

circuit.SinusoidalVoltageSource('V1', 'N1', circuit.gnd, amplitude=1 @ u_V, frequency=1 @ u_kHz)
circuit.R('1', 'N001', 'N1', 550 @ u_Ω)
V1 = circuit.SinusoidalVoltageSource('V1', 'N1', circuit.gnd, amplitude=1 @ u_V, frequency=1 @ u_kHz)
circuit.R('1', 'N1', 'N001', 550 @ u_Ω)
circuit.C('1', 'N2', 'N1', 150 @ u_pF)
circuit.L('1', 'N2', 'N001', 12 @ u_H)
circuit.R('2', 'N2', circuit.gnd, 1 @ u_kΩ)

circuit.R2.plus.add_current_probe(circuit)
# Set the simulation temperature
temperature = 25

simulator = circuit.simulator(temperature=25, nominal_temperature=25)
analysis = simulator.ac(start_frequency=f_start @ u_Hz, stop_frequency=f_stop @ u_Hz, number_of_points=10,
variation='dec') # 1 to 1MHz, 10 points per decade

frequency = analysis.frequency.as_ndarray()
voltage_N1 = analysis['n1'].as_ndarray()
current_R2 = analysis['vr2_plus'].as_ndarray()

Z = voltage_N1 / current_R2

# plot imepdance and phase
fig, ax = plt.subplots(2, 1, figsize=(10, 7))
ax[0].semilogx(frequency, np.abs(Z))
ax[0].semilogy()
ax[0].set_ylabel('Impedance [Ohm]')
ax[0].set_title('Impedance of the circuit')
ax[0].grid(True)

ax[1].semilogx(frequency, np.angle(Z, deg=True))
ax[1].set_xlabel('Frequency [Hz]')
ax[1].set_ylabel('Phase [°]')
ax[1].set_title('Phase of the circuit')
ax[1].grid(True)
# Setup the frequency range
f_start = 1
f_stop = 1000000

plt.tight_layout()
plt.show()
# Instantiate the simulator
simulator = circuit.simulator(temperature=temperature, nominal_temperature=25)

# Perform the noise analysis
analysis = simulator.noise(
output_node='N1', # Correct node naming
ref_node='N2', # Correct node naming
src='V1', # Specify the correct noise source
points=100,
start_frequency=f_start @ u_Hz, # Ensure the unit is properly appended
stop_frequency=f_stop @ u_Hz, # Ensure the unit is properly appended
variation='dec'
)

# Output the results
for frequency, noise in zip(analysis.frequency, analysis.noise_voltage_density):
print(f'Frequency: {frequency} Hz, Noise: {noise} V/√Hz')

# circuit.R2.plus.add_current_probe(circuit)
#
# simulator = circuit.simulator(temperature=25, nominal_temperature=25)
# analysis = simulator.ac(start_frequency=f_start @ u_Hz, stop_frequency=f_stop @ u_Hz, number_of_points=10,
# variation='dec') # 1 to 1MHz, 10 points per decade
#
# frequency = analysis.frequency.as_ndarray()
# voltage_N1 = analysis['n1'].as_ndarray()
# current_R2 = analysis['vr2_plus'].as_ndarray()
#
# Z = voltage_N1 / current_R2

# # plot imepdance and phase
# fig, ax = plt.subplots(2, 1, figsize=(10, 7))
# ax[0].semilogx(frequency, np.abs(Z))
# ax[0].semilogy()
# ax[0].set_ylabel('Impedance [Ohm]')
# ax[0].set_title('Impedance of the circuit')
# ax[0].grid(True)
#
# ax[1].semilogx(frequency, np.angle(Z, deg=True))
# ax[1].set_xlabel('Frequency [Hz]')
# ax[1].set_ylabel('Phase [°]')
# ax[1].set_title('Phase of the circuit')
# ax[1].grid(True)
#
# plt.tight_layout()
# plt.show()



Expand Down
1 change: 1 addition & 0 deletions src/view/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,7 @@ def get_x_vector(data_meta, default_vector):
return data_meta["data"][:, 0] # Use the first column as the time vector
return default_vector


for i, (canvas, combo_box, checkbox) in enumerate(zip(self.canvases, self.comboboxes, self.checkboxes)):
selected_key = combo_box.currentText()
if not selected_key:
Expand Down

0 comments on commit a135a47

Please sign in to comment.