Choose Language Hide Translation Bar

Getting the Most from Repairable Systems Simulation (RSS): Going Beyond the Documentation (2022-US-45MP-1132)

Repairable systems can be complex. The RSS platform offers several lesser-known or undocumented features that can help users build simulations more closely aligned to how they are running a system. Did you know block replacement can be contingent on its age or the number of times it has failed? Or that maintenance schedules can be based on system state, making it possible to skip maintenance if it would bring the system down? Using an example-based approach, this session will illustrate useful features and functionality not easily found or missing from the documentation and JMP User Community. Topics include getting, setting, and employing system information as well as applications for less commonly used Events and Actions. A JMP Journal with all examples will be available.

 

 

Thanks for taking some time out, watch this video.

I'll assume that you're here because you've used or tried to use

the Repairable System Simulation platform within JMP.

You might want to learn a little bit more.

You might get be frustrated because there's things

that you're trying to do that you didn't think you can do.

So what I'm going to cover today

are things that aren't explained deeply in the documentation,

things that I've learned through trial and error

that aren't in the documentation,

and even stuff that I've learned from talking to the developer

about the platform, stuff that you wouldn't be able to find

any other way.

I'm going to assume that you've had some exposure to this platform.

I'm going to assume that you know a little bit about reliability analysis,

so I don't have to go into detail in terms of what is a parallel setup,

what is something that's K out of N, and so on.

So I've got a number of examples I want to cover,

a lot of materials. Let's get started.

Now, before we actually look at

the Repairable System Simulation platform, let me just give you an overview.

Hopefully, you've used it before, you're familiar with it,

we're going to talk about three different components

and aspects about those components that aren't in the documentation

or aren't well-explained in the documentation.

The first thing we're going to talk about are blocks.

Blocks are what I use to build my Repairable System Simulation things

like Parallel, K out of N, Standby.

So for each block, I can associate an event with that block.

So those are these little orange blocks that we see, and for each block,

I can associate one or more outcomes or actions.

Those are those blue dots that we see,

and that's going to help me build up my simulation.

For example, I might want to know when a block fails

and when that block fails, I might want to repair that block,

and that repair might take, let's say, 40 hours.

That's the idea behind building these RSS diagrams.

Let's start with talking about

some of the general properties of the blocks.

There are six different blocks in a knot,

which allows me to tie things together within the diagram.

Five of those six blocks are composites.

They were built to be made of two or more subunits.

JMP allows you to use them with one subunit.

But really, the use case is that they are built up of two or more subunits.

Events and Actions are applied to the entire block.

So this is an important point.

The components within the block can't be treated individually,

except for the standby block.

They're assigned a single distribution.

Taken together, these two bullet points mean that things like repair

or maintenance on subunits is not possible

within these composite blocks.

I've got to wait until the entire block to fail

before I can actually do anything with that block

before I can repair the block.

If I want different distributions within the block,

I can't do that either, with the exception of the Standby.

And we'll talk about that in an example we have a little bit later on.

Subunits for all the composite blocks,

except for the Series blocks, are in parallel.

The thing that differentiates those different composite blocks

are the number of components that are running,

when the block fails, and how the block ages.

So I put together a little table that goes over all of the composite blocks

and how they differ with those characteristics.

For all the composite blocks, with the exception of Standby,

all the components are running simultaneously.

With a composite block, I can pick K of my N units to be running

and then the remaining units to be backups.

So when do these blocks fail?

Obviously, if you have any experience in reliability, a Series block fails

when one unit fails.

A Parallel block fails when all the units fail,

K out of N when you have fewer than K units, and so on.

So things that you might expect naturally in reliability analysis,

Standby and Sharing have another method of failure.

With the standby unit, there is a switching mechanism.

There could be one switch for the entire block

or one switch for each backup.

The Standby can fail if I have a single switch

when that switch fails, or if I attempt to use

each one of my backup units

and the switch on those backup units fail as well.

So switch failure could be another cause for the block failing.

Same thing goes with Stress Sharing.

However, with Stress Sharing, I only have a single switch.

The final characteristic that's different

between these composite blocks is how they age.

For the first three blocks, they age equally.

All the subunits within the block age equally.

For Standby and Stress Sharing, the subunits can age...

For Standby, they can age differently.

For Stress Sharing, they definitely age differently.

