Skip to content
Snippets Groups Projects
Commit 11dae0a6 authored by Marc Moritz's avatar Marc Moritz
Browse files

Merge branch 'revert-32bcec09' into 'master'

Revert "Dummy change for testing of JupyterHub Execution"

See merge request !2
parents 32bcec09 8415acd6
Branches master
No related tags found
1 merge request!2Revert "Dummy change for testing of JupyterHub Execution"
%% Cell type:markdown id:cfe34583 tags:
<style>
/* Set the font for the entire notebook to Times New Roman */
* {
font-family: "Times New Roman", Times, serif;
}
</style>
<div>
<img src="figures/slew_logo.png" style="float: right;height: 15em;">
</div>
# SLEW Case 2 - Electromechanical Dynamics: Small-signal Stability
## Theoretical background
### The Linearized Swing Equation
The swing equation is the fundamental equation that determines the rotor dynamics of a synchronous machine.
$$M\frac{d^{2}\delta}{dt^{2}} = P_{m} - P_{e}(\delta) - D\frac{d\delta}{dt}$$
For small signal stability analysis, the swing equation can be linearized in the vicinity of the operating point $\delta = \delta_{0}$:
$$M\frac{d^{2}\Delta\delta}{dt^{2}} + D\frac{d\Delta\delta}{dt} + K_{e^{'}}\Delta\delta = 0 $$
where $K_{e^{'}}$ is the transient synchronizing power coefficient at $\delta = \delta_{0}$:
$$K_{e^{'}} = \frac{\partial P_{e^{'}}}{\partial \delta^{'}}\mid_{\delta = \delta_{0}}$$
The characteristic equation and the roots of the linearized swing equation are as follows:
$$\lambda^{2} + \frac{D}{M}\lambda + \frac{K_{e^{'}}}{M} = 0 $$
$$\lambda_{1,2} = -\frac{D}{2M} \pm \sqrt{\left(\frac{D}{2M}\right)^{2} - \frac{K_{e^{'}}}{M}} $$
%% Cell type:markdown id:eb59e0e7 tags:
***
## Case description
%% Cell type:markdown id:58c9e2a2 tags:
This is a test change (05.06.2025)
A $200$ MVA round-rotor generator is connected to an infinite bus system of nominal frequency of $50 Hz$ through a step-up transformer with reactance of $0.13 p.u.$ and a transmission line with reactance of $0.17 p.u.$ . The generator’s transient reactance is $x'_{d} = 0.23 p.u.$ and the inertia constant is $H = 4 s$. The synchronous generator mechanical power is set to $0.6 p.u.$ and the steady-state emf $E = 1.1 p.u.$
%% Cell type:markdown id:a6940104 tags:
***
## Case tasks
%% Cell type:markdown id:f3323e4f tags:
#### Task 1
If at the given operating point the system eigen-values are as follows:
$$\lambda_{1,2} = -0.06254 \pm 8.8318j $$
&nbsp;&nbsp;&nbsp;&nbsp;a) Determine whether the system response following a small disturbance is overdamped, critically damped or underdamped.
&nbsp;&nbsp;&nbsp;&nbsp;b) Verify your findings by calculating the damping coefficient *D* (not to be confused with the damping ratio ζ ) of the synchronous generator and then applying the damping value *"D"* in SLEW and generating the corresponding power and angle plots.
#### Task 2
If the system response following a small disturbance is critically damped.
&nbsp;&nbsp;&nbsp;&nbsp;a) Determine the synchronous generator damping coefficient (not to be confused with the damping ratio ζ ).
&nbsp;&nbsp;&nbsp;&nbsp;b) Apply the calculated damping value in SLEW and generate the corresponding power and angle plots.
&nbsp;&nbsp;&nbsp;&nbsp;c) Increase the damping coefficient (not to be confused with the damping ratio ζ ) value further and observe the impact of such increase.
%% Cell type:markdown id:1fc3c700 tags:
**You can process the results from SLEW (as required in 1 and 2) using the prepared notebook cells below.**
%% Cell type:markdown id:9da09483 tags:
***
## Case solutions
%% Cell type:markdown id:d1a0ab6c tags:
#### First Setup your Python for post-processing
%% Cell type:markdown id:8432a231 tags:
Import relevant Python packages by executing the following cell:
%% Cell type:code id:5bb649a9 tags:
``` python
from IPython.display import display
from villas.web.result import Result
from pprint import pprint
import tempfile
import os
from villas.dataprocessing.readtools import *
from villas.dataprocessing.timeseries import *
import matplotlib.pyplot as plt
import scipy.io as sio
outputs = sio.loadmat('outputs/case2_output.mat', simplify_cells=True)
#matplotlib widget
%matplotlib widget
```
%% Cell type:markdown id:290ba8ba tags:
### Task 1
%% Cell type:markdown id:3564b4be tags:
If at the given operating point the system eigen-values are as follows:
$$\lambda_{1,2} = -0.06254 \pm 8.8318j $$
%% Cell type:markdown id:29d31393 tags:
#### a)
Determine whether the system response following a small disturbance is overdamped, critically damped or underdamped.
%% Cell type:code id:e1620ea2 tags:
``` python
import scipy.io
import ipywidgets as widgets
from IPython.display import display, clear_output
# Load the solution from the .mat file
mat_data = scipy.io.loadmat('outputs/case2_output.mat')
correct_answer = mat_data['correct_answer'][0]
# Create a radio button widget for the damping question
radio_btn = widgets.RadioButtons(
options=['Overdamped', 'Critically Damped', 'Underdamped'],
description='Select the correct answer:',
disabled=False
)
# Create an output widget to display the result
output = widgets.Output()
# Function to check the student's answer, display the result, and disable further attempts
def check_answer(b):
with output:
clear_output() # Clear previous output
selected = radio_btn.value
if selected == correct_answer:
print("Your answer is correct! The Eigenvalues are complex conjugate pair with an imaginary part \ineq 0, therefore the system is underdamped.")
else:
print("Incorrect. Try again!")
# Disable the radio buttons and the submit button after the first attempt
#radio_btn.disabled = True
#submit_btn.disabled = True
# Create a button to submit the answer
submit_btn = widgets.Button(description="Submit Answer")
# Link the submit button to the check_answer function
submit_btn.on_click(check_answer)
# Display the radio button, submit button, and output widget
display(radio_btn, submit_btn, output)
```
%% Cell type:markdown id:b08ba784 tags:
#### b)
Verify your findings by calculating the damping coefficient (not to be confused with the damping ratio ζ ) of the synchronous generator and then applying the damping value in SLEW and generating the corresponding power and angle plots.
To check your results, enter your solution for *"D"* rounded to 4 digits after the decimal point:
%% Cell type:code id:4d8c545c tags:
``` python
D=input('D in Subtask 1.b:')
print('Your result is', 'correct!' if round(float(D),4)==round(outputs['D_T1b'],4) else 'incorrect. You should double-check your calculations.')
```
%% Cell type:markdown id:5592eeac tags:
Download simulation results from SLEW
%% Cell type:markdown id:192ea4fa tags:
Enter SLEW under [https://slew.k8s.eonerc.rwth-aachen.de](https://slew.k8s.eonerc.rwth-aachen.de).
Adjust the parameter *"D"* and run a corresponding simulation.
Then, enter your code snippet for result download in the following cell:
%% Cell type:code id:3a922965 tags:
``` python
# Access result using token
r = Result(1, 'xyz', endpoint='https://slew.k8s.eonerc.rwth-aachen.de')
```
%% Cell type:markdown id:7d391ebd tags:
#### Plot the results
%% Cell type:code id:609cbf87 tags:
``` python
results_file_name='SP_SynGenTrStab_SMIB_Fault_SlewCase2_JsonSyngenParams_SP.csv'
# Get file by name
f = r.get_file_by_name(results_file_name)
# Create temp dir
tmpdir = tempfile.mkdtemp()
results_file_path = os.path.join(tmpdir,results_file_name)
# Load files as bytes
f = f.download(dest=results_file_path)
# Get time series object from csv
ts_res1 = read_timeseries_csv(results_file_path, print_status=True)
# Plot the currents
plt.figure(figsize=(8,12))
name_list = ['Rotor Angle','Output Power']
plt.subplot(2,1,1)
plt.ylabel("Angle (degree)")
plt.xlabel("time (s)")
plt.plot(ts_res1['delta_r_gen'].time, ts_res1['delta_r_gen'].values, label='Rotor Angle task 1.b', color='C0')
plt.xlim([1, 30])
plt.legend(loc='upper right')
plt.subplot(2,1,2)
plt.ylabel("Power (MW)")
plt.xlabel("time (s)")
plt.plot(ts_res1['P_elec'].time, ts_res1['P_elec'].values/1e6, label='Output Power task 1.b', color='C1')
plt.xlim([1, 30])
plt.legend(loc='upper right')
plt.show()
```
%% Cell type:markdown id:8ef9b8b2 tags:
### Task 2
%% Cell type:markdown id:00d2e239 tags:
If the system response following a small disturbance is critically damped.
%% Cell type:markdown id:b1a6d35b tags:
#### a)
Determine the synchronous generator damping coefficient (not to be confused with the damping ratio ζ ).
To check your results, enter your solution for *"D"* rounded to 4 digits behind the comma:
%% Cell type:code id:e70ae849 tags:
``` python
D=input('D in Subtask 2.a:')
print('Your result is', 'correct!' if round(float(D),4)==round(outputs['D_T2a'],4) else 'incorrect. You should double-check your calculations.')
```
%% Cell type:markdown id:6ab41b23 tags:
#### b)
Apply the calculated damping value in SLEW and generate the corresponding power and angle plots.
Download simulation results from SLEW
%% Cell type:markdown id:5f52f99d tags:
Enter SLEW under [https://slew.k8s.eonerc.rwth-aachen.de](https://slew.k8s.eonerc.rwth-aachen.de).
Adjust the parameter *"D"* and run a corresponding simulation.
Then, enter your code snippet for result download in the following cell:
%% Cell type:code id:921256f6 tags:
``` python
# Access result using token
r = Result(1, 'xyz', endpoint='https://slew.k8s.eonerc.rwth-aachen.de')
```
%% Cell type:markdown id:4d20385b tags:
#### Plot the results
%% Cell type:code id:ba064129 tags:
``` python
results_file_name='SP_SynGenTrStab_SMIB_Fault_SlewCase2_JsonSyngenParams_SP.csv'
# Get file by name
f = r.get_file_by_name(results_file_name)
# Create temp dir
tmpdir = tempfile.mkdtemp()
results_file_path = os.path.join(tmpdir,results_file_name)
# Load files as bytes
f = f.download(dest=results_file_path)
# Get time series object from csv
ts_res2 = read_timeseries_csv(results_file_path, print_status=False)
# Plot the currents
plt.figure(figsize=(8,12))
name_list = ['Rotor Angle','Output Power']
plt.subplot(2,1,1)
plt.ylabel("Angle (degree)")
plt.xlabel("time (s)")
plt.plot(ts_res2['delta_r_gen'].time, ts_res2['delta_r_gen'].values, label='Rotor Angle task 2.b', color='C0')
plt.plot(ts_res1['delta_r_gen'].time, ts_res1['delta_r_gen'].values, label='Rotor Angle task 1.b', color='black', linestyle=':')
plt.xlim([1, 30])
plt.legend(loc='upper right')
plt.subplot(2,1,2)
plt.ylabel("Power (MW)")
plt.xlabel("time (s)")
plt.plot(ts_res2['P_elec'].time, ts_res2['P_elec'].values/1e6, label='Output Power task 2.b', color='C1')
plt.plot(ts_res1['P_elec'].time, ts_res1['P_elec'].values/1e6, label='Output Power task 1.b', color='black', linestyle=':')
plt.xlim([1, 30])
plt.legend(loc='upper right')
plt.show()
```
%% Cell type:markdown id:18f20b09 tags:
#### c)
Increase the damping coefficient (not to be confused with the damping ratio ζ ) value (for example by a factor of 2) further and observe the impact of such increase.
Download simulation results from SLEW
%% Cell type:markdown id:4f967ccb tags:
Enter SLEW under [https://slew.k8s.eonerc.rwth-aachen.de](https://slew.k8s.eonerc.rwth-aachen.de).
Adjust the parameter *"D"* and run a corresponding simulation.
Then, enter your code snippet for result download in the following cell:
%% Cell type:code id:79b309eb tags:
``` python
# Access result using token
r = Result(1, 'xyz', endpoint='hhttps://slew.k8s.eonerc.rwth-aachen.de')
```
%% Cell type:markdown id:a4dbb836 tags:
#### Plot the results
%% Cell type:code id:ba53ba36 tags:
``` python
results_file_name='SP_SynGenTrStab_SMIB_Fault_SlewCase2_JsonSyngenParams_SP.csv'
# Get file by name
f = r.get_file_by_name(results_file_name)
# Create temp dir
tmpdir = tempfile.mkdtemp()
results_file_path = os.path.join(tmpdir,results_file_name)
# Load files as bytes
f = f.download(dest=results_file_path)
# Get time series object from csv
ts_res3 = read_timeseries_csv(results_file_path, print_status=False)
# Plot the currents
plt.figure(figsize=(8,12))
name_list = ['Rotor Angle','Output Power']
plt.subplot(2,1,1)
plt.ylabel("Angle (degree)")
plt.xlabel("time (s)")
plt.plot(ts_res3['delta_r_gen'].time, ts_res3['delta_r_gen'].values, label='Rotor Angle task 2.c', color='C0')
plt.plot(ts_res2['delta_r_gen'].time, ts_res2['delta_r_gen'].values, label='Rotor Angle task 2.b', color='black', linestyle=':')
plt.xlim([1, 30])
plt.legend(loc='upper right')
plt.subplot(2,1,2)
plt.ylabel("Power (MW)")
plt.xlabel("time (s)")
plt.plot(ts_res3['P_elec'].time, ts_res3['P_elec'].values/1e6, label='Output Power task 2.c', color='C1')
plt.plot(ts_res2['P_elec'].time, ts_res2['P_elec'].values/1e6, label='Output Power task 2.b', color='black', linestyle=':')
plt.xlim([1, 30])
plt.legend(loc='upper right')
plt.show()
```
%% Cell type:code id:d74fe01a tags:
``` python
```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment