Result models

From Heureka Wiki
Jump to navigation Jump to search

Implementing a Result Model

A result model is implemented as a class that is depending on:

  1. Treatment Unit
  2. Period (optional)
  3. Before or after treatment in the period (optional)
  4. Other results (optional)

The class is given the attribute Slu.Heureka.DomainLayer.Forest.TreatmentUnitResult. In the attribute it is specified which of the above parameters that are required by the model (the attribute also specifices more information about the result model, see code for more details).

The class must also have a constructor with the required parameters, in the same order as above. When a new instance of the class is created, the results of the model should be calculated.

All properties of the class will be exposed as result variables, unless the attribute Browsable is set to false.

Each property must be a simple type (nullable simple types are allowed), or a SpeciesCollection. Note that all simple types not have been used, so simple types other than int, double, bool, and int-based enums should be tested carefully.

On each property the DisplayName attribute and the Slu.Heureka.DomainLayer.Forest.Unit attribute may be given. The former is the name that will be presented to the user for the property, and the latter gives the user extra information about the unit of the attribute.

All result model classes should implement IClonable and return a deep copy of the result when cloned.

Note that if a result model needs results from other result models, these results must be retrieved from the passed result list parameter in the constructor, not via the TreatmentUnit. This is due to the fact that sometimes the result is created via copying of results (see Getting results in code, and the passed parameter contains the results being copied.

Once the model is implemented as above, the result variables will be available in maps, diagrams, tables, optimization models, and forest domains.

Different types of result models

There are several different types of result models:

Before/After result
Results that might be changed when a treatment occurs, e.g. volume, biomass
Treatment result
Somewhat misleading term for results that are the product by a treatment (e.g. cut volume), or that are not changed in the same period due to treatments (e.g. growth, carbon and nitrogen)
Single result
Results that are independent of period, that spans an entire alternative (e.g. net present value)
Generated result
Most results are generated from a simulation, but some results (e.g. habitat value) cannot be generated in a simulation, such non-generated results are created from an entire plan

Advanced result model implementation considerations

Results that depend on year or their relative position among other results (e.g. CarbonAndNitrogenData) must implement the IRecalculatableResult interface. The reason for this is that for efficiency reasons results may be copied from one generation to another (e.g. if generation 3 of a TreatmentUnit should be identical to generation 2). The interface is implemented as follows:

SetDirty(int period, double year)
Called when the result is copied from one generation to another
IsDirty
Indicates that the result is dirty and needs recalculation to become valid
Update(IList<Result> results)
Called when the result should be updated
Clean()
Called when the result no longer needs to be recalculated (allows cleanup of intermediate results)

Getting results in code

The results of each result model is managed by the Result class, which acts as a container of result model objects, and can be accessed via a TreatmentUnit:

GetResult(int period)
Gets the results in a given period changes)
GetResults()
Gets all results up to current period
CopyResults(IList<Type> resultTypes)
Gets a deep copy of all results up to current period, with all result models of the given types calculated.

Note that results retrieved via the first two methods might later be changed if the state of the TreatmentUnit changes. The values from a given result model can be retrieved on demand when the result(s) has been retrieved. Results retrieved via the last method are a copy and thus will not be changed, and the result models must be specified beforehand, values from result models not given in the call cannot be retrieved at a later time.

Once a Result object has been retrieved, it is possible to get result model objects from it:

ContainsBefore(Type t), ContainsAfter(Type t), ContainsTreatment(Type t)
Checks if the given result model type exists in the result
Before(Type t), After(Type t), Treatment(Type t)
Gets the result model objects of the given type (note that several objects may be retrieved)
BeforeAndAfterSame
Returns a value indicating that values before treatment are identical to values after treatment (e.g. no treatment has been performed in the period the result belongs to)

The inner workings of the result class

Storage of result model objects

The Result class stores result model objects in dictionaries with the Type of the result model as key. There is one dictionary for before results, one for after results, and one for treatment and single results.

Getting result model objects

Results are retrieved via the private method getResult(Type resultType, Dictionary<Type, object[]> existingResults, Dictionary<Type, object[]> alternativeExistingResults, TreatmentUnitResultType treatmentUnitResultType).

The method will do the following:

  1. If the result exists in the given dictionary it is returned from the given dictionary.
  2. If the alternativeExistingResults parameter is passed and the result is identical before and after treatment, and the result exists in that dictionary, it is returned from that dictionary. Alternative dictionary to the before dictionary is the after dictionary and vice versa, this means that if the result has been calculated for "after" it can be used for "before" and vice versa (given that no treatment has occured).
  3. If none of the above works, and TreatmentUnit is available (which is true if the result was "getted", not copied, see Getting results in code, the result is created (see Creating result model objects.

Creating result model objects

Creating new result model objects is fairly simple, based on the TreatmentUnitResult attribute the result class can determine the expected format of the constructor for the result model class, and invoke it.

Creating copies of results

When results are copied (see Getting results in code), this is done as follows:

  1. A new list with results is created
  2. Existing results model objects are copied from the TreatmentUnits results (so that the results don't have to be calculated again)
  3. Missing result model objects (except single results) are calculated in the new result list
  4. These results models objects are now copied back to the TreatmentUnit (in case those results will be needed again)
  5. Finally single result model objects are added