Calculation Context |
Many calculations in DME Component Libraries require various pieces of contextual information. What leap seconds have been defined? How are you modeling the orientation of the Earth for transforming between the Fixed and Inertial reference frames? This contextual information could be passed around everywhere that it is needed, but that would be cumbersome. A Calculation Context is a consistent place to store and find such information so that it does not need to be passed around as parameters to methods.
A Calculation Context is functionally similar in some ways to a global variable or static class. Like a global variable or static class, it is conveniently accessible from anywhere. A Calculation Context is much more flexible, however. Rather than having a single copy of a global variable or static class for an entire application, each thread can have its own Calculation Context. This allows different threads to model the transformation between the Earth Fixed and Earth Inertial frames in a different way, while still allowing code that needs to do that transformation to use the correct transformation without passing around anything additional.
Thus, the Calculation Context is very useful for contextual information that is usually consistent throughout an entire application but sometimes needs to be different in different threads of execution. For example, a multi-user web application handling requests for two different users might use one model of the Earth for one user and a different model of the Earth for the other user based on the users' preferences.
In general, you will not interact directly with the CalculationContext type, but will instead work with CalculationContextFacets, each of which represents a chunk of contextual information that resides in the CalculationContext. Facets generally have a static GetFromContext method to retrieve the current effective instance from the current CalculationContext. For example, to retrieve the CentralBodiesFacet instance, use its GetFromContext method:
CentralBodiesFacet centralBodies = CentralBodiesFacet.GetFromContext();
If a CentralBodiesFacet instance does not already exist in the context, a new one will be created and returned.
Similarly, you can add an instance to the CalculationContext, as in the following code, which uses the CentralBodiesFacetUseInCurrentContext method:
CentralBodiesFacet centralBodies = new CentralBodiesFacet(); // configure centralBodies as necessary centralBodies.UseInCurrentContext();
The following facets are supplied with DME Component Libraries:
As previously mentioned, each thread has its own calculation context. This raises the question: how do new threads get a calculation context? And where do they get it from?
When a thread is first created, it does not have a calculation context. The first time you attempt to access the calculation context, either by accessing the Instance property or by calling a facet's GetFromContext method, a default CalculationContext instance is created for the thread.
You can change the default context that gets created for new threads by setting the DefaultForNewContexts property. For example, the following code makes the current context the default one that is used for new threads.
// Make the current calculation context the default one that is used for new threads.
CalculationContext.DefaultForNewContexts = CalculationContext.Instance;
Whenever DME Component Libraries creates new threads, such as to parallelize an Access computation, it copies the calculation context that initiated the calculation into the threads that are to actually perform the calculation. You can do the same very easily for your own threads by using the BackgroundCalculation type. BackgroundCalculation is very similar to the .NET BackgroundWorker class, except that it automatically copies the calculation context into the worker thread. BackgroundCalculation has a few other differences from BackgroundWorker. See the reference documentation for more information.
If you are not using BackgroundCalculation, you can still copy the calculation context manually. Before starting the new thread, make a copy of the current calculation context:
CalculationContext contextForThread = CopyForAnotherThread.Copy(CalculationContext.Instance);
Then, pass the new instance to the thread:
Thread thread = new Thread(ThreadEntryPoint);
thread.Start(contextForThread);
Finally, at the top of the thread's entry point function, set the current calculation context to the one passed from the main thread:
public static void ThreadEntryPoint(object parameter) { CalculationContext calculationContext = (CalculationContext)parameter; CalculationContext.Instance = calculationContext; // The thread's actual work goes here. }