filter - Define a filter PROPOSED FOR 4.0

## Synopsis

filtername(z) =numerator/denominator; filtername(z,sampling-time[time-unit]) =numerator/denominator; filtername(z,sampling-time[time-unit],time-skew[time-unit]) =numerator/denominator;

## Description

A GLM file may define a discrete-time filter as a strictly causal rational function in *z*-domain. Filters can be used to transform a signal from a source double or pseudo-double property into a new value that is applied to a double property in another object. This has the effect of creating an arbitrary link between two objects with a wide range of possible behaviors such as delay, decay, integration, oscillation, etc.

The transfer function from one property to another may be of arbitrary order. However the transfer function must be strictly causal, i.e., the output signal must *follow* the input signal in time. As a result it is a requirement that the order of the *numerator* polynomial in *z* be lower than the order of the *denominator* polynomial in *z*. For example "1/(z^2+2z+1)" is strictly causal (although it is not stable), but "z/(z+0.5)" is not strictly causal (although it is stable).

The *sampling-time* is based on the global clock so that the input signal is sampled periodically and the *time-skew* is used to offset the *sampling-time*, i.e., the input signal is sampled when [math]clock \mod sampling\_time = time\_skew.[/math]

The default *sampling-time* is 1 second and the default *time-skew* is 0 seconds. These defaults are also the minimum values.

Some basic filters include

filter delay(z,${T}) = 1/z; // delay T seconds filter delay(z,${T}) = 1/z^${N}; // delay N*T seconds filter integrate(z,${T}) = 1/(z-1); // integrate sampling every T seconds (note the added delay to make it strictly proper) filter decay(z,${T}) = 1/(z-${A}); // decay at the rate log(A/T) (note the added delay to make it strictly proper) filter oscillator(z,${T}) = ${A}/(z^2-${B}z+1); // use A=sin(wT) and B=2cos(wT) where w is the frequency

For more information on basic filters, consult a Table of z-Transforms.

## Example

The following example delays a signal 5 minutes:

filter delay(z,5min) = 1/z; class from { randomvar value; } class to { double value; } object from { name from; value "type:normal(1,0); min:-3.0; max:+3.0; refresh:1min"; } object to { value delay(from:value); }

The following example implements a discrete form of a second-order response for the non-minimum phase system [math]G(s)=\frac{(s-0.1)}{(s+0.05)(s+0.02)}.[/math] Using the Matlab function

>> c2d(tf([1,-0.1],conv([1,0.05],[1,0.02])),1,'zoh')

we obtain the discrete system [math]G_d(z)=\frac{0.9168z-1.013}{z^2-1.931z+0.9324}.[/math] The corresponding GLM definition of the filter is

filter Gd(z) = ( 0.9168 z - 1.013 ) / ( z^2 - 1.931 z + 0.9324 );

## Caveats

### Stability

The filter definition allows unstable filters to be designed, i.e., the denominator polynomial may have roots outside the unit circle. These roots are called the *poles* of the filter. It is not a requirement that the transfer function be stable and therefore the filter may have poles outside the unit circle. However, unless otherwise compensated for in the implementation of the model, an unstable filter may eventually lead to infinite output.

### Sampling rate

As a general rule it is not advisable to use a sampling rate less than 10x the fastest pole of the filter. For example, the filter [math]\frac{1}{s+0.05}[/math] should use no less than a 2 second sampling time, while the filter [math]\frac{1}{(s+0.1)(s+0.05)}[/math] should use no less than a 1 second sampling time. The filter directive does not check this or emit a warning of any sort if the sampling time is unsuitable.

Multiple filters may be set up in series with different sampling rates, resulting in decimation. GridLAB-D will not warn when the sampling rates differ, but the results will not be same as when the sampling rates are the same, or the filters are combined analytically, as the following example illustrates.

```
filter delay(z,1min) = 1/z;
filter integrate(z,2min) = 1/(z-1);
filter integrated_delay(z,2min) = 1/(z^2-z);
class source {
randomvar output;
}
class delay {
double output;
}
class integrate {
double output;
}
class integrated_delay {
double output;
}
clock {
starttime '2000-01-01 00:00:00';
stoptime '2000-01-02 00:00:00';
}
object source {
name source;
output "type:normal(0,1); min:-3.0; max:+3.0; refresh:30s";
}
object delay {
name delay;
output delay(source:output);
}
object integrate {
name integrate;
output integrate(delay:output);
}
object integrated_delay {
name integrated_delay;
output integrated_delay(source:output);
}
module tape;
object multi_recorder {
property source:output,delay:output,integrate:output,integrated_delay:output;
file output.csv;
interval -1;
};
```

The output shows the expected difference

```
# timestamp,source:output,delay:output,integrate:output,integrated_delay:output
2000-01-01 00:00:00 UTC,-0.191708,-0.191708,+0,+0
2000-01-01 00:00:30 UTC,0.387072,-0.191708,+0,+0
2000-01-01 00:01:00 UTC,-0.423121,-0.423121,+0,+0
2000-01-01 00:01:30 UTC,-0.398225,-0.423121,+0,+0
2000-01-01 00:02:00 UTC,-0.550527,-0.550527,-0.423121,-0.191708
2000-01-01 00:02:30 UTC,1.907246,-0.550527,-0.423121,-0.191708
2000-01-01 00:03:00 UTC,0.033733,+0.0337334,-0.423121,-0.191708
2000-01-01 00:03:30 UTC,0.762972,+0.0337334,-0.423121,-0.191708
2000-01-01 00:04:00 UTC,2.146893,+2.14689,-0.389388,-0.742235
2000-01-01 00:04:30 UTC,0.373822,+2.14689,-0.389388,-0.742235
2000-01-01 00:05:00 UTC,-1.381907,-1.38191,-0.389388,-0.742235
2000-01-01 00:05:30 UTC,1.021778,-1.38191,-0.389388,-0.742235
2000-01-01 00:06:00 UTC,-0.505672,-0.505672,-1.7713,+1.40466
2000-01-01 00:06:30 UTC,1.178886,-0.505672,-1.7713,+1.40466
2000-01-01 00:07:00 UTC,-0.644429,-0.644429,-1.7713,+1.40466
2000-01-01 00:07:30 UTC,-1.398343,-0.644429,-1.7713,+1.40466
2000-01-01 00:08:00 UTC,0.945546,+0.945546,-2.41572,+0.898986
2000-01-01 00:08:30 UTC,-0.260355,+0.945546,-2.41572,+0.898986
2000-01-01 00:09:00 UTC,1.083978,+1.08398,-2.41572,+0.898986
2000-01-01 00:09:30 UTC,2.881078,+1.08398,-2.41572,+0.898986
2000-01-01 00:10:00 UTC,-0.044953,-0.0449535,-1.33175,+1.84453
```

## History

Development of filters is tracked under Ticket 948. Support for general discrete-time filters was introduced in McNary (trunk) at https://sourceforge.net/p/gridlab-d/code/5412. It is planned for release in Keeler (Version 4.0).

## Wanted

The following capabilities are still to be implemented:

- causal filters (requires iteration);
- quantized output (requires support for zpk format filter definitions); and
- saturation limits (requires adding extra parameters to filter spec).