Sensor Placement for Fault Isolation in Linear Differential-Algebraic Systems

Erik Frisk, frisk@isy.liu.se
Department of Electrical Engineering
Linköpings universitet
Sweden

GNU General Public License

Author (s) : Erik Frisk
Revision : 0.1, Date : 2007/09/04

Copyright (C) 2007 Erik Frisk

This file is part of LinSensPlaceTool.

LinSensPlaceTool is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
LinSensPlaceTool is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with LinSensPlaceTool; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110 - 1301 USA

Read Me First

This Mathematica notebook contains an implementation of the algorithm described in the paper 
"Sensor Placement for Fault Isolation in Linear Differential-Algebraic Systems" by Erik Frisk, Mattias Krysander, and Jan Åslund (2009), Automatica, 45(2):364--371.

The software is primarily intended for research and small problems, no effort has been put into the development to make the implementation fast or efficient. Also, the implementation has not undrgone any thorough testing and is probably subjected to some, hopefully none important, bugs.

With this in mind, feel free to use and explore the software as is. At the moment there is no documentation written outside what is found in this notebook. Explore the function reference and example sections for instructions on how to use the software.

If you use this software in research or otherwise, please cite the paper mentioned above or any possible later publications. The latest version of this software can always be found at
http://www.fs.isy.liu.se/Software/LinSensPlaceTool/ where also a reference is given to our latest publication concerning this software.

If you have comments, bugreports or other concerns the author may be contacted at the following email address: Erik Frisk (frisk@isy.liu.se).

Sensor Placement Functions - package source

IsolabilityAnalysis

IsolabilityAnalysis::usage:="Performs isolability analysis.
Syntax: IsolabilityAnalysis[M] 

Input: A model object M
Output: Returns a list where each element represent a group of faults that are isolable from the rest. For example, {f1,{f2,f3},f4} means that f1 and f4 are uniquely isolable while f2 and f3 can not be isolated from each other in model M.";
IsolabilityAnalysis[M_LinMod]:=Module[{res,k,f,fgroup,bar,ndpos,M2},
M2=DecoupleNonDetectableFaults[M];
f=GetFNames[M2];
(*f=Extract[GetFNames[M],Position[DetectabilityAnalysis[M],True]];
M2=M;*)
res={};
While[Length[f]>0,
bar=DecoupleFaults[M2,{f[[1]]}];
ndpos=Position[DetectabilityAnalysis[bar],False];
fgroup=If[Length[ndpos]>0,
{f[[1]]}⋃Extract[GetFNames[bar],ndpos],
{f[[1]]}];
res=res⋃{fgroup};
f=SetDiff[f,fgroup];
];
Return[res];
];

DecoupleNonDetectableFaults[M_LinMod]:=Module[{ndf},
ndf=Extract[GetFNames[M],Position[DetectabilityAnalysis[M],False]];
Return[DecoupleFaults[M,ndf]];
];

SensPlaceIsolability

SensPlaceIsolability::usage="Performs sensor placement analysis to meet a given isolability specification.
Syntax: SensPlaceIsolability[M,sensloc,isolspec,sensfault]

Input: 
M        - Model object
sensloc  - List of possible sensor locations (optional). If not provided, or given as {}, it is assumed that all variables in the model are possible sensor locations.
isolspec - Isolability specification, e.g. {f1, [f2,f3},f4} (optional). If not provided, or given as {}, it is assumed that full isolability is specified.
sensfault - Set of sensor positions that, when added, may become faulty. If a sensor position is included in sensfault, full isolability of also this new fault is used in the analysis. If set to 0 or 1, none or all sensors are assumed to be subjected to faults.(optional).

Output: Set of minimal sensor sets.";

SensPlaceIsolability[M_]:=SensPlaceIsolability[M,GetXNames[M],{}];
SensPlaceIsolability[M_,svar_]:=SensPlaceIsolability[M,svar,{}];
SensPlaceIsolability[M_LinMod,svar_,freq_]:=SensPlaceIsolability[M,svar,freq,0];