For Standby,

I get to pick a single distribution for my operating units.

For my backup units,

I can choose that they don't age— that's cold standby—

or I can give them a different distribution

as to how they age when they're in standby mode

or when they're acting as backups.

That could be the same as my operating units could be something different.

So I get to pick two different distributions.

The thing is, again,

as with all these composite blocks, I get one distribution

for all my operating units, one distribution for all my backups.

I can't mix and match.

Finally, with Stress Sharing, Stress Sharing works

a little bit differently.

With Stress Sharing, the stress is distributed among the blocks equally.

However, when one of the subunits fails,

additional stress is associated with the remaining operating units.

So the stress changes from failure to failure.

We'll see an example of that in a little bit.

Now, one of the things that you might have found,

or you might realize is that sometimes

you want to have a little bit more flexibility

and the way I can build flexibility is I can arrange my basic blocks

or my basic block as I would a Parallel or K out of N

as one of my composite blocks.

Basic blocks can be easily arranged like Series, Parallel, or K out of N

harder to do with the Standby and Stress Sharing blocks

when you've got more than two units.

I've got an example in a little bit

that shows a Standby setup with two basic blocks.

Relatively straightforward how to do that.

But if I were to try to extend that to three blocks,

it becomes much more difficult.

Not impossible, just very difficult to do

because I'd have to keep track of what's operating, what's not operating,

what happens when the failure occurs and so on.

Same thing goes with the Stress Sharing as well.

Let's go to our first example.

For our first example, we're going to look at a basic K out of N arrangement.

We're going to have five subunits.

Three of them need to be operational.

They're all going to have the Weibull with an Alpha of 2,000, Beta of 1.

We're going to assume that when a block fails

that it takes 40 hours to replace.

We're going to put that arrangement

in series with a K out of N composite block.

We'll also assume that the K out of N composite block takes 40 hours

to replace, despite the fact that there are five subunits

within that block that have failed.

We'll take a look at what difference does it make

whether I treat these as individual items

or as my composite item?

This is what my setup would look like in RSS.

I've kept it very simple.

Here are my subunits relative to my K out of N.

What makes it a K out of N is in the knot.

I can specify how many of the incoming items

need to be operating.

And here I've said I need three operating. So that's what makes it a K out of N,

this arrangement, K out of N.

And I'm just going to keep it very simple.

The only event I'm going to look at is whether or not the block fails,

the only action I'm going to take is to repair as new.

Let's go ahead and run this.

Relatively fast.

I want to spend a little bit talking about the output

because what gets sent to the output and the states

will help us when we start building our diagrams in the future.

Let's just take a look at the order of operations

for this very first failure.

So we see it's 766 hours unit Sub1 has failed.

The block is removed.

So that's important to note because that is one of my potential events.

So block is removed, I start the process, I replace the block.

And after the block is replaced, after the 40 hours I specified

in my setup, the system is turned on automatically.

So that's another important point.

We're going to run into the situation

where for some of my events, the system doesn't get

turned on automatically, and I'm going to talk about which ones those are.

What we'll have to do in those cases

is actually turn the system on, or in some cases, when the block

doesn't get turned on automatically, turn the block on manually.

Here we've got Sub3 fail and you see a similar series of events,

Sub2 fails, etc. Let's take a look at what happens

when the system goes down.

And we know that happens for sure when K out of N fails.

Okay, so here's the first case where the system fails.

Again, very similar in respect to most of the operations,

but you'll notice that it turns the system down.

Notice it turned the system down, so I get to turn system down.

It turns off all of the blocks in the system.

Again, something important to keep in mind,

because there are going to be cases where we need to know

whether or not a block is on and off.

Whether or not we need to turn on a block manually.

So here we go to the replacement.

Once the replacement is finished,

all the blocks are turned on automatically.

And again with this replace, with new,

the turning off and turning on all happens automatically.

So I don't have to worry about that.

Finally, I finished my K out N replacement and I'm done until the next failure.

Relatively straightforward.

If I wanted to take a look at how my system has performed

I'm just going to launch my Result Explorer.

By default, I am not going to see the component level information.

If I want the component level information, I need to go back up to the hotspot

and pick if I want an overall,

I'm going to pick my downtime by component.

Now, the thing to keep in mind with this organization,

this is just telling me

the downtime of the system associated with my components.

