Overview
Calculation Models in Bazefield are used to perform complex interval calculations within the Bazefield Calculation Service. They allow users to define reusable logic using templates and functions, enabling scalable and maintainable KPI generation across assets.
How Calculation Models Work
Common Functions: The same C# expression syntax of standard Interval Calculations is available within Calculation Models
Script-Based Logic: Built using a Handlebars-based script template and one or more function lists.
Interval Execution: Operate like Interval Calculated Points, running at scheduled intervals.
Reusable Components: Functions can be shared across models and dynamically compiled per asset.
Key Components
ℹ️ Script Template
Defines the variables used in the model using placeholders like:
string objectKey = "{{domainObject.objectKey}}";
These are resolved at compile time based on asset metadata.
ℹ️ Function Lists
Reusable code blocks that can be imported into the script. For example:
if (TagCount([@WindSpeed]) > 0)
windSpeedValue = TagTimeAvg([@WindSpeed]);
ℹ️ Metadata Access
Models can reference any asset and site attributes using Handlebars syntax like:
{{domainObject.ratedPower}}
(retrieve the ratedPower of the current object context){{parent.objectKey}}
(retrieve the key of the current object’s parent object)
Configuration Workflow
Create Model: Define Acronym, Name, Model Type, applicable Object Type, and Script Template.
Add Lookup Functions: Import reusable logic blocks defined outside of the Calculation Model.
Reference in Points: Use
ScriptType = Interval_Calculation
andExpression = $$CalculationModel$$
. SetCalcModelRef
to the acronym of the calculation model.Compile: Load the Point(s) into the Calculation Service to automatically resolve placeholders and assemble the final script.
Example Use Case
Turbine Theoretical Power Curve Lookup Calculation Model
Uses Nacelle wind speed reading to estimate expected energy based on user-uploaded reference power curve.
If Turbine Nacelle sensor reading is unavailable, falls back to alternative wind speed readings: neighbor turbine(s), Met Mast, or daily/weekly/annual average.
Compiled per asset using metadata like object keys, reference turbines, and site Annual Average Wind Speed (AAWS).
Script Template
Defines the templatized logic for the Calculation Model
//Theoretical Power based on best available WindSpeed and PC Lookup
string objectKey = "{{domainObject.objectKey}}";
string parentObjectKey = "{{parent.objectKey}}";
double? theoreticalPower = null;
double? windSpeedValue = null;
double? annualAverageWindSpeed =null;
string PMMWindSpeed = "";
{{#if parent.AAWS}}
annualAverageWindSpeed = Double.Parse(ObjectMetadata.GetAttribute(parentObjectKey, "AAWS"));
{{/if}}
{{#if domainObject.referencePMM}}
string PMMObjectKey = ObjectMetadata.GetAttribute(objectKey, "referencePMM");
PMMWindSpeed = PMMObjectKey + "-WindSpeed";
{{/if}}
{{#each importedFunctions.lookupFunctions.Functions}}
if (windSpeedValue == null)
{
{{this.codeBlock}}
}
{{/each}}
if (windSpeedValue != null)
{
var refCurve = RefCurves.GetRefCurve(objectKey, "Contractual", "Default");
if (refCurve == null) refCurve = RefCurves.GetRefCurve(objectKey, null, "EnergyModel");
if (refCurve == null) refCurve = RefCurves.GetRefCurve(parentObjectKey, null, "EnergyModel");
theoreticalPower = RefCurves.LinearLookup(refCurve, windSpeedValue.Value);
}
return theoreticalPower;
Lookup Functions
Imported functions that will be used in the compiled Calculation Model
During runtime, the Lookup Functions will be evaluated in the order they are defined on the Calculation Model. They are generally used to “lookup” a value for a script variable in order of preference—such as for WindSpeed when you have multiple sensors like nacelle anemometer, neighboring turbine(s), and primary met mast.
AssetWindSpeed
Option 1: Retrieve the object’s WindSpeed reading. (Most desirable.)
if (TagCount([@WindSpeed]) > 0) windSpeedValue = TagTimeAvg([@WindSpeed]);
NeighborsAvgWindSpeed
Option 2: Retrieve average WindSpeed reading from neighboring turbines.
var refObjectIds = JsvStringArrayToArray(ObjectMetadata.GetAttribute(objectKey, "referenceTurbines"));
int count = 1;
foreach(string refObjectId in refObjectIds) {
var refObjectKey = ObjectMetadata.GetAttribute(refObjectId, "objectKey");
if (TagCount(refObjectKey + "-WindSpeed") > 0) {
if (windSpeedValue != null) {
count = count+1;
windSpeedValue = windSpeedValue + TagTimeAvg(refObjectKey + "-WindSpeed");
}
if (windSpeedValue == null) windSpeedValue = TagTimeAvg(refObjectKey + "-WindSpeed");
}
}
if (windSpeedValue != null) windSpeedValue = windSpeedValue/count;
PMMWindSpeed
Option 3: Retrieve the Primary Met Mast (PMM) WindSpeed reading.
if(PMMWindSpeed.Length > 0) if(TagCount(PMMWindSpeed) > 0) windSpeedValue = TagTimeAvg(PMMWindSpeed);
AssetWindSpeed1d
Option 4: Calculate the Turbine’s 1 day average WindSpeed value.
if(TagAggregate(objectKey + "-WindSpeed", "COUNT", IntervalEnd().AddHours(-24), IntervalEnd()) > 0) windSpeedValue = TagAggregate(objectKey + "-WindSpeed", "AVERAGE", IntervalEnd().AddHours(-24), IntervalEnd());
AssetWindSpeed7d
Option 5: Calculate the Turbine’s 7 day average WindSpeed value.
if(TagAggregate(objectKey + "-WindSpeed", "COUNT", IntervalEnd().AddHours(-168), IntervalEnd()) > 0) windSpeedValue = TagAggregate(objectKey + "-WindSpeed", "AVERAGE", IntervalEnd().AddHours(-168), IntervalEnd());
AssetWindSpeedAAWS
Option 6: Retrieve the site Average Annual WindSpeed attribute. (Least desirable.)
windSpeedValue = annualAverageWindSpeed;
Compiled Calculation Model
All Handlebars expressions and helpers are resolved. This means that the objectKey for the asset and site is resolved and that all the lookupFunctions are inserted.
//Theoretical Power based on best available WindSpeed and PC Lookup
string objectKey = "WP1-T001";
string parentObjectKey = "WP1";
double? theoreticalPower = null;
double? windSpeedValue = null;
double? annualAverageWindSpeed =null;
string PMMWindSpeed = "";
annualAverageWindSpeed = Double.Parse(ObjectMetadata.GetAttribute(parentObjectKey, "AAWS"));
string PMMObjectKey = ObjectMetadata.GetAttribute(objectKey, "referencePMM");
PMMWindSpeed = PMMObjectKey + "-WindSpeed";
if (windSpeedValue == null)
{
if (TagCount([@WindSpeed]) > 0) windSpeedValue = TagTimeAvg([@WindSpeed]);
}
if (windSpeedValue == null)
{
if (TagCount([@WindSpeed]) > 0) windSpeedValue = TagTimeAvg([@WindSpeed]);
var refObjectIds = JsvStringArrayToArray(ObjectMetadata.GetAttribute(objectKey, "referenceTurbines"));
int count = 1;
foreach(string refObjectId in refObjectIds) {
var refObjectKey = ObjectMetadata.GetAttribute(refObjectId, "objectKey");
if (TagCount(refObjectKey + "-WindSpeed") > 0) {
if (windSpeedValue != null) {
count = count+1;
windSpeedValue = windSpeedValue + TagTimeAvg(refObjectKey + "-WindSpeed");
}
if (windSpeedValue == null) windSpeedValue = TagTimeAvg(refObjectKey + "-WindSpeed");
}
}
if (windSpeedValue != null) windSpeedValue = windSpeedValue/count;
}
if (windSpeedValue == null)
{
if(PMMWindSpeed.Length > 0) if(TagCount(PMMWindSpeed) > 0) windSpeedValue = TagTimeAvg(PMMWindSpeed);
}
if (windSpeedValue == null)
{
if(TagAggregate(objectKey + "-WindSpeed", "COUNT", IntervalEnd().AddHours(-24), IntervalEnd()) > 0) windSpeedValue = TagAggregate(objectKey + "-WindSpeed", "AVERAGE", IntervalEnd().AddHours(-24), IntervalEnd());
}
if (windSpeedValue == null)
{
if(TagAggregate(objectKey + "-WindSpeed", "COUNT", IntervalEnd().AddHours(-168), IntervalEnd()) > 0) windSpeedValue = TagAggregate(objectKey + "-WindSpeed", "AVERAGE", IntervalEnd().AddHours(-168), IntervalEnd());
}
if (windSpeedValue == null)
{
windSpeedValue = annualAverageWindSpeed;
}
if (windSpeedValue != null)
{
var refCurve = RefCurves.GetRefCurve(objectKey, "Contractual", "Default");
if (refCurve == null) refCurve = RefCurves.GetRefCurve(objectKey, null, "EnergyModel");
if (refCurve == null) refCurve = RefCurves.GetRefCurve(parentObjectKey, null, "EnergyModel");
theoreticalPower = RefCurves.LinearLookup(refCurve, windSpeedValue.Value);
}
return theoreticalPower;
Why Calculation Models Matter
✔️ Advanced Logic: Go beyond one-line expressions with full scripting.
✔️ Reusability: Centralize and reuse logic across assets and projects.
✔️ Scalability: Dynamically compile per asset using metadata.
✔️ Maintainability: Update logic in one place and apply it fleet-wide.