# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
import numpy as np
from VeraGridEngine.enumerations import DeviceType
from VeraGridEngine.Devices.Dynamic.rms_template import RmsModelTemplate
from VeraGridEngine.Devices.Dynamic.var_factory import VarFactory
from VeraGridEngine.Utils.Symbolic.block import (Block, VarPowerFlowReferenceType)
from VeraGridEngine.Utils.Symbolic.block_helpers import tf_to_diffblock_with_antiwindup
import VeraGridEngine.Utils.Symbolic.symbolic as sym
[docs]
def VSCShuntBuild(vfactory: VarFactory, name: str = "") -> RmsModelTemplate:
"""
VSC shunt model
with from side the DC bus and to side the AC bus
"""
templ = RmsModelTemplate()
templ.tpe = DeviceType.VscDevice
inputs = [vfactory.add_var("Vm_"), vfactory.add_var("Va_"), vfactory.add_var("Vdc_"), vfactory.add_var("Idc_")]
Vm = inputs[0]
Va = inputs[1]
v_dc = inputs[2]
i_dc = inputs[3]
# Vars:
P_ac = vfactory.add_var('P_ac')
Q_ac = vfactory.add_var('Q_ac')
P_dc = vfactory.add_var('P_dc')
alpha = vfactory.add_var('alpha')
am = vfactory.add_var('am')
Im = vfactory.add_var('Im')
Ia = vfactory.add_var('Ia')
# Parameters:
bt = vfactory.add_var('bt')
gt = vfactory.add_var('gt')
event_dict = {
bt: vfactory.add_const(0.0),
gt: vfactory.add_const(0.1),
}
sqrt_38 = vfactory.add_const(np.sqrt(3 / 8))
vsc_block = Block(
algebraic_eqs=[
P_ac - (gt * Vm ** 2 - sqrt_38 * am * v_dc * Vm * sym.cos(Va - alpha)
- sqrt_38 * bt * am * v_dc * Vm * sym.sin(Va - alpha)),
Q_ac - (-bt * Vm ** 2 + sqrt_38 * bt * am * v_dc * Vm * sym.cos(Va - alpha)
- sqrt_38 * bt * am * v_dc * Vm * sym.sin(Va - alpha)),
gt * (sqrt_38 * am * v_dc) ** 2 - sqrt_38 * am * v_dc * Vm * sym.cos(Va - alpha)
+ sqrt_38 * am * v_dc * Vm * sym.sin(Va - alpha),
P_ac ** 2 + Q_ac ** 2 - Vm * Im,
P_ac * sym.sin(Va - alpha) - Q_ac * sym.cos(Va - alpha),
],
algebraic_vars=[P_ac, Q_ac, alpha, am, Im, Ia],
)
vsc_block.event_dict = event_dict
vsc_block.in_vars = inputs
vsc_block.out_vars = [P_ac, Q_ac, alpha, am]
templ.block.external_mapping = {
VarPowerFlowReferenceType.Vmt: inputs[0],
VarPowerFlowReferenceType.Vat: inputs[1],
VarPowerFlowReferenceType.Pt: P_ac,
VarPowerFlowReferenceType.Qt: Q_ac,
VarPowerFlowReferenceType.Pf: v_dc * i_dc,
VarPowerFlowReferenceType.Vdc: v_dc,
VarPowerFlowReferenceType.Idc: i_dc,
}
vsc_block.name = name
templ.block = vsc_block
return templ
[docs]
def VSCShuntBuild2(vfactory: VarFactory, name: str = "") -> RmsModelTemplate:
"""
VSC shunt model
with from side the DC bus and to side the AC bus
"""
templ = RmsModelTemplate()
templ.tpe = DeviceType.VscDevice
inputs = [vfactory.add_var("Vm_"), vfactory.add_var("Va_"), vfactory.add_var("Vdc_")]
Vm = inputs[0]
Va = inputs[1]
v_dc = inputs[2]
# Vars:
Pt_vsc = vfactory.add_var('Pt_vsc')
Qt_vsc = vfactory.add_var('Qt_vsc')
Pf_vsc = vfactory.add_var('Pf_vsc')
vsh = vfactory.add_var('vsh')
ash = vfactory.add_var('ash')
# Parameters:
bt = vfactory.add_var('bt')
gt = vfactory.add_var('gt')
Qf = vfactory.add_var('Qf')
event_dict = {
Qf: vfactory.add_const(0.0),
bt: vfactory.add_const(0.0),
gt: vfactory.add_const(0.1),
}
sqrt_38 = vfactory.add_const(np.sqrt(3 / 8))
exp1 = Qt_vsc + bt * Vm ** 2
exp2 = Pt_vsc - gt * Vm ** 2
vsc_block = Block(
algebraic_eqs=[
Pt_vsc + (-bt * vsh * Vm * sym.sin(ash - Va) + gt * Vm ** 2 - gt * vsh * Vm * sym.cos(ash - Va)),
Qt_vsc + (bt * vsh * Vm * sym.cos(ash - Va) - bt * Vm ** 2 - gt * vsh * Vm * sym.sin(ash - Va)),
Pf_vsc + (bt * vsh * Vm * sym.sin(ash - Va) + gt * Vm ** 2 - gt * vsh * Vm * sym.cos(ash - Va)),
],
algebraic_vars=[Pt_vsc, Qt_vsc, vsh, ash, Pf_vsc],
init_eqs={
vsh: sym.sqrt((exp1 ** 2 + exp2 ** 2) / (gt ** 2 + bt ** 2)) / Vm,
ash: sym.atan((-gt * exp1 - bt * exp2) / (bt * exp1 - gt * exp2)),
}
)
vsc_block.event_dict = event_dict
vsc_block.in_vars = inputs
vsc_block.out_vars = [Pt_vsc, Qt_vsc, vsh, ash, Pf_vsc]
vsc_block.external_mapping = {
VarPowerFlowReferenceType.Vmt: inputs[0],
VarPowerFlowReferenceType.Vat: inputs[1],
VarPowerFlowReferenceType.Pt: Pt_vsc,
VarPowerFlowReferenceType.Qt: Qt_vsc,
VarPowerFlowReferenceType.Pf: Pf_vsc,
VarPowerFlowReferenceType.Qf: Qf,
VarPowerFlowReferenceType.Vdc: v_dc,
}
vsc_block.name = name
templ.block = vsc_block
return templ
[docs]
def PVControlBuild2(vfactory: VarFactory, name: str = "") -> RmsModelTemplate:
templ = RmsModelTemplate()
inputs = [vfactory.add_var("Vm_"), vfactory.add_var("Vdc_"), vfactory.add_var("vhs_"), vfactory.add_var("ahs_"),
vfactory.add_var("Pdc_")]
Vm = inputs[0]
vdc = inputs[1]
vhs = inputs[2]
ahs = inputs[3]
Pdc = inputs[4]
# Vars:
x = vfactory.add_var('x')
# Parameters:
Kp = vfactory.add_var('Kp')
Ki = vfactory.add_var('Ki')
vdc_ref = vfactory.add_var('vdc_ref')
Temp = vfactory.add_var('Temp')
G = vfactory.add_var('G')
gamma_p = vfactory.add_var('gamma_p')
Pref = vfactory.add_var('Pref')
Gref = vfactory.add_var('Gref')
Tref = vfactory.add_var('Tref')
vref = vfactory.add_var('vref')
Km = vfactory.add_var('Km')
Ts = vfactory.add_var('Ts_controller')
event_dict = {
vref: vfactory.add_const(None),
Km: vfactory.add_const(10.0),
Ts: vfactory.add_const(0.01),
Kp: vfactory.add_const(0.1),
Ki: vfactory.add_const(0.1),
Temp: vfactory.add_const(300.0),
G: vfactory.add_const(1000.0),
Gref: vfactory.add_const(1000.0),
Pref: vfactory.add_const(None),
Tref: vfactory.add_const(300.0),
gamma_p: vfactory.add_const(-0.001),
vdc_ref: vfactory.add_const(1.033),
}
Ppv = (G / vfactory.add_const(1000.0)) * (Pref * (1 + gamma_p * (Temp - Tref)))
i_dc_ref = Ppv / vdc_ref
init_eqs = {
vref: inputs[0],
Pref: inputs[4],
}
block_vhs_control, _ = tf_to_diffblock_with_antiwindup(
vfactory,
x=(Vm - vref),
y=vhs,
num=[Km],
den=[vfactory.add_const(1), Ts],
name="VSC Vhs Control",
sat_min=vfactory.add_const(-1.0),
sat_max=vfactory.add_const(1.0),
)
# Block that Controls Vdc_ref based on MPPT optimal point
solar_block = Block(
state_eqs=[Ki * (vdc_ref - vdc)],
state_vars=[x],
algebraic_eqs=[
Kp * (vdc_ref - vdc) + x - ahs,
i_dc_ref + vdc_ref * i_dc_ref.diff(vdc_ref),
],
algebraic_vars=[vdc_ref],
in_vars=inputs,
init_eqs=init_eqs,
# children = [block_vhs_control],
event_dict=event_dict,
)
solar_block.name = name
templ.block = solar_block
templ.block.event_dict = event_dict
return templ
[docs]
def PVControlBuild(vfactory: VarFactory, name: str = "") -> RmsModelTemplate:
templ = RmsModelTemplate()
inputs = [vfactory.add_var("Vdc_"), vfactory.add_var("ahs_")]
Vdc = inputs[0]
alpha = inputs[1]
# Vars:
x = vfactory.add_var('x')
# Parameters:
Kp = vfactory.add_var('Kp')
Ki = vfactory.add_var('Ki')
vdc_ref = vfactory.add_var('vdc_ref')
Temp = vfactory.add_var('Temp')
G = vfactory.add_var('G')
gamma_p = vfactory.add_var('gamma_p')
Pref = vfactory.add_var('Pref')
Gref = vfactory.add_var('Gref')
Tref = vfactory.add_var('Tref')
event_dict = {
vdc_ref: vfactory.add_const(1.0),
Kp: vfactory.add_const(0.1),
Ki: vfactory.add_const(0.1),
Temp: vfactory.add_const(300.0),
G: vfactory.add_const(1000.0),
Gref: vfactory.add_const(1000.0),
Pref: vfactory.add_const(0.1),
Tref: vfactory.add_const(300.0),
gamma_p: vfactory.add_const(-0.001),
}
Ppv = (G / vfactory.add_const(1000.0)) * (Pref * (1 + gamma_p * (Temp - Tref)))
i_dc_ref = Ppv / vdc_ref
init_eqs = {}
solar_block = Block(
state_eqs=[Ki * (vdc_ref - Vdc)],
state_vars=[x],
algebraic_eqs=[
Kp * (vdc_ref - Vdc) + x - alpha,
i_dc_ref + vdc_ref * i_dc_ref.diff(vdc_ref),
],
in_vars=inputs,
init_eqs=init_eqs,
)
solar_block.name = name
templ.block = solar_block
templ.block.event_dict = event_dict
return templ
[docs]
def PVCellBuild(vfactory: VarFactory, name: str = "") -> RmsModelTemplate:
templ = RmsModelTemplate()
inputs = [vfactory.add_var("Vdc_")]
Vdc = inputs[0]
# Vars:
iD = vfactory.add_var('iD')
iL = vfactory.add_var('iL')
Idc = vfactory.add_var('Idc')
vD = vfactory.add_var('vD')
Pdc = vfactory.add_var('Pdc')
# Parameters:
Temp = vfactory.add_var('Temp')
gamma_p = vfactory.add_var('gamma_p')
Pref = vfactory.add_var('Pref')
Gref = vfactory.add_var('Gref')
Tref = vfactory.add_var('Tref')
Rsh = vfactory.add_var('Rsh')
Rse = vfactory.add_var('Rse')
Temp0 = vfactory.add_var('Temp0')
is0 = vfactory.add_var('is0')
Sn = vfactory.add_var('Sn')
Aa = vfactory.add_var('Aa')
rho_e = vfactory.add_var('rho_e')
C_temp = vfactory.add_var('C_temp')
G = vfactory.add_var('G')
iL0 = vfactory.add_var('iL0')
event_dict = {
Temp: vfactory.add_const(298.15),
gamma_p: vfactory.add_const(-0.004),
Pref: vfactory.add_const(0.02),
Gref: vfactory.add_const(1000.0),
Tref: vfactory.add_const(298.15),
Rsh: vfactory.add_const(1000.0),
Rse: vfactory.add_const(0.005),
Temp0: vfactory.add_const(298.15),
is0: vfactory.add_const(1e-10),
Sn: vfactory.add_const(1.0),
Aa: vfactory.add_const(1.2),
rho_e: vfactory.add_const(1.68e-8),
C_temp: vfactory.add_const(1000.0),
G: vfactory.add_const(1000.0),
iL0: vfactory.add_const(None)
}
qe = vfactory.add_const(1.60217662e-19) # Electron charge (C)
kB = vfactory.add_const(1.380649e-23) # Boltzmann constant (J/K)
gamma = vfactory.add_const(1.2) # Diode ideality factor (unitless)
def energy_band(Temp):
# Empirical relation for silicon
Eg0 = 1.17 # eV
alpha = 4.73e-4 # eV/K
beta = 636 # K
Eg_of_Temp = Eg0 - (alpha * Temp ** 2) / (Temp + beta)
Eg_of_Temp0 = Eg0 - (alpha * Temp0 ** 2) / (Temp0 + beta)
return qe * Eg0 / (kB * gamma) * (Eg_of_Temp0 - Eg_of_Temp)
v_T = kB * Temp / qe
Is = is0 * (Temp / Temp0) ** 3 * sym.exp(energy_band(Temp))
# TODO: Include Temperature dynamics model
solar_block = Block(
algebraic_eqs=[
Idc - (iL - iD - vD / Rsh),
Pdc - Idc * Vdc,
vD - Vdc - Rse * Idc,
Is * (sym.exp(vD / (gamma * v_T)) - vfactory.add_const(1)) - iD,
iL - iL0,
],
algebraic_vars=[iD, iL, Idc, vD, Pdc],
in_vars=inputs,
out_vars=[Pdc, Idc],
external_mapping={
VarPowerFlowReferenceType.P: Pdc,
VarPowerFlowReferenceType.Idc: Idc,
VarPowerFlowReferenceType.Vdc: Vdc,
},
init_eqs={
Idc: Pdc / Vdc,
vD: Vdc + Rse * Idc,
iD: Is * (sym.exp(vD / (gamma * v_T)) - vfactory.add_const(1)),
iL0: Idc - (- iD - vD / Rsh),
iL: Idc - (- iD - vD / Rsh),
}
)
solar_block.name = "PVCell" + name
solar_block.event_dict = event_dict
templ.block = solar_block
return templ
[docs]
def VscControlledPV(vfactory: VarFactory, name='VscControlledPV') -> RmsModelTemplate:
templ = RmsModelTemplate()
templ.tpe = DeviceType.GeneratorDevice
vsc_block = VSCShuntBuild2(vfactory).block
pv_block = PVControlBuild2(vfactory).block
pv_cell_block = PVCellBuild(vfactory).block
vfactory.add_connections([pv_block.in_vars[2]], [vsc_block.out_vars[2]])
vfactory.add_connections([pv_block.in_vars[3]], [vsc_block.out_vars[3]])
vfactory.add_connections([pv_cell_block.in_vars[0]], [vsc_block.out_vars[0]])
templ.block = Block(children=[pv_block, pv_cell_block])
templ.block.external_mapping = {
VarPowerFlowReferenceType.Vm: vsc_block.in_vars[0],
VarPowerFlowReferenceType.Va: vsc_block.in_vars[1],
VarPowerFlowReferenceType.P: vsc_block.out_vars[0],
VarPowerFlowReferenceType.Q: vsc_block.out_vars[1],
}
return templ