Earlier today, I read this very interesting blog by Jerome (go read it!), and it got me thinking about how else the suggested methods could be useful. With Jerome Lefebvre's method, we can get counts of child elements in particular states without adding a bunch of extra attributes to the child elements, but I have often heard a couple of questions that extend this idea:

  1. How can I get a sum of attribute x values (e.g., volumes, power, etc.) for child elements that are in a particular state indicated by attribute y (e.g., status, product name, etc.)? The conditional or filtered rollup.
  2. How can I get a list of child elements that are in a particular state, not just a count, into an attribute value for visualization or reporting?

With these two questions in mind, I jumped into PSE and started experimenting. In the past, my testing with arrays in analyses was often stymied by the lack of functions for arrays (e.g., being able to add or multiply corresponding values in two arrays or perform a dot product). Jerome’s method of using an index array helped unlock that for me.


Below are some approaches that can answer the two questions posed above, but let me say up front, Jerome and I both tested our methods on a small number of elements (four), and in my case, only triggering once per minute. My guess is these array-based methods aren’t as efficient as normal rollup analyses, so test carefully before implementing, especially before scaling out to large numbers of elements.


Question1:

Let’s start at the end first and then go about explaining it:

  • Lines 1-5 are explained in Jerome’s blog (really, go read it!).
  • Line 6 is used to check which elements are in the Auto state and map the True/False result to a 1/0.
  • Line 7 is where the magic really happens; using the index array (Line 3), we go through each entry in the IsAuto 1/0 array (line 6) and multiply it by the corresponding entry in the attribute value array (line 4); basically, we are zeroing out the values coming from child elements that aren’t in the Auto state. Note: this isn’t a fully original idea as it expands upon another interesting blog from Jerome (I know; the guy is good).
  • Line 8 is where we get our desired final value by applying the Total function to sum up the values in the array from Line 7.

To help illustrate, here is a numeric example (orange font shows how the filtering for Auto gets applied):

vChildCount [1]

4

vLastNVals [2]

[96.37,99.157,86.398,18.173]

v1ToN [3]

[1,2,3,4]

vChildVals [4]

[78.976,78.976,78.976,78.976]

vModes [5]

[Cascade,Prog-Auto,Auto,Auto]

vAutoTrue

[0,0,1,1]

vAutoValues [7]

[0,0,78.976,78.976]

vAutoValSum

157.95


Notes: Like in Jerome’s rollup example, the same final result can be achieved by adding additional attributes to the child elements. For this example, analysis attributes that use an expression like:

If ‘Mode’ = “Auto” Then ‘Tag Value’ Else 0

would be appropriate. These “filtered” attributes could then be summed at the parent level with a rollup analysis. But as Jerome mentioned, this starts to add up when you want to do this for multiple states.

Question 2:

Again, let’s start at the end:

  • Lines 1-7 are affectively the same, the main differences being:
    • Line 5 grabs the values of string builder attributes holding the names of the child elements instead of numeric attributes that we want to sum.
    • Line 7 is much the same calculation as before, but here we return an array that holds the indices of elements in the state Auto and a zero otherwise.
  • Line 8 trims the array to get rid of the zero values so only the indices of the Auto elements remain.
  • Line 9 applies the array of auto element indices (Line 8) to the array of element names (Line 5) to return an array that only holds the names of elements in the Auto state.
  • Line 10 converts the array of names (Line 9) to a string value so it can be written to an output attribute (some checking for empty arrays is also included).

The result data for two examples look like the following:

Run1

Run2

vChildCount [1]

4

4

vLastNVals [2]

[24.223,3.7947,0.76475,14.358]

[24.223,3.7947,0.76475,14.358]

v1ToN [3]

[1,2,3,4]

[1,2,3,4]

vChildNames [4]

[Element1,Element2,Element3,Element4]

[Element1,Element2,Element3,Element4]

vModes [5]

[Prog-Auto,Auto,Program,Auto]

[Cascade,Cascade,Manual,Prog-Auto]

vAutoTrue

[0,1,0,1]

[0,0,0,0]

vAutoIndex [7]

[0,2,0,4]

[0,0,0,0]

vAutoIndFilt

[2,4]

[]

vChildInAuto [9]

[Element2,Element4]

[]

vChildInAutoStr [10]

[Element2, Element4]

None


Notes: I can’t think of way to accomplish this with rollup analyses since they can’t act on strings, but for visualization purposes, you could produce similar lists, with no need for additional attributes or analyses in AF, in PI Vision and PI DataLink by using Collections with attribute filters and the Asset Filter Search, respectively.

I could see this approach being modified to address the “tell me which element is producing the rollup min/max value” use case as well, but I haven’t had a chance to test it.


Note about both cases: scheduling can be a bit tricky if you want event triggered scheduling since you can’t trigger on the arrays themselves. You’ll have to get one or more input attributes that update at the desired times/frequency into variables in the analysis.