So this tells me how many times K out of N has taken the system down.

For each one of these,

these corresponded to when that subunit took the system down.

So if you think about it, these correspond to the times

when subunit was the last of my five units to go down,

because that's the only time the system is going to go down

is when all of those five subunits go down.

Now, if I want to look at my component level distribution,

this only tells me when it brings the system down.

But say I'm interested in how often Sub1 was down,

how often Sub2 was down and so on.

The hotspot, I can look at this Point Estimation

for Component Availability.

And if I scroll down, for example, for Sub1,

this gives me my distributional information

for the component itself.

Z includes the times not only when Subunit 1 took the system down,

but when Subunit 1 went down itself.

Easiest way to dive into the individual distributions of all my subcomponents.

Let's move on to some of these actions and events

and some of the particular properties associated with those.

We'll start with the events.

Not mentioned in the documentation

but Inservice is only available for Basic blocks.

So to remind you, Inservice is based on the age of the block

and not the age of the time of the simulation.

So, for example, if I want to service something

after 100 hours of use, I would use an Inservice.

Otherwise, I could use just a scheduled event.

A scheduled event would be serviced once a week,

but Inservice is only available for my Basic block.

So tells me if I want to use Inservice,

I'm going to have to build up these basic blocks.

Initialization, Block Failure, and System is Down

can only be used once for each block.

So what you'll see is when you use them,

they disappear from your palette of events, so it won't show up anymore.

So you can only use these once.

Let's talk about actions.

There are 13 actions that I can use.

Install Used, Change Distribution, and If can only be used with Basic blocks.

Minimal Repair can only be used with Basic and Series.

So that leaves us nine actions that I can use for any of the blocks.

So they are the Turn On and Off a Block, Turn On and Off the System,

Replace with New, Install New, Remove a Block, Inspect Failure,

and Schedule. So those are the nine

that any of the block, either the Basic or the Composite blocks can use.

There is no limit to how many times an action can be used to a given block.

So I have an example where I used Turn Off Block more than once.

The reason is that

what I want to do after I turn or when I turn on the block might differ

depending on when the block gets turned on.

And again, we'll see an example of that in a little bit.

Actions can only be connected to other actions.

As a matter of fact, events can only be connected to actions.

So I cannot connect an event or an action to a block or to another event.

However, I'm unlimited in terms of how many actions I can chain together.

A few specific properties.

Initialization only occurs once, and it's at the very start

of the simulation run.

We're going to have an example where that makes a difference,

where I turn off a block at an initialization,

but when the system comes back online, that block, it's turned on again.

So I'm going to have to make sure I turn that off manually.

So Initialization only happens at the beginning.

System is Down occurs for every block,

regardless of which block brought the system down.

So I can use that as a listener to see...

For example, if the system goes down,

I might want to perform maintenance on the block even though the block

was running, maybe just to bring it back up

to near new or to bring it back up as new.

So a System is Down will occur regardless of which block brought it down.

Then about the actions,

Turn on System turns on every available block.

Now, by every block, I mean everything that's not already down

because of failure or scheduled to be down.

So I could schedule something to be down

either by using that scheduled event or by using the scheduled action.

Turn on the System is going to turn on all of my blocks.

Blocks got to be turned on manually

after Install New, Install Used, or Change Distribution.

Those are a couple of those cases where the system does not get turned on,

the block does not get turned on automatically.

This is why I might want to use,

for example, install new instead of Replace with New.

Install New lets me do stuff before I turn the system back on.

It gives me that chance to perform other operations

or other actions before I turn on my system.

Turn on Block does not turn on the system if the block was turned off manually.

As we saw in the output,

as you might have noticed in the output, that regardless of the system being on,

you might get a Turn on System event show up.

It does not hurt to turn on a system if the system is already on.

What I say is, if in doubt, add a Turn on System

to your chain of events to make sure the system is on.

By the way, it doesn't matter whether the block is turned on first

or the system is turned on first.

Both work the same way.

All right, let's move on to our second example.

What we're going to do is we're going to build a basic block

in a Standby arrangement.

We're going to have one main unit, one standby unit.

We're going to do a cold startup,

meaning that the standby unit, the backup unit, does not age.

However, it's going to take 30 minutes to start up the unit.

Something I can't do with the composite standby.

Eight hours of maintenance on the backup is performed after control

