Defectivity reduction efforts drive large-scale investigations of historical process data in photoresist manufacturing. For production engineers at Inpria, this data is stored as time series data on an SQL database. Data is recorded from equipment sensors every two seconds. As a result, large-scale investigations of new data metrics are time-consuming and present serious data integrity challenges. So, what happens if you need to investigate a time-dependent parameter across historical production records?
For the HVM and Pilot engineering teams, this question presents very different challenges. The HVM systems have access to years of high-quality batch records. The Pilot systems are R&D equipment. Batch records exist in various forms: lab notebooks, Excel spreadsheets, and SQL data tables.
JMP makes it easy to import batch record data, regardless of data source. The SQL query parameters are generated using this batch record data. Once a subset is created, querying the time series data is simple using JSL SQL integration. From there, the time series data is cleaned and explored using JMP visualization tools. Lastly, a summary table is generated that can then be joined with defectivity data sets.

Hi, my name is Jack Phelps, and I'm a process engineer at Inpria, an EUV photoresist manufacturer. I'm currently working on our pilot-scale process, but have worked all over, including our HVM, which has our 55-gallon and 360-gallon tanks. I'd like to give a presentation today about utilizing JMP's SQL integration and data exploration platforms to explore large sets of time series data.
Let's start with a quick overview of the problem. Inpria process engineers most commonly analyze time series data, and this is to troubleshoot abnormal tool behavior using the time series data that's generated by our digital control system. We also summarize our filtration parameters for C of A and explore this processing data for finished goods defect mechanisms, i.e., seeing how does our processing impact the on-wafer performance of our material.
Because defect reduction is the name of the game in photoresist, we are constantly looking for new parameters that we can use to reduce defectivity on our process. What happens when we identify these new parameters, and we want to analyze years of historical data? In short, we don't currently. The reality of this is that our team is used to manually compiling this data, which is extremely labor-intensive for any sort of data exploration that we want to do.
Why is this? Our team's specialty is chemical process engineering, but we hold many quality responsibilities, largely because our company came out of a startup and has been transitioning from a startup to a manufacturing company. There's little SQL knowledge on the team or experience within the process team. To undertake this kind of project is a big task for our group, and we typically have done this manually in the past.
A goal of mine was to start to learn more SQL so that we can start to compile some of this historical data that we currently are blind to or have not analyzed. A goal for this project would be to compile our historical temperature data for a chemical addition step within our process. The solution for this is we have a wealth of batch record data, so let's utilize that batch record data in SQL to query our historical time series machine data.
This presents two different problems here depending on what tank we're using. For our HVM tanks, this query is relatively simple due to our QMS and MES. These record batch record and step data easily and readily so that we can readily query it. For our pilot-scale batches, this data is much more scattered.
Our batch record has existed in many different forms, including Excel spreadsheets, lab notebooks, sometimes physical lab notebooks. To get all this data from our process database, we will need to do a bit more of an intensive query on that.
What's the fundamental problem here? Our machine digital control system records every 2 seconds, so we have our sensor data from pressure, temperature, flow, and pumps. It's recorded to a SQL database every 2 seconds, and each tank has its own data table. In effect, for some of our older tanks, this means we have about 7 years of historical data, which is about 105 million lines. This is a very large data set, and we will need to use SQL to effectively subset and grab the bit that we want to actually analyze here.
How do we generate this? For starters, what I've learned from this process is that you want to start by looking at the structure of the data. Let's query one batch, look at it, explore it for parameters that we can use to then refine the query and expand it for a larger data set. In this essence, we're using JMP's data exploration tools to look at this data and find parameters that we can then use to query more data.
We can expand this query, and once we find a parameter for that, we expand it, query it over the batch record, and explore that large data set for process variation, and perform this same sort of data exploration here. Effectively, this is just an iterative process to improve our data integrity on this query. When we look at that process variation, what we're really looking for is a practical evaluation of whether this data set is then usable for the parameters that we want to summarize out of it. If it's not, we continue this process and look to improve the query. If it is workable, we can then summarize it and analyze it.
Where do we start for this? I think the easiest place to start is to query the data set that you know the best. In this case, it's the HVM data, largely because of that hybrid eQMS and MES. This contains our batch record and our manufacturing step records as well. This is discrete data. It'll be easy to subset and join onto the machine data, which is continuous and time series, as I've mentioned. This can be done in a JSL script using an SQL query, and this can be done easily to subset the machine data.
Let's take a look at what this looks like in practice here. Let's start by, like we said, looking at a graph. I've queried one data point here. What we can see is some of the parameters that we use to query this data. I've plotted here the dose mass and the tank temperature versus time. What can we see? We can see that when we highlight by recipe ID, this is where our addition happens. We can see the dose mass decrease and the tank temperature increase, which is the expected behavior from this process.
Looking at this and other batches that I've screened for this query, we can see that this behavior seems to be fairly consistent and that the recipe ID should be a sufficient parameter to query on. The reason for this is that we're really just wanting to look at this temperature change. If this occurs within the step, then this is sufficient to query on. From here, we can then use our QMS to narrow down this query for each batch record and then expand it so we can query a number of batches. For our QMS database query here, I'm really looking to get some of the step data that we need to narrow it down.
The critical parameters here in this first SELECT statement are this ID parameter, which is the step ID. Then the WHERE statement on this. Essentially, we're going to query where this recipe ID equals 27, as we showed in the previous graph, and also where the process type is an addition. We also clean these lines after a batch is completed, and so we could get data that's from the line cleaning rather than the chemical addition.
This should be sufficient to narrow down our query and get something that we can join the process database on. Let's look at the process data query here. This is essentially using a JMP script to take the data points that we've queried from this QMS data and then put it in a form that we can use in SQL here. We're doing some string operations to essentially get a list of these step IDs that we want and then insert it into this SQL statement.
This is looking at all of our parameters on the process database. We select these columns and then select with WHERE this step ID is in this list that we generated in this script. What this does is it essentially just narrows that query based on our batch record data.
When we look at the data we query here, well, we can see that we've done that. We've successfully queried pretty much the temperature data for pretty much every batch. But there are some problems with this data set. The key thing with this analysis is that once you start to expand it, you will start to notice variation. This is just naturally due to how the process is executed.
For instance, a few errors that I've commonly seen when looking at this data set is the step not starting when you would expect it to. Essentially, we get a batch record or a step ID for this, but the process hasn't started, and that's to be expected with our process. This is where we have to make a decision: is this data set good enough to then continue on and analyze?
In this case, as variable as this may look, I decided it was, largely because the types of issues and variance we're seeing is relatively easy to filter out. It's mostly steps that weren't started on time or just variances in the dose rate, and so we can filter that out fairly easily using a manual scrubbing. I was satisfied at this point and decided, let's look at our HVM data.
I was satisfied and concluded that the data set, while not great and high variance, was good enough that we could prune down the data manually. I didn't have a good sense at the time of a parameter that we could use in the SQL query to do this as well, but I understood the data structure enough to analyze it. This led me to the next step, which was the more complicated bit of this analysis, namely querying the Pilot data.
Without this MES data recording to a database, well, we need to use some timestamps to query. A clarification on this is that our step IDs are recorded to the HVM process database, but they're not recorded to the Pilot database, largely because we're not using an MES like the HVM is. Effectively, what this means is we need to do a little bit more on the scripting side to make sure that we get the correct data in there to narrow down our query. Using JSL and SQL, this task is relatively simple, but it is more prone to manual entry errors than using data from the QMS. We start with the Excel batch record here.
We import it into JMP and use the JSL script to prepare timestamps for the query and compile our batch record data. Essentially then, we run the SQL database query and do the same thing here as we did for the HVM, where we create the subset of the machine data. What does this look like in practice? The first thing I want to look at here is, can we use the same parameter that we use for the HVM data to narrow it down? It was a logical conclusion from that first one, and we have a similar parameter on our systems.
I've labeled it here as recipe ID, but in reality, it's the sequence ID is what we would call it. It's a function of the control plans we use on our Pilot systems, and essentially, it just indicates the position of the step within the control plan. It may not be as well validated or have as high data integrity as the recipe IDs, which are controlled by the QMS.
Essentially, this here is something we could consider using. But when we start to do this data exploration, you can see some problems here. First off, this step doesn't seem to capture all the temperature data that I want to get, and so we have to look and see if this is consistent step to step. But, generally speaking, this is not going to be sufficient. We need to figure out some method to get all this temperature data that I want to analyze here.
Let's go back to the drawing board a bit. Like I said, we have our batch record data, and one thing we record here is the addition time of this. Again, this is manually recorded, but we can use it to start to generate a query here. I thought, well, let's generate a range of timestamps to account for some of that variation we would expect to see from the manual entry process.
This script here does a data table query using the Query function in JMP and essentially opens that Excel sheet as a data table, and we're using it to generate a window of plus or minus 1 hour on timestamps. That's what these two statements do here, and then from there, we go, and we're going to construct a list of lists that can then be used to make an inline table in SQL. This is converting the timestamps into the format we need and then taking the other data that we want from the batch record, including our batch IDs, production systems, and the formulation.
This inserts it into a list that we can use to query this data. That's the preparation side. What does the SQL statement look like? This is slightly more complicated here. First off, I've included a FOR statement here so that we can then query by tank. Essentially, we take our columns that we want off the process database and then the columns that we want off of the inline table that we're going to construct, and that's where we're going to add on to the data table.
This line here is what we use to construct this inline table. Essentially, we're using a VALUES statement and insert that list that we constructed in the previous script, and use that to get our timestamps, which we're going to bound the query on. Essentially, from there, all we need to do is do a table join, essentially timestamp between those two timestamps, and we can join this table and get all the data we want out of this. This script is just doing it for each tank that we have, and we want to analyze using an ODBC query.
What does this look like in practice? Essentially, it's this data that we were showing here earlier. This is effectively our time series data from Pilot plus or minus the 1 hour here. I think it's a useful exercise to go through and show the bits that are a bit tough about this method. Unfortunately, these timestamps are manually entered, and we can see that it captures the data we want often, but not all the time, and so we would need to manually rectify this, unfortunately.
While this could be sufficient for an initial exploration, unfortunately, this is not sufficient to compile the data that we want to compile. I think, unfortunately, we have to go a bit back to the drawing board here and look for a parameter that we can more effectively prune the query on. In this case, because we're recording from our machine data, we are also recording a column that we call outputs here. This represents essentially the valve positions at any given point in our process.
What I was thinking here is that since we don't necessarily trust this recipe ID step, can we get a state where a consistent state when that valve opens? The answer is yes. This outputs column will always read at this value when these valves are open, and the only thing that would change that would be PLC changes, which we do very sparingly.
This is a much more reliable parameter to build a query off of, and I decided to use this to build the next iteration of our query to refine it and get a better subset of this data. What does this query look like? Essentially, this query, it's a little bit more complicated, but it works very similarly to the previous one. What we're looking at here is we're going to grab the columns that we want from this database and also the batch record columns that we had as well. But we're going to start with a subquery here, and this is where we generate the parameters that we want to narrow the query down on.
I mentioned that we're going to use this outputs parameter. How we do this is that we're going to start by taking the first timestamp where this outputs equals that value when the valve is open. Where our outputs equals 1024, we want to grab the minimum of that timestamp or the first bit of that. Additionally, we're going to do a fixed bound on this query and then add 15 minutes to that as our final timestamp. Importantly, much like the HVM data, we also need to group this to make sure we're not capturing cleaning data.
We're going to group this by our timestamp here and then run this initial subquery on the initial and final bounds. To eliminate that cleaning data, we're going to use the inline query, inline like we did in the last one. Essentially making sure that we take the spike date or the addition date and use that to make sure we're not capturing cleaning data. That's, in essence, the structure of this query. It adds a little bit of complexity, but it is much more consistent at getting the data that we want.
What does this look like when we query it? It looks a lot more refined, and it's grabbing a much more consistent section of the data. We can see here that we are looking at our temperature data, and this is essentially the structure of the data table that we would expect, where we see an increase in the temperature after that valve has opened, and we capture most of the data for this temperature increase. This is great. This is what we want to see.
Obviously, there will still be some data integrity challenges with this, but it is in a much better state than it was before, and so we can start to do some analysis on this. I decided from here, let's generate a summary table. In this case, I used Tabulate and just generated a quick summary with some of our parameters that we want to see here.
Let's take a look at it, and we can actually see some stuff with this. We can see that it's trending with concentration, which is expected. Essentially, this query is working as I would expect it to. Then the question is, well, can we take this same idea and then use it to refine our HVM query? The answer is yes, but we do need to be a bit careful with it.
The same point I alluded to with this Pilot data here is that we do need to be careful to make sure we're not over-pruning the data. As we can see, we have some data in here that doesn't necessarily show the increase that we expect. If we look at the tank volume increase, well, we can see that it may not be grabbing the right data as we expect here, and so we might need to refine that further. That's the challenge with this, is that you are always going to be fighting the integrity of your data here.
The more you prune down your query, the more you're going to risk that you're cutting off data that you want to have in your data set. We can do the same for the HVM data. But when I did this, we found that same thing, where this was over-pruning data, specifically for more recent batches. I decided for this analysis, let's keep that original data table that we were using.
Now we can generate summary tables for both our Pilot and our HVM batches, and this is where we really get to see the fruits of our labor here. Essentially, we get to analyze our data. What are we looking for here? This really was just mainly to look at some really simple stamp data points here, such as summarizing our temperature data for min, max, and range, and then joining on concentration and then heat of reaction data so that we can look at this and really analyze whether we can see differences in materials.
That was the real goal here, is see if this is a useful metric first. Is it a useful metric? Let's look at our HVM and our Pilot data compared here. We could see a trend when we sort by component on our Pilot data. We can't necessarily see that same trend here. It is much more tightly grouped on the HVM side.
What we're seeing here is more the variance of one product versus the concentration curve that we see with many different products at different concentrations. We'd expect to see these behave differently because they have more chemical variance within them, and so they should behave differently, unlike this HVM data, where this is much more of a monitor. We're seeing our concentration variance in this graph as well as the variance in our tank temperature. But we can still see some of this variance of different materials.
This is where these metrics become interesting to explore. We've generated a data set that is very rich to explore, and we can see differences in these processes. This is not limited to this concentration data. We can also see some similarities as well, in that we see some variances by component. This component A is the principal component of our material, and so we do expect to see chemical differences between these two. Knowing the compositions of these, I would expect these two materials to behave similarly and this one to behave quite differently, which we do see.
We also see this in the HVM data, too. We can see these CFA and ASA materials, they're comparing very similarly, but different from this BZX material. But we can also see on the HVM data, we have these outliers here from this data set. I do want to show this with the points just to emphasize how much more data we have for this BZX material in our HVM side. Part of this would be like, why is that? Now that we've generated the summary data, we can look into it and start to see differences in our tanks.
One place that we can see this is in our minimum tank temperature versus our tank, our dT. We see a trend in this for our parameters or for that BZX material here, where we see the larger increase in tank temperature. We see it needs to be trending with our minimum tank temperature, and the main reason for this is an equipment one and a process difference. We control the temperatures of our tanks on the HVM side, but we don't do that on our Pilot tanks. On our Pilot tanks, we're actually just seeing the reaction unfold, and whatever temperature difference occurs as a result.
This is a much more stochastic distribution, and we don't see any trend. This is where this is a very rich data set. We have gone from millions of lines of time series data to a data set that's actually manageable and that we can look into and then join with those parameters that we want at the end. This is the real value of this proposition here, is that we've effectively improved our data accessibility really significantly.
Using this method, we can more efficiently investigate these changes or distribution shifts, and then, additionally, compile historical data sets that we have never really looked into, like this temperature data here. There are a number of parts of our process where we could use this on, just to rectify some of the historical data that we have. Additionally, this really highlights that the data exploration process, especially with these large sets of data, it's iterative, and you inherently explore more data by looking at these larger data sets.
Using JMP's visualization tools are very useful, not only to help refine your queries, but also so that you can look and see behavior in previous processes that you wouldn't have been able to see before. That's immensely valuable, especially for us as, realistically, a research and development company. We're looking to develop the next generation of photoresist materials, and so oftentimes, we do need to look at some of these parameters that we've discarded in the past as not important.
But another critical conclusion that I had from this is that when you start to work with SQL, you start to realize the importance of data integrity and your query validation. A critical part of this process, which was not necessarily highlighted in the materials I have here, is that you do need to validate that your query has executed correctly, that you're grabbing the data that you want to grab.
Whether this is from that initial step of the process where you're looking at the shape and structure of your data to the steps where you're looking at hundreds of batches, anytime you narrow the query and narrow down the subset to get a more efficient query, you are potentially cutting off important data. It's very important to validate this. In this case, we can do this using the summary table like we saw there as well. That concludes the presentation. Thank you for your time.
Presenter
Skill level
- Beginner
- Intermediate
- Advanced