Well, if you got this far, I hope you think it was worth it. If you have any comments or ideas or come up with your own use case/extension, please post below, and as always, thanks for your time!

  • [Mention:0058W000008ZPTbQAO]​, glad it was helpful! If you have further questions, please reply back. Also, if you can share your final solution, that would be great!

  • Thanks [Mention:0051I000000cUuLQAU]​ . This is really good

     

     

    What if the interested string attribute is available as the child of multiple attributes of multiple child elements?

    e.g:

    Parent Element (Want to know how many attributes of all of my child compressor elements is having "Fault" as the mode value)

     

    • Child Element 1 (Template= Compressor)
      • Attribute 1 (Attribute Category=Compressor)
        • Mode
      • Attribute 2 (Attribute Category=Compressor)
        • Mode
    • Child Element 2 (Template= Compressor)
      • Attribute 1 (Attribute Category=Compressor)
        • Mode
      • Attribute 2 (Attribute Category=Compressor)
        • Mode

     

     

     

  • Thanks for the detailed response, @bbregenzer. Really appreciate it.

    In my case, the child element counts can vary and also it can have other elements other than compressors. Also, the attribute counts can vary too in some cases but they all will have the same attribute categories. Also, our Fault code attribute is set as an enumeration value. however, we have total of 5 fault types that we want to count. So, I was looking for a smarter way to roll up the fault counts instead of creating 5 child attributes.

     

    I was able to use the array-based logic to define two types of rollups.

     

    1. In case of single element in the hierarchy:

    <the following "Flare" element only has two attributes>


    image.png_b3563233-fa82-4f0f-87d9-62670a07b3b9.png
    2. if multiple elements are available of the same type.

    <we have 4 compressors elements with 7 attributes each: using indices to loop through element and index to narrow down to attribute>


    image.png_69dc58b2-84f6-4419-892c-f8f99be172fa.png
     

    the above solution works since i have define the indices as an int array, consisting of, numbers from 1 to 15. This will make sure it can loop through at least top 15 elements to find the particular equipment type of my interest and can be also used to loop through at least 15 attributes in case of single occurrence of the element.

     

    I couldn't optimize it further for multiple occurrences of the same element type earlier. but after seeing your response, i think I should be able to reduce few more steps.

     

    Thanks for the help.

     

    Regards,

    Sahil

  • [Mention:0058W000008ZPTbQAO]​, thanks for the question!

    You can see below for ways I think you can adjust my approach to fit your use case, but before you do something this complex, a quick question.

    Are the Mode child attributes using an enum set?

    If so, it would probably be easier to add another child attribute that uses a Formula to remap this to a 0-1 value (with 1 indicating fault), and then use the standard rollup analysis to add them up. The rollup analysis has filters for element template and attribute category and can access child level attributes (switch the dropdown for Attribute Level from Root Level to Child Level) out of the box. This will make the selection of applicable child elements and their attributes of interest dynamic, which is much more difficult with the expression/array approach. Once you have the rollup filters dialed in to get the attributes you want, select Sum as the function type, and you will get a count of the number of faults across all compressor child elements and all of their Mode child attributes.

    If you don't think this out of the box approach will work for your use case, then read on below.

    The only way I can think to do this is to have a compound index that enumerates the child elements and their attributes from a given category. Using your example, you have two child elements and each element has two child attributes of interest (here I assume there are no other attributes in the "Compressor" attribute category), so the compound Index Array would look like this: [11 12 21 22]. This array would sub into the v1toN variable in my example. There may be some clever algorithm to build this dynamically like we did for the simple 1-to-N index array, but I can't come up with one off the top of my head, so I would just add an attribute called Index Array that has data type Int16 Array and build it by hand to match the number of child elements and their attributes of interest. Assuming the number of child elements doesn't change very much over time, this should be a minor configuration task.

    Next, this compound index needs to be split to enumerate the elements and their attributes for the MapData step in the variable vChildModes (we don't need the vChildNames variable for this example since we only want to count the number of modes in the "Fault" state). The path and filter syntax used in my example needs to be expanded to not just use the index of the child elements but also the index of the attributes of each element. Something like this should work:

    MapData('Index Array',TagVal(TagNum(Concat(".\[@index=",Left(String($val),1),"]|[@Category=Compressor][",Right(String($val),1),"]|Mode"))))

    Here, it's assumed that the only child elements are the ones of interest, if you have other types of child elements that aren't compressors, then you can specify with an additional filter; for example:

    MapData('Index Array',TagVal(TagNum(Concat(".\[@Template=Compressor][",Left(String($val),1),"]|[@Category=Compressor][",Right(String($val),1),"]|Mode"))))

    For more on the syntax, see Path syntax for filters.

    The above MapData will give an array with the values of the Mode attributes ("Fault", "Normal", or whatever other values your Mode attributes can have). From there you can use the approach in variable vAutoTrue, changing "Auto" to "Fault", to get an array of 0s and 1s, with 1s representing Faults. Finally, you can run the Total function on this array to get the total count of the number of Faults.

    Please let me know what you think of this and if you have any further questions.