is passed back to the main unit.

We're going to look at two different switch situations.

We're actually going to look at an infallible switch and we're going

to look at a switch where we've got 90% reliability.

For all of my blocks, I'm going to assume Exponential(500).

I'm going to assume eight hours to either repair or maintain.

Failing blocks will be replaced with new.

Like the previous example, we're going to look at the basic blocks

in this arrangement in series with a standby block with the same setup,

obviously, with the exception of that 30-minute startup time,

which I can't do.

Let's take a look.

Here is our basic arrangement.

I've got my backup unit, my main unit, my standby unit, and for each one of these

I've associated a failure event and a Replace with New.

Sort of the bare bones of what I might start with.

I don't know if I pointed out previously when we were looking at the output,

but it is helpful when I add an event, add an action to give it a unique name

because those are the names that appear in my output.

All right, so let's start with a backup unit.

When my system comes online at time zero, everything is going to be turned on.

I want to make sure that at time zero, that backup unit gets turned off.

The way I'm going to do that is I'm going to use Initialization.

You'll notice that when I used Initialization,

it disappeared.

Next thing I want to do is turn off the block.

Now, if you have used RSS before,

you'll remember that when I try to add an action,

it's going to stack these actions on top of one another.

Sometimes that's not the best location for them.

What you could do anytime you have one action to work from,

I can select the action, click and hold this plus sign

and then just drag off the plus.

Now I can add my action.

In this case, I want to turn off the block.

Now, I don't want the Turn off Block associated with this, Replace with New.

What I'm going to do to avoid that happening is just delete the arrow.

Now I can connect to the event I want to.

The advantage of doing that is now I can move that block anywhere I want to.

I have much more flexibility with the way that block works.

The other thing I'm going to do is I definitely want to label these

so I know when I look at my result table, I'll know what happens.

This is Turn off Block...

Let's just call this turn off backup at start.

I'm going to give each one of my events, each one of my actions a unique name

so I know when they occurr.

That turns my backup off at the start.

The next thing I'm going to want to do is when the main unit fails,

I'm going to want to turn on the backup unit.

Now, again, rather than selecting the backup unit and say Turn on Block,

I'm going to choose my Turn on Block interactively.

I'm going to delete my connection here so I can move this.

I'm going to move things around a bit.

I'll move this right over here.

Again, I want to name this so that if I come back to this diagram

in a couple of days, I know what this action is associated with.

We'll call this turn on backup from main.

Another thing that is mentioned in the documentation,

but I don't know how strongly, but I can connect,

in this case, I'm connecting an event from one block to an action

in another block.

I can definitely do that.

Now, as I mentioned earlier, this will turn on the block

because that block was turned off manually,

I'm going to have to turn on the system as well.

Turn on the system.

Again, I probably want to label that,

I'll say, Turn on System from backup and so on.

Now the only other thing I need to add to my diagram is that

when my when my main comes back online, I want to turn off my backup,

do maintenance to my backup.

Now I could probably...

Well, let's start with, turn off the backup, Turn off Block.

Delete that.

We'll call this.

Must have picked turn on, I got Turn off Block.

Let's try it again.

Turn off Block and label that, turn off backup from main.

There's where my main comes online.

There I've turned off my backup.

I want to perform maintenance on it.

The way I'm going to do that is I'm just going to Replace with New.

Okay, we'll just call this preventive maintenance.

I think I said that that preventive maintenance is going

to be eight hours that it takes.

Now again, I've got to remember

that Replace with New automatically starts the block.

What I'm going to have to do now is turn off the block.

Now, thinking about that in a little bit more detail,

I could have probably just gone directly from the new coming back online

to doing the maintenance.

Because once I start the maintenance on my backup, the backup stops operating.

Then once I am done with my maintenance,

turning off the block.

This block doesn't hurt, but it's redundant.

So let's just get rid of it.

That would be my setup.

I always like to run to double-check to see if things operate

in the proper order and I'll generate my output.

In this case, I've only got it set up to do one simulation.

I might have to take a couple

of simulations depending on the reliability associated with my units.

Here's the main fails, the system turns down,

the standby block is turned off, put in a new main,

turn on backup for main.

If we look at the predator column, there's my turn on backup for main.

Turn on System from backup, Turn on Block by system.