SensPlaceIsolability[M_LinMod,svar_,freq_,Pf_]:=Module[{detSS,isolSS,freq2,senspos},
senspos=If[Length[svar]==0,GetXNames[M],svar];
freq2=If[Length[freq]==0,GetFNames[M],freq];
detSS=MinimalHittingSet[SensPlaceDetectability[M,senspos]];
isolSS=If[Length[detSS]>0,
(*Join@@(ExtendSS[MinimalHittingSet[SensPlaceInDetectable[AddSensors[M,#,Pf],senspos,freq]],#]&/@detSS)*)
Join@@(SensPlace2[M,senspos,freq2,#,Pf]&/@detSS),
MinimalHittingSet[SensPlaceInDetectable[M,senspos,freq2]]];
Return[SimplifySP[isolSS]];
];

SensPlace2[M_LinMod,svar_,freq_,detSS_,Pf_]:=Module[{M2,newfreq},
M2=AddSensors[M,detSS,Pf];
newfreq=Join[freq,SetDiff[GetFNames[M2],Flatten[freq]]];
Return[ExtendSS[MinimalHittingSet[SensPlaceInDetectable[M2,svar,newfreq]],detSS]]];

ExtendSS[MHS_,sens_]:=
If[Length[MHS]==0,{sens},UnionMS[sens,#]&/@MHS];

DecoupleFaults

DecoupleFaults::usage="DecoupleFaults[model,faults]";
DecoupleFaults[M_LinMod,f_]:=Module[{Mnew,dpos},
If[Length[f]==0,Return[M],
Mnew=M;
dpos=Flatten[Position[GetFNames[M],#]&/@f];
Mnew[[2]]=DropColumns[GetF[M],dpos];
Mnew[[1]]=AddColumns[GetH[M],GetColumns[GetF[M],dpos]];
Mnew[[5]]=Join[GetXNames[M],f];
Mnew[[6]]=Delete[GetFNames[M],{#}&/@dpos];
Return[Mnew]];
];

SensPlaceInDetectable

(*SensPlaceInDetectable::usage="SensPlaceInDetectable[Model(,possible sensor locations,isolability specification)]";*)
SensPlaceInDetectable[M_LinMod]:=SensPlaceInDetectable[M,GetXNames[M],{}];
SensPlaceInDetectable[M_LinMod,svar_]:=SensPlaceInDetectable[M,svar,{}];

(*SensPlaceInDetectable[M_LinMod,svar_,freq_]:=Module[{fspec},
fspec=If[Length[freq]==0,GetFNames[M],freq];
fspec=If[Head[#]===List,#,{#}]&/@fspec;
Return[Join@@(SensPlaceDetectability[DecoupleFaults[M,#],svar]&/@fspec)];
];*)

SensPlaceInDetectable[M_LinMod,svar_,freq_]:=Module[{fspec,nf},
fspec=If[Length[freq]==0,GetFNames[M],freq];
fspec=If[Head[#]===List,#,{#}]&/@fspec;
nf=Length[fspec];
Return[Join@@(SensPlaceInDetectable2[M,svar,fspec[[#]],Flatten[Take[fspec,-nf+#]]]&/@Array[#&,nf-1])];
(*Return[Join@@(SensPlaceDetectability[DecoupleFaults[M,#],svar]&/@fspec)];*)
];

SensPlaceInDetectable2[M_,svar_,decfaults_,detfaults_]:=
Join@@(SensPlaceDetectability[DecoupleFaults[M,{#}],svar,detfaults]&/@decfaults);

SensPlaceDetectability

SensPlaceDetectability::usage="Performs sensor placement analysis to meet a given detectability  specification.
Syntax: SensPlaceDetectability[M,sensloc,faults]

Input: 
M        - Model object
sensloc  - List of possible sensor locations (optional). If not provided it is assumed that all variables in the model are possible sensor locations.
faults - Set fo faults to be detected (optional). If not provided,  it is assumed that full isolability is specified.

Output: Set of detectability sets (Not sensor sets!).";
SensPlaceDetectability[M_LinMod]:=SensPlaceDetectability[M,GetXNames[M]];
SensPlaceDetectability[M_LinMod,P_,Fdet_]:=Module[{Mnew},
Mnew=M;
Mnew[[2]]=GetColumns[GetF[M],Flatten[Position[GetFNames[M],#]&/@Fdet]];
Mnew[[6]]=Fdet;
Return[SensPlaceDetectability[Mnew,P]];
];
SensPlaceDetectability[M_LinMod,P_]:=Module[{H,F,X,bxi,DS,ndet},
H=GetH[M];
ndet=Flatten[Position[DetectabilityAnalysis[M],False]];
If[Length[ndet]==0,Return[{}]];
F=GetColumns[GetF[M],ndet];
X=Simplify[LinearSolve[H,F]];
bxi = Transpose[Not[SameQ[##,0]]&/@#&/@X];
DS=(GetXNames[M][[##]]&/@Flatten[Position[#,True]])&/@bxi;
DS=#⋂P&/@DS;
DS=SimplifySP[Delete[DS,Position[DS,{}]]];
Return[DS];
];

AddSensors

AddSensors::usage="Add sensors to a given model. 
Input:
  sensors - set of sensor positions
  faultypos - Set of sensor positions that, when added, may become faulty. If set to 0 or 1, none or all sensors are assumed to be subjected to faults.(optional).

AddSensors[model, sensors(,faultypos)]";
AddSensors[M_LinMod,s_]:=AddSensors[M,s,{}];
AddSensors[M_LinMod,s_,n_Integer]:=If[n==1,AddSensors[M,s,GetXNames[M]],AddSensors[M,s,{}]];
AddSensors[M_LinMod,s_, Pf_]:=Module[{pos,H,F,L,Fadd,Hadd,Ladd,ns,nx,nf,nz,ne,k,modelname,fsenspos,nfnew,fnameadd},
ns=Length[s];
H=GetH[M];
L=GetL[M];
F=GetF[M];
modelname=GetModelName[M];
ne=Length[H];
nx=Length[H[[1]]];
nf=Length[F[[1]]];
nz=Length[L[[1]]];
pos=FindPos[s,GetXNames[M]];
Hadd=Array[0&,{ns,nx}];
For[k=1,k≤ns,k++,
Hadd[[k,pos[[k]]]]=1;
];
L=AddRows[AddColumns[L,Array[0&,{ne,ns}]],
AddColumns[Array[0&,{ns,nz}],-IdentityMatrix[ns]]];
H=AddRows[H,Hadd];
fsenspos=Position[MemberQ[Pf,#]&/@s,True];
nfnew=Length[fsenspos];
Fadd=Array[0&,{ns,Length[fsenspos]}];
For[k=1,k≤Length[fsenspos],k++,
Fadd[[fsenspos[[k,1]],k]]=1];
F=AddColumns[F,Array[0&,{ne,nfnew}]];
F=AddRows[F,AddColumns[Array[0&,{ns,nf}],Fadd]];
fnameadd=fz[#[[1]]+nz]&/@fsenspos;
Return[CreateLinModel[H,F,L,Join[GetRelNames[M],Array[e[#+ne]&,ns]],
GetXNames[M],
Join[GetFNames[M],fnameadd],
Join[GetZNames[M],Array[z[#+nz]&,ns]],
modelname]]];

DetectabilityAnalysis

DetectabilityAnalysis::usage="Returns a list of True/False corresponding to if the faults in the model is detectable or not.

DetectabilityAnalysis[model]";
DetectabilityAnalysis[M_LinMod]:=Module[{H,F},
H=GetH[M];
F=GetF[M];
Return[DetectabilityAnalysis[H,Transpose[{#}]]&/@Transpose[F]];
];
DetectabilityAnalysis[H_,Fi_]:=MatrixRank[AddColumns[H,Fi]]>MatrixRank[H];

MinimalHittingSet

MinimalHittingSet::usage="MinimalHittingSet[family of sets]";
MinimalHittingSet[FS_]:=MinimalHittingSet[FS,{}];
MinimalHittingSet[FS_,HS_]:=Module[{newHS,c,invalidated,extcand},
If[Length[FS]==0,
Return[HS],
c=First[FS];
If[Length[HS]==0,
newHS={#}&/@c,
invalidated=Position[Length[Intersection[c,#]]==0&/@HS,True];
extcand=Flatten[ExtendCandidate[#,c]&/@Extract[HS,invalidated],1];
newHS =SimplifySP[Delete[HS,invalidated]⋃extcand];
];
Return[MinimalHittingSet[Drop[FS,1],newHS]];
];
];
ExtendCandidate[c_,s_]:=
c⋃{#}&/@s;

Simplify detectability sets

SimplifySP::usage="SimplifySP[set of detectability sets]";
SimplifySP[DS_]:=
Module[{sDS,rem,k,l},
sDS=Sort[DS];
rem={};
For[k=1,k≤Length[sDS],k++,
For[l=k+1,l≤Length[sDS],l++,
rem=rem⋃If[SubsetQMS[sDS[[k]],sDS[[l]]],{{l}},{}];
];
];
Return[Delete[sDS,rem]];
];

Set operations

SubsetQ[v1_,v2_]:=Length[Complement[v1,v2]]==0;

SetDiff[v1_,v2_]:=
v1⋂Complement[v1,v1⋂v2];

SubsetQMS[s1_,s2_]:=Module[{v1},
v1=Variables[s1];
Return[If[Length[Select[(Count[FindPos[s1,{#}],1]&/@v1)-
(Count[FindPos[s2,{#}],1]&/@v1),#>0&]]>0,
False,
True]]];

UnionMS[s1_,s2_]:=Module[{v,n},
v=Variables[s1]⋃Variables[s2];
n=(Count[FindPos[s1,{#}],1]&/@v)+
(Count[FindPos[s2,{#}],1]&/@v);
Return[Join@@Function[k,Array[v[[k]]&,n[[k]]]]/@(Array[#&,Length[v]])];
];

FindPos[v_,l_]:=
Flatten[If[MemberQ[l,#],Position[l,#],{-1}]&/@v];

Constructors

CreateLinModel::usage="There are two possible ways to create a model object. Either direct specification of model matrices H,F, and L or by supplying a system of Mathematica equations Meq. Variablenames, equation names, and modelname are optional.

CreateLinModel[H,F,L(,relnames,xnames,fnames,znames,modelname)]
CreateLinModel[Meq,xnames,fnames,znames,modelname]";
GetH::usage="Get matrix H from a model; GetH[model]";
GetF::usage="Get matrix F from a model; GetF[model]";
GetL::usage="Get matrix L from a model; GetL[model]";
GetRelNames::usage="Get equation names from a model; GetRelNames[model]";
GetXNames::usage="Get names of unknown variables in a model; GetXNames[model]";
GetFNames::usage="Get names of fault variables in a model; GetFNames[model]";
GetZNames::usage="Get names of known variables in a model; GetZNames[model]";
GetModelName::usage="Get name of model; GetModelName[model]";
SetRelNames::usage="Set names of equations; SetRelNames[model,relnames]";
SetXNames::usage="Set names of unknown variables; SetXNames[model,xnames]";
SetFNames::usage="Set names of fault variables; SetFNames[model,fnames]";
SetZNames::usage="Set names of known variables; SetZNames[model,znames]";
SetModelName::usage="Set name of model; SetModelName[model,name]";
GetEquations::usage="Get the model equations; GetEquations[model]";

CreateLinModel[H_,F_,L_,relnames_,xnames_,fnames_,znames_,modelname_]:=Module[{rn,xn,fn,zn,ne,nx,nf,nz},
ne = Length[H];
nx = Length[H[[1]]];
nf = Length[F[[1]]];
nz=Length[L[[1]]];
If[MatrixRank[H]<nx,Print["Warning: Solution to model equations is not well defined"]];
rn=If[Length[relnames]≠ne,Array[e,ne],relnames];
xn=If[Length[xnames]≠nx,Array[x,nx],xnames];
fn=If[Length[fnames]≠nf,Array[f,nf],fnames];
zn=If[Length[znames]≠nz,Array[z,nz],znames];
Return[LinMod[H,F,L,rn,xn,fn,zn,modelname]]];
CreateLinModel[H_,F_,L_,relnames_,xnames_,fnames_,znames_]:=
CreateLinModel[H,F,L,relnames,xnames,fnames,znames,""];
CreateLinModel[H_,F_,L_]:=CreateLinModel[H,F,L,{},{},{},{},""];

CreateLinModel[Meq_,xvars_,fvars_,zvars_,modelname_]:=Module[{H,F,L,Meqt},
Meqt=(Meq/.{Derivative[n_][x_][t]->s^n x[t],Derivative[n_][x_]-> s^n x})/.{Equal[a_,b_]->ExpandAll[a-b]};
H=Coefficient[#,xvars]&/@Meqt;
F=Coefficient[#,fvars]&/@Meqt;
L=Coefficient[#,zvars]&/@Meqt;
Return[CreateLinModel[H,F,L,{},xvars,fvars,zvars,modelname]];
];

GetH[M_LinMod]:=M[[1]];
GetF[M_LinMod]:=M[[2]];
GetL[M_LinMod]:=M[[3]];
GetRelNames[M_LinMod]:=M[[4]];
GetXNames[M_LinMod]:=M[[5]];
GetFNames[M_LinMod]:=M[[6]];
GetZNames[M_LinMod]:=M[[7]];
GetModelName[M_LinMod]:=M[[8]];
SetModelName[M_LinMod,modelname_]:=Module[{Mnew},
Mnew=M;
Mnew[[8]]=modelname;
Return[Mnew];
];
SetRelNames[M_LinMod,relnames_]:=Module[{Mnew},
Mnew=M;
Mnew[[4]]=relnames;
Return[Mnew];
];
SetXNames[M_LinMod,xnames_]:=Module[{Mnew},
Mnew=M;
Mnew[[5]]=xnames;
Return[Mnew];
];
SetFNames[M_LinMod,fnames_]:=Module[{Mnew},
Mnew=M;
Mnew[[6]]=fnames;
Return[Mnew];
];
SetZNames[M_LinMod,znames_]:=Module[{Mnew},
Mnew=M;
Mnew[[7]]=znames;
Return[Mnew];
];
GetEquations[M_LinMod]:=#==0&/@(GetH[M].GetXNames[M]+GetL[M].GetZNames[M]+GetF[M].GetFNames[M]);

Model simplification

SimplifyLinModel::usage=
"Simplifies model for use in sensor placement analysis.
Warning: Experimental! 

If possible sensor locations is limited to a subset of the unknown
variables, the model equations may be simplified to obtain a 
simpler model description.

Syntax: SimplifyLinModel[M, sensloc]
Input: 
M        - Model object
sensloc  - List of possible sensor locations

Output: A new simplified model object";

SimplifyLinModel[M_LinMod,svar_List]:=Module[{H,F,L,rn,xn,fn,zn,name,h1pos,h2pos,H1,H2,Nh1},
H=GetH[M];
L=GetL[M];
F=GetF[M];
h2pos=Flatten[Position[GetXNames[M],#]&/@svar];
h1pos=SetDiff[Array[#&,{Length[GetXNames[M]]}],h2pos];
If[Length[h1pos]==0,Return[M],
H1=Transpose[Transpose[H][[h1pos]]];
H2=Transpose[Transpose[H][[h2pos]]];
Nh1=NullSpace[Transpose[H1]];
Return[CreateLinModel[Simplify[Nh1.H2],Simplify[Nh1.F],Simplify[Nh1.L],{},Extract[GetXNames[M],{#}&/@h2pos],GetFNames[M],GetZNames[M],GetModelName[M]]]]];

Display function

Format[LinMod[H_,F_,L_,rn_,xn_,fn_,zn_,modelname_]]:=
If[StringLength[modelname]>0,
"Model: "<>modelname <>"\n",
"Linear model with "]<> ToString[Length[H]]<> " equation(s), "<> ToString[Length[H[[1]]]]<> " unknown(s), "<> ToString[Length[F[[1]]]]<> " fault(s), and "<> ToString[Length[L[[1]]]]<> " known variable(s).";

Matrix operations

AddRows[M1_,M2_]:=Join@@{M1,M2};
AddColumns[M1_,M2_]:=Transpose[AddRows[Transpose[M1],Transpose[M2]]];

GetColumns[M_,n_Integer]:=Transpose[{Transpose[M][[n]]}];
GetColumns[M_,n_List]:=
Transpose[Extract[Transpose[M],
{#}&/@n]];

DropColumns[M_,n_Integer]:=DropColumns[M,{n}];
DropColumns[M_,n_List]:=Transpose[Delete[Transpose[M],
{#}&/@n]];

Function Reference

This a brief functional reference where all user functions are included. See the example section below for examples on how they can be used. In addition to these user functions there are a number of internal functions that may be useful. See source code for information about these functions. 

Help for a particular user function can be accessed by, for example:

?SensPlaceIsolability

Model creation and simplification functions

CreateLinModel, SimplifyLinModel, GetH, GetF, GetL, GetRelNames, GetXNames, GetFNames, GetZNames, GetModelName, SetModelName, SetRelNames, SetXNames, SetFNames, SetZNames, GetEquations.

The model is represented in the general differential-algebraic form

H (p)x(t)+L(p)z(t)+F(p)f(t)=0

where x(t), z(t), and f(t) are unknown, known, and fault-variables respectively. Matrices H(p),  L(p), and F(p) are polynomial matrices in the differentiation operator p. Note that the software only handles linear systems.

Sensor placement analysis functions

There are two sensor placement analysis functions; SensPlaceIsolability who finds all minimal sensor sets that achieves a certain isolability performance and SensPlaceDetectability that achieves a specified detectability performance. The function MinimalHittingSet computes, from a familty of detectability sets, the set of minimal sensor sets using a basic minimal hitting set algorithm.

Model analysis functions

The function IsolabilityAnalysis gives the fault isolability properties of a given model, DetectabilityAnalysis the detectability properties,  and AddSensors makes it possible to add sensors to a given model

Simple example

Small 5:th order academic example used to illustrate some basic principles. 

First, define the model equations and create the model object

Meq={(Subscript[x, 1])'==-(Subscript[x, 1])+(Subscript[x, 2])+(Subscript[x, 5]),
(Subscript[x, 2])'==-2 (Subscript[x, 2])+(Subscript[x, 3])+(Subscript[x, 4]),
(Subscript[x, 3])'==-3 (Subscript[x, 3])+(Subscript[x, 5])+(Subscript[f, 1])+(Subscript[f, 2]),
(Subscript[x, 4])'== -4 (Subscript[x, 4])+(Subscript[x, 5])+(Subscript[f, 3]),
(Subscript[x, 5])'== -5 (Subscript[x, 5])+u+(Subscript[f, 4])};
xvars={(Subscript[x, 1]),(Subscript[x, 2]),(Subscript[x, 3]),(Subscript[x, 4]),(Subscript[x, 5])};
fvars={(Subscript[f, 1]),(Subscript[f, 2]),(Subscript[f, 3]),(Subscript[f, 4])};
zvars={u};
M=CreateLinModel[Meq,xvars,fvars,zvars,"Small ODE example"]

Extract some model properties

GetH[M]//MatrixForm
GetXNames[M]

Compute sensor set that achieves detectability of all faults. Assume that all unknown variables are possible sensor locations.

DS=SensPlaceDetectability[M]
SS=MinimalHittingSet[DS]

Add the second sensor set {(Subscript[x, 2])} and perform isolability analysis to see the resulting isolability properties.

M2=AddSensors[M,SS[[2]]];
IsolabilityAnalysis[M2]

All faults are detectable but it is not possible to isolate them from each other. Now, assume we want maximum isolability when all unknown variables are possible sensor locations. First, define the set of sensor locations

svar=GetXNames[M];

Perform the sensor placement analysis

SS=SensPlaceIsolability[M,svar]

Five possible solutions. Add the second solution and verify the isolability properties.

IsolabilityAnalysis[AddSensors[M,SS[[2]]]]

Example from K. Murota

This example is a passive R/L/C,  electrical circuit taken from the book "Matrices and Matroids for System Analysis", Springer-Verlag, 2000. A schematic of the circuit and a description of the example is included in the IFAC paper.

System definition

Define model equations and create the model object.

Meq={
u1==z1+fgen,
u2==R1 i2+fR1,
u3==R2 i3+fR2,
u1==u5,
u5==u2+u3,
u3==u4,
i1==i2+i5,
i1==i3+i4+i5,
u4 == L i4'+fL,
i5==C u5'+fC};
xnames={i1,i2,i3,i4,i5,u1,u2,u3,u4,u5};
znames={z1};
fnames={fgen,fR1,fR2,fL,fC};
M=CreateLinModel[Meq,xnames,fnames,znames,"Murota model 1"]

Sensor placement for detectability

Determine sensors to add to obtain detectability of all faults. All unknown variables are assumed to be possible sensor locations.

DSdeta1=SensPlaceDetectability[M];
SSdeta1=MinimalHittingSet[DSdeta1]

Sensor placement for isolability

Sensor placement analysis for maximum isolability.

svar=xnames;
SSisola1=SensPlaceIsolability[M,svar]

Add first solution and compute the isolability properties of the model.

IsolabilityAnalysis[AddSensors[M,SSisola1[[1]]]]

Now, assume that the sensors may fail and we want to isolate also these faults. The objective is maximum isolability.

Pf=1;
isolspec = {fR1,fC,fgen,fL,fR2};
ss=SensPlaceIsolability[M,svar,isolspec,Pf]
IsolabilityAnalysis[AddSensors[M,ss[[1]],Pf]]

Second example again, now assume that only voltage measurements are possible.

Pf=1;
svar={u1,u2,u3,u4,u5};
ss=SensPlaceIsolability[M,svar,{fR1,fC,fgen,fL,fR2},Pf]
IsolabilityAnalysis[AddSensors[M,ss[[1]],Pf]]

Fourth run; no measurement restriction, no sensor faults, and a given isolability specification where we want to isolate fgen and fC completely from all other faults, but need not separate the faults in the resistors or inductor fR1, fR2, och fL.

svar=xnames;
ss=SensPlaceIsolability[M,svar,{fgen,{fR1,fR2,fL},fC}]
IsolabilityAnalysis[AddSensors[M,ss[[1]]]]