Source code for andes.models.exciter.saturation

from andes.core.block import Block
from andes.core.common import dummify
from andes.core.service import ConstService, FlagValue, InitChecker


[docs]class ExcExpSat(Block): r""" Exponential exciter saturation block to calculate A and B from E1, SE1, E2 and SE2. Input parameters will be corrected and the user will be warned. To disable saturation, set either E1 or E2 to 0. Parameters ---------- E1 : BaseParam First point of excitation field voltage SE1: BaseParam Coefficient corresponding to E1 E2 : BaseParam Second point of excitation field voltage SE2: BaseParam Coefficient corresponding to E2 """ def __init__(self, E1, SE1, E2, SE2, name=None, tex_name=None, info=None): Block.__init__(self, name=name, tex_name=tex_name, info=info) self._E1 = E1 self._E2 = E2 self._SE1 = SE1 self._SE2 = SE2 self.zE1 = FlagValue(self._E1, value=0., info='Flag non-zeros in E1', tex_name='z^{E1}', ) self.zE2 = FlagValue(self._E2, value=0., info='Flag non-zeros in E2', tex_name='z^{E2}', ) self.zSE1 = FlagValue(self._SE1, value=0., info='Flag non-zeros in SE1', tex_name='z^{SE1}', ) self.zSE2 = FlagValue(self._SE2, value=0., info='Flag non-zeros in SE2', tex_name='z^{SE2}') # disallow E1 = E2 != 0 since the curve fitting will fail self.E12c = InitChecker( self._E1, not_equal=self._E2, info='E1 and E2 after correction', error_out=True, ) # data correction for E1, E2, SE1 self.E1 = ConstService( tex_name='E^{1c}', info='Corrected E1 data', ) self.E2 = ConstService( tex_name='E^{2c}', info='Corrected E2 data', ) self.SE1 = ConstService( tex_name='SE^{1c}', info='Corrected SE1 data', ) self.SE2 = ConstService( tex_name='SE^{2c}', info='Corrected SE2 data', ) self.A = ConstService(info='Saturation gain', tex_name='A^e', ) self.B = ConstService(info='Exponential coef. in saturation', tex_name='B^e', ) self.vars = { 'E1': self.E1, 'E2': self.E2, 'SE1': self.SE1, 'SE2': self.SE2, 'zE1': self.zE1, 'zE2': self.zE2, 'zSE1': self.zSE1, 'zSE2': self.zSE2, 'A': self.A, 'B': self.B, }
[docs] def define(self): r""" Notes ----- The implementation solves for coefficients `A` and `B` which satisfy .. math :: E_1 S_{E1} = A e^{E1\times B} E_2 S_{E2} = A e^{E2\times B} The solutions are given by .. math :: E_{1} S_{E1} e^{ \frac{E_1 \log{ \left( \frac{E_2 S_{E2}} {E_1 S_{E1}} \right)} } {E_1 - E_2}} - \frac{\log{\left(\frac{E_2 S_{E2}}{E_1 S_{E1}} \right)}}{E_1 - E_2} """ self.E1.v_str = f'{self._E1.name} + (1 - {self.name}_zE1)' self.E2.v_str = f'{self._E2.name} + 2*(1 - {self.name}_zE2)' self.SE1.v_str = f'{self._SE1.name} + (1 - {self.name}_zSE1)' self.SE2.v_str = f'{self._SE2.name} + 2*(1 - {self.name}_zSE2)' self.A.v_str = f'{self.name}_zE1*{self.name}_zE2 * ' \ f'{self.name}_E1*{self.name}_SE1*' \ f'exp({self.name}_E1*log({self.name}_E2*{self.name}_SE2/' \ f'({self.name}_E1*{self.name}_SE1))/({self.name}_E1-{self.name}_E2))' self.B.v_str = f'-log({self.name}_E2*{self.name}_SE2/({self.name}_E1*{self.name}_SE1))/' \ f'({self.name}_E1 - {self.name}_E2)'
class ExcQuadSat(Block): r""" Exponential exciter saturation block to calculate ``A`` and ``B from ``E1``, ``SE1``, ``E2`` and ``SE2``. Input parameters will be corrected and the user will be warned. To disable saturation, set either ``E1`` or ``E2`` to 0. Parameters ---------- E1 : BaseParam First point of excitation field voltage SE1: BaseParam Coefficient corresponding to E1 E2 : BaseParam Second point of excitation field voltage SE2: BaseParam Coefficient corresponding to E2 """ def __init__(self, E1, SE1, E2, SE2, name=None, tex_name=None, info=None): Block.__init__(self, name=name, tex_name=tex_name, info=info) self._E1 = dummify(E1) self._E2 = dummify(E2) self._SE1 = SE1 self._SE2 = SE2 self.zSE2 = FlagValue(self._SE2, value=0., info='Flag non-zeros in SE2 as 1s', tex_name='z^{SE2}') # data correction for E1, E2, SE1 (TODO) self.E1 = ConstService( tex_name='E^{1c}', info='Corrected E1 data', ) self.E2 = ConstService( tex_name='E^{2c}', info='Corrected E2 data', ) self.SE1 = ConstService( tex_name='SE^{1c}', info='Corrected SE1 data', ) self.SE2 = ConstService( tex_name='SE^{2c}', info='Corrected SE2 data', ) self.a = ConstService(info='Intermediate Sat coeff', tex_name='a', ) self.A = ConstService(info='Saturation start', tex_name='A^q', ) self.B = ConstService(info='Saturation gain', tex_name='B^q', ) self.vars = { 'E1': self.E1, 'E2': self.E2, 'SE1': self.SE1, 'SE2': self.SE2, 'zSE2': self.zSE2, 'a': self.a, 'A': self.A, 'B': self.B, } def define(self): r""" Define equations for calculating the saturation coefficients. Parameter corrections for E2 and SE2: if SE2 == 0, E2 and SE2 will be set to nonzeros by adding `2` to each. The resulting ``A`` and ``B`` will both be zeros to disable saturation. Notes ----- TODO. """ self.E1.v_str = f'{self._E1.name}' self.E2.v_str = f'{self._E2.name} + 2 * (1 - {self.name}_zSE2)' self.SE1.v_str = f'{self._SE1.name}' self.SE2.v_str = f'{self._SE2.name} + 2 * (1 - {self.name}_zSE2)' self.a.v_str = f'(Indicator({self.name}_SE2>0)+Indicator({self.name}_SE2<0)) * ' \ f'sqrt({self.name}_SE1 * {self.name}_E1 /({self.name}_SE2 * {self.name}_E2))' self.A.v_str = f'{self.name}_E2 - ({self.name}_E1 - {self.name}_E2) / ({self.name}_a - 1)' self.B.v_str = f'(Indicator({self.name}_a>0)+Indicator({self.name}_a<0)) *' \ f'{self.name}_SE2 * {self.name}_E2 * ({self.name}_a - 1)**2 / ' \ f'({self.name}_E1 - {self.name}_E2)** 2'