Again, I like to step through these once I build them to make sure

things happen in the right order.

Turn system gets turned on.

There's the new main is put in.

I turn on my system,

again, it's already on, but doesn't matter, doesn't hurt.

New main Replace with New.

That is the block, the backup being turned

to getting a PM and so on.

There should be that backup being turned off.

Again, I like to use those just to double-check

to make things are working properly.

That's the setup without the switch.

To add the switch is relatively straightforward.

Where we would put that is, we would put that

between the main failing and the backup starting

and that's what the if blocks are made for.

Let me get rid of that connection.

It doesn't matter which of my blocks that if comes from

and I didn't want to put it there.

Let me go from... Here we go.

I've got a backup just in case something like that happened.

Here we go.

There's my backup being turned on.

There is my main failing.

We are going to get rid of that.

I probably want to add in my if block so I can float it anywhere.

I'm just going to grab any one of these and we'll add an if.

Delete the connection.

Let's connect that.

Now, the way I would set up my if,

the way if works is that it evaluates what's in the if condition box.

If it's true, if it evaluates to one, then you continue going.

If it evaluates to zero, you stop.

What I can say is if... I don't need the if there,

I'm going to say random uniform is less than 0.9.

When that's true the chain will continue, otherwise, it'll stop there.

That's it.

It's as simple as that to put in that if statement.

Again, I probably want to label this too.

So if switch is success or something like that.

At this point, the only drawback is this what would happen if the switch failed?

Well, switch failed, it's going to have to wait until the main comes back online.

Main comes back online, it goes tries to turn off my backup.

Doesn't hurt trying to turn off something that's already off.

But the thing that I might not want to do

is I might not want to do the maintenance if the backup wasn't used.

In a later example, we'll see how to get around that,

how to look into the system and say, is that backup running?

If it isn't running, yeah, do the maintenance.

If it is running, you don't need the maintenance.

Let's move on to the next example.

In this case, I've got two pumps operating in parallel with a preventive maintenance

performed every two weeks taking eight hours.

The PMs are staggered by a week to keep from having both pumps down

at the same time.

If a pump fails, minimal repair, taking four hours is performed.

In this case, simulation is run for a year's time.

Let's start with the bare bones here.

Here's my pumps with the pump failures and replacements.

How do I set up the wait a week before you start the PMs?

Like the last example, I'm going to start with initialization.

What I'm going to say is wait a week.

How do I say wait a week?

Well, what I'll do is I will use the schedule action.

I'm going to do what I've been doing in the past.

We'll set up the schedule, delete my connection.

I've got a number of different options with schedule.

I only want this to happen once. I want it to happen at the very beginning

so it's connected to my initialization event.

We're going to call this wait one week.

I only want this to happen once.

I want it to take 168 hours.

That's because I've got my simulation set up in hours.

What that does is on start waits a week

and then performs whatever action I connect to here.

What's the action I'm going to connect?

Well, I'm going to use schedule again.

What this is going to be is this going to be PM on pump 2.

I'm going to leave my max occurrence as blank.

So it does it until the end of the simulation.

The completion time is going to be 168x2 so 336.

Now what happens is that after that first week, every 336 hours,

I am going to be able to perform another operation.

That operation a couple of different ways to do that.

We're going to assume that it's PM [inaudible 00:37:03].

So we're going say, Replace with New.

I can say here, replace pump B.

Now, one of the things you might be thinking is that

what happens if I go to do my preventive maintenance

and everything is staggered but for whatever reason pump A is down,

it might have had a failure.

I wouldn't necessarily take down my pump B

for preventive maintenance.

Do that and take down the system. What we're going to use is we're going

to use a hidden variable called simulation context.

Simulation context is the way I can look into my system.

What we'll do is we will put the simulation context,

we'll put it between my scheduled and my actual preventative maintenance.

Let's do this.

Let's get rid of that and we will implement it with an if.

Let me connect and we'll call this, if pump A is working,

now the question is, how do I tell whether pump A is working?

Well, in my if condition, I need to specify the hidden variable

that's associated with pump A's... Whether it's active or not.

The way I would do that is simulation context

and case does not matter.

Simulation context and the variables,

the system variables are stored as an associate of array.

This is called status of pump A.

Now what I have found is that,

this particular part of the dialogue box is not quite as flexible.

If I've got something more complex that I want to build,

then I'll usually do it in a script window and then copy it and then paste it

into this window.

But this is relatively small and we'll just go ahead and type away.

We're going to say this is active.

That's what I would use.

Now, how did I know that that's what I have to use?

Well, as it turns out, this if condition and a little bit

later on when we talk about the stress sharing,

this acts like a script.

What I can do and what I've often done

is put in a print statement, how do I know what is available to me?

Let's put in a print statement there

and we'll say print simulation context, okay.

The only thing that we need to be sure of

is that if statement, the last statement in that if condition evaluates the true.

Just to show you what happens

when I do this, let's just go ahead and run this.

I'm going to run that just to give you an idea

of what gets printed to the log.

Let me go ahead and grab my log and it's over on one of my other…

Here we go, there's my log

and here we go.

This is what got printed to the log.

That gives me information on all of the variables that are available

in simulation context for this particular diagram.

If I had put more blocks in there, more actions, more events

then those would show up, information on those would show up too.

But this is a good way to tell

what's in my system and what are the variables that I have access to.

The important thing to remember here is

I can read this, but I cannot write back to this particular variable.

I don't have that option to change things in my simulation,

but I can use that information.

Going back to our last example, what I could have done

is I could have put an if statement in there

and said if my backup was active,

well then turn it down, do the maintenance.

Okay.

Now for the sake of time, I'm going to move on

to my last bit of information I want to share with you.

By the way, I will have a journal available,

should have a writeup available,

I'll have more examples that I didn't have time to cover.

You'll see a couple more of the actions

we didn't have a chance to look at how they get implemented

and maybe some of the other things

that might not be terribly straightforward.

Custom stress sharing is a little bit unusual in the sense that by default,

stress is shared equally among the subunits.

It's implemented by altering how this block ages

and not explicitly altering the distribution.

When I have no stress sharing, let's say I have n subunits in parallel,

let's say the first one fails at time one, second at time two, and so on.

With stress sharing, the way I age the block

is that, that first failure occurs at t one times n,

times the number of working units.

The second one ages the block an additional n minus one

times the difference between my first and second failure.

The third one n minus two times the difference between

the third, second failure and so on.

That's the way that the blocks age.

This is the information that gets passed to that custom function.

When I try to build this into my stress sharing,

okay, so let me share with you this last example on how this is set up.

Let me find my example.

Okay, here I've got three different stress sharing.

There's my basic stress sharing, so I've got that set up as basic.

Here's my custom.

This is the end that I'm talking about.

You'll know that it's embedded in this log function.

It's done that way so that the aging of the block matches up

with the distributions that I can use for my custom stress sharing block.

What I do is I think of things in terms of my explanation

that the first unit ages at some multiplier times the time,

the second one at some multiplier times the difference between

my first and second failure times, and so on. That's the way I think

about all these things. For example, let's say I wanted to set up,

well, before I go there, let me just briefly say,

as with the if block I can put print statements in here.

If I were to put in a print statement here,

you'll notice that this gets executed before the system starts,

so it's going to print five and it's going to print five

because in this case the block has got five units.

Going to print five when the first one fails,

it's going to go back into the function print four,

and so on, so on and so forth.

Let's say I wanted to set this up as a custom failure where the block

ages at 10 times, not 5 times, but 10 times the rate,

the second one ages at five, the third one at three.

The second one ages at one,

and the last one is over stressed and ages twice as fast.

What I could do is,

again, let's put this here.

Let me add a little space

and make my box a bit bigger.

I might put something like,

again, I want to put that log, that log has got to be in there,

log and I would say something like choose.

What choose does based on n, it'll return,

let's see, when there's one unit, I want it to return a half, one with two units,

let's say 2.5 with 3 units,

5 with 4 units, and then finally 10.

That's how I would create my custom stress sharing.

I'm not limited to this choose function.

Here I've got in this particular example,

I'm using a function.

Okay, again, the thing that's important here

is that this all gets embedded in this log function so that the aging

of the block and the distributions can match up.

Okay, unfortunately, that's all I have time to cover today.

I wish I had more time. There's certainly a lot

of other things that we could talk about.

Those will be in the materials that I provided for you,

and I'd love to hear your feedback

and questions and comments that you have.

Again, thanks for taking the time. Watch this.

I hope this is really beneficial. Thanks.