Converting methodology equations into executable code using Guardian's customLogicBlock
This chapter teaches you how to implement methodology calculations as working code that produces accurate emission reductions or removals. You'll learn to translate VM0033's mathematical formulas into executable functions, using the ABC Mangrove's real world data artifact as your validation benchmark. By the end, you'll write code that transforms methodology equations into verified carbon credit calculations.
Learning Objectives
After completing this chapter, you will be able to:
Translate methodology equations into executable JavaScript or Python code
Implement formulas for baseline emissions, project emissions, and net emission reductions
Process monitoring data through mathematical models defined in VM0033 methodology
Validate equation implementations against Allcot test artifact input/output data
Handle data precision and validation requirements for accurate calculations
Structure mathematical calculations for production-ready environmental credit systems
Prerequisites
Completed Part IV: Policy Workflow Design and Implementation
Understanding of VM0033 methodology and equations from Part I
Basic programming knowledge for implementing mathematical formulas (JavaScript or Python)
Guardian customLogicBlock: Your Calculation Engine
The Mathematical Execution Environment
Guardian's customLogicBlock is your calculation engine for environmental methodologies - it's where mathematical equations become executable code. Think of it as a computational engine that processes monitoring data through formulas to produce emission reductions that match methodology equations precisely.
You can write your calculations in JavaScript or Python - Guardian supports both languages. Most of our examples use JavaScript, but the concepts apply equally to Python.
customLogicBlock in VM0033's PDD submission flow
Understanding Your Input Data
Every customLogicBlock receives Guardian documents through arguments[0]. These contain the measured variables and parameters needed for your methodology equations - real data from environmental monitoring. Here's the data structure you'll process through mathematical formulas:
This is actual data from the ABC Blue Carbon Mangrove Project in Senegal - the same project used in our test case spreadsheet.
Accessing Data Like a Pro
Field Access Patterns from Production Code
Let's look at how VM0033's production code accesses data. These utility functions from er-calculations.js make your code clean and readable:
The ?? operator provides safe defaults when data might be missing.
Building Your Calculation Engine
The Main Calculation Function
Every customLogicBlock starts with a main function that processes the documents. Here's the pattern from VM0033's production code:
Processing Project Instances
Each project instance represents a restoration site. The processInstance function is where you implement the methodology calculations:
Implementing Baseline Emission Equations
From Methodology Equations to Code
Baseline emissions implement the scientific equations from VM0033 Section 8.1 - representing the "business as usual" scenario without restoration. Each equation in the methodology PDF more or less becomes a function in your code.
Example: VM0033 Equation 8.1.1 - Soil CO2 Emissions
Implementing Project Emission Equations
Translating VM0033 Section 8.2 Equations
Project emissions implement equations from VM0033 Section 8.2 - the restoration scenario calculations. These equations typically show reduced emissions and increased sequestration compared to baseline.
VM0033 Section 8.5 - The Final Scientific Calculation
This implements VM0033's core equation that transforms baseline and project emissions into verified carbon units (VCUs). Each line of code corresponds to specific equations in Section 8.5 of the methodology.
Example: VM0033 Equation 8.5.1 - Net Emission Reductions
Handling Real-World Data Challenges
Defensive Programming Patterns
Real project data is messy. Projects miss monitoring periods, equipment fails, and data gets corrupted. They might send a different data type than you might expect. Your code needs to handle this gracefully:
Error Handling
Validation: Allcot Test Artifact as Your Benchmark
Ensuring Mathematical Accuracy
The Allcot test artifact is your validation benchmark - it contains input parameters and expected output results calculated manually according to VM0033 methodology equations. Your code must reproduce these results exactly to ensure mathematical accuracy.
Your equation implementations must produce the same results as the manual calculations to be valid.
Python Alternative
Writing CustomLogicBlocks in Python
Guardian also supports Python for customLogicBlock development. The concepts are the same, just different syntax:
Choose the language you're more comfortable with - both produce identical results.
Testing Your Code
Quick Testing Tips
While Chapter 21 covers comprehensive testing, here are quick validation techniques while you're developing:
Using VM0033's calculation engine with the ABC Blue Carbon Mangrove Project data, here are the actual VCU projections over the 40-year crediting period(data added till 2055 only):
Year
VCU Credits
Year
VCU Credits
Year
VCU Credits
Year
VCU Credits
2022
0.01
2032
104,012.50
2042
122,680.75
2052
75,559.80
2023
0.29
2033
110,576.46
2043
120,929.68
2053
72,200.65
2024
4.31
2034
115,770.40
2044
118,625.12
2054
69,072.40
2025
1,307.66
2035
119,502.79
2045
115,610.59
2055
66,174.64
Total Project Impact: 2,861,923 VCU credits over 40 years
This demonstrates what your code should produce - substantial carbon credits from mangrove restoration that follow the methodology calculations exactly.
Deep Dive: VM0033 Production Implementation Analysis
Note for Readers: This section provides an detailed analysis of VM0033 calculation implementation in Guardian's customLogicBlock. It's intended for developers who need to understand, write, or maintain VM0033. You can skip this section if you only need to understand the basic customLogicBlock concepts.
This deep dive examines the complete production implementation of VM0033 tidal wetland restoration calculations in Guardian, using the VM0033 Allcot Test Case Artifact and er-calculations.js as our reference implementations.
Complete VM0033 Production Code Architecture
The 1261-line er-calculations.js contains 25+ interconnected functions implementing the full VM0033 methodology. Here's the complete function catalog mapped to test artifact worksheets:
Core Architecture Overview
Test Artifact Mapping
Each function maps directly to specific data models defined within VM0033_Allcot_Test_Case_Artifact.xlsx:
8.5NetERR (53x23) → processNETERR() and all VCU calculation functions
Section 3: Temporal Boundary System (Lines 181-350)
Peat and Soil Depletion Time Calculations
VM0033 calculates when carbon pools will be depleted to determine crediting periods. This maps directly to the 5.1_TemporalBoundary worksheet (36x24) in our test artifact.
Two Ways to Calculate Soil Organic Carbon Benefits
VM0033 offers two approaches for calculating soil organic carbon benefits. Both map to the 5.2.4_Ineligible wetland areas worksheet (47x30) in our test artifact.
This function selects which approach to use and calculates the final SOC_MAX value:
Test Artifact Cross-Reference:
SOC calculations map to 5.2.4_Ineligible wetland areas worksheet columns A-AD
Total stock approach uses 100-year projections from columns B-H
Stock loss approach uses carbon loss rates from columns I-O
Both approaches feed into SOC_MAX value in column AD
Section 1: Monitoring and Submergence Processing (Lines 39-94)
Processing Time-Series Monitoring Data
VM0033 tracks wetland submergence over time to calculate biomass changes. This maps to the MonitoringPeriodInputs worksheet (158x8) in our test artifact.
The Master Controller: How All 25+ Functions Work Together
The processInstance() function is where the entire VM0033 methodology comes together. It orchestrates all the functions we've covered and maps to multiple test artifact worksheets. This is the production-level implementation that processes a complete project instance.
Parameter Extraction Phase (Lines 1126-1184)
The function starts by extracting parameters from every section of the Guardian document:
Monitoring Data Processing Phase (Lines 1185-1221)
Next, the function processes monitoring period inputs:
Calculation Orchestration Phase (Lines 1221-1241)
Finally, the function orchestrates all the calculations in the correct order:
This is the production implementation that processes VM0033 baseline emissions, mapping directly to the 8.1BaselineEmissions worksheet (158x84) in our test artifact.
Key Production Features:
AR Tool Integration - integration with AR Tool 14 (afforestation) and AR Tool 05 (fuel)
Temporal Boundary Application - PDT/SDT constraints applied to actual emission calculations
Submergence Integration - Monitoring data affects biomass calculations
Multiple Calculation Methods - Field data, proxies, IPCC factors handled
Defensive Programming - Safe defaults and null checks throughout
Year-level Aggregation - Proper summing across strata and time
Each function processes multi-dimensional calculations across temporal and spatial boundaries
Baseline Emissions Processing
Let's examine the baseline emissions calculation in detail, cross-referencing with test artifact data:
The processProjectEmissions function calculates the project scenario emissions. It follows a parallel structure to baseline processing but applies project-specific parameters.
AR Tool Results Integration
The function begins by extracting AR Tool results for each stratum:
This corresponds to the 6.2ARTool14ProjectData worksheet (2x4 dimensions) where AR Tool 14 calculates carbon stock changes in:
Tree biomass - Column C in test data
Shrub biomass - Column D in test data
And 6.4ARTool5ProjectData worksheet (43x4 dimensions) where AR Tool 05 calculates fossil fuel consumption for project machinery and operations.
Biomass Application Logic
The function includes conditional logic for biomass components:
This checks stratum configuration flags to determine which biomass pools should be included in calculations. The corresponding test data in 7.2ProjectScenarioData worksheet (43x28 dimensions) shows these boolean flags in columns H-J.
Project Scenario Soil Emissions
The soil emissions calculation follows the same three-method approach as baseline but applies project scenario parameters:
This maps to 7.2ProjectScenarioData columns K-M which contain project scenario soil carbon change data calculated using the same methods as baseline but with project-specific parameters.
Non-CO2 Gas Calculations
The function handles CH4 and N2O emissions from soil using project-specific approaches:
This corresponds to columns N-P in 7.2ProjectScenarioData where CH4 emissions are calculated using project-specific approaches and emission factors.
Prescribed Burning Calculations
The function includes specialized calculations for prescribed burning activities:
This calculates emissions from biomass burning using emission factors for N2O and CH4, converted to CO2 equivalent using Global Warming Potentials. The calculations use the Math.pow(10, -6) conversion factor for unit consistency. Test data in columns S-U of 7.2ProjectScenarioData validate these burning emission calculations.
Annual Aggregation
The function aggregates all emission components for each monitoring year:
This produces the annual project scenario emissions that feed into net emission reduction calculations. The final aggregation creates cumulative totals across all monitoring years using the reduce operations.
The function outputs correspond to 7.3ProjectScenarioGHGEmissions worksheet (43x7 dimensions) which contains:
The processNETERR function calculates the net emission reductions for each monitoring year. This function brings together baseline and project scenario results to determine final creditable volumes.
Baseline and Project Aggregation
The function begins by aggregating baseline and project scenario results across all strata for each monitoring year:
This aggregation corresponds to 8.1NetERRCoreData worksheet (43x8 dimensions) where baseline and project scenario emissions are aggregated across all strata to produce project-level totals for each monitoring year.
Cumulative Calculations
The function maintains cumulative sums across monitoring years using running totals:
This produces cumulative emission totals that are essential for stock loss approach calculations and buffer pool management. Test data in columns C-F of 8.1NetERRCoreData shows these cumulative progressions.
Stock Loss Deduction Logic
The function implements stock loss approach deductions when enabled:
This logic deducts any emissions above the maximum soil organic carbon limit (SOC_MAX) to ensure conservative crediting. The calculation corresponds to column G in 8.1NetERRCoreData which shows stock loss deductions applied when cumulative differences exceed the methodology limits.
Fire Reduction Premium Integration
The function includes optional fire reduction premium credits:
This applies fire reduction credits based on documented fire management activities. Test data in column H of 8.1NetERRCoreData shows annual fire reduction premium applications.
NERRWE Calculation
The core net emission reduction calculation combines all components:
This formula represents the fundamental VM0033 equation: Net Emission Reductions = Baseline Emissions + Project Emissions + Fire Reduction Premium - Leakage - Stock Loss Deductions.
Capping Logic
The function applies optional annual emission reduction caps:
This ensures annual emission reductions don't exceed methodology-defined limits. Test data in 8.2NetERRAdjustments worksheet (43x6 dimensions) shows the application of caps in column C.
Uncertainty Adjustments
The function applies measurement and model uncertainties:
This incorporates both positive (allowable) and negative (model error) uncertainty adjustments. The calculation corresponds to column D in 8.2NetERRAdjustments where uncertainty percentages are applied to final emission reductions.
Buffer Pool Calculations
The function calculates buffer pool deductions using an incremental approach:
This calculates buffer deductions based on incremental changes between monitoring years rather than applying the buffer percentage to total accumulations. Test data in 8.3NetERRBufferDeduction worksheet (43x6 dimensions) validates these buffer calculations.
Final VCU Calculations
The function produces final Verified Carbon Units:
This produces the final creditable carbon units for each monitoring year. The outputs correspond to 8.4NetERRFinalCalculations worksheet (43x6 dimensions) which contains:
Gross emission reductions - Column C
Uncertainty-adjusted reductions - Column D
Buffer deductions - Column E
Final VCU issuance - Column F
The function establishes total VCU quantities that determine final carbon credit issuance amounts for the project.
Chapter Summary
You've learned how to translate scientific equations from environmental methodologies into executable code that produces verified carbon credits. The key principles:
Equation-to-Code Translation - Every methodology equation becomes a function in your customLogicBlock
Scientific Precision Required - Use defensive programming to handle edge cases while maintaining mathematical accuracy
Allcot Test Artifact is Your Benchmark - Your code must reproduce manual calculations exactly for scientific validity
Field Access Utilities enable clean implementation of complex mathematical formulas
Both JavaScript and Python supported - choose the language that best implements your equations
The VM0033 code deep dive how complex environmental methodology calculations are implemented, tested, and validated in production Guardian systems with extreme precision and comprehensive error handling
Your equation implementations are the foundation of environmental credit integrity. When coded properly, they transform scientific methodology equations into verified carbon units that represent real, measured emission reductions from restoration projects.
The next chapter explores Formula Linked Definitions (FLDs) for managing parameter relationships, and Chapter 21 covers comprehensive testing to ensure your calculations are production-ready.
// Guardian customLogicBlock structure - this is your equation implementation workspace
{
"blockType": "customLogicBlock",
"tag": "methodology_equation_implementation",
"expression": "(function calc() {\n // Implement methodology equations here\n const documents = arguments[0] || [];\n // Process monitoring data through scientific formulas\n return calculatedResults;\n})"
}
// Real document structure from final-PDD-vc.json
const document = {
document: {
credentialSubject: [
{
// Real project information
project_cert_type: "CCB v3.0 & VCS v4.4",
project_details: {
registry_vcs: {
vcs_project_description: "ABC Blue Carbon Mangrove Project..."
}
},
// The data your calculations need
project_data_per_instance: [{
project_instance: {
// Baseline emissions data
baseline_emissions: { /* monitoring data */ },
// Project emissions data
project_emissions: { /* monitoring data */ },
// Where your calculations go
net_ERR: {
total_VCU_per_instance: 0 // You'll calculate this!
}
}
}],
// Project settings and parameters
project_boundary: { /* boundary conditions */ },
individual_parameters: { /* methodology parameters */ }
}
]
}
};
// VM0033 Production Utility Functions - Clean data access patterns
function getProjectBoundaryValue(data, key) {
return data.project_boundary_baseline_scenario?.[key]?.included ??
data.project_boundary_project_scenario?.[key]?.included ??
undefined;
}
function getIndividualParam(data, key) {
return data?.individual_parameters?.[key] ?? undefined;
}
function getMonitoringValue(data, key) {
return data?.monitoring_period_inputs?.[key] ?? undefined;
}
// Example usage pattern in processInstance function:
const BaselineSoil = getProjectBoundaryValue(project_boundary, 'baseline_soil');
const GWP_CH4 = getIndividualParam(data, 'gwp_ch4');
const SubmergenceData = getMonitoringValue(data, 'submergence_monitoring_data');
// Main entry point - this is where your calculations begin
function calc() {
// Guardian passes documents as arguments[0]
const documents = arguments[0] || [];
const document = documents[0].document;
const creds = document.credentialSubject;
let totalVcus = 0;
// Process each project instance (some projects have multiple sites)
for (const cred of creds) {
for (const instance of cred.project_data_per_instance) {
// This is where the real work happens
processInstance(instance, cred.project_boundary);
// Add up the verified carbon units
totalVcus += instance.project_instance.net_ERR.total_VCU_per_instance;
}
// Set the total for this credential
cred.total_vcus = totalVcus;
}
// Guardian expects this callback
done(adjustValues(document.credentialSubject[0]));
}
def calc():
"""Main calculation function - Python version"""
import sys
documents = sys.argv[0] if len(sys.argv) > 0 else []
if not documents:
return {}
document = documents[0]['document']
creds = document['credentialSubject']
total_vcus = 0
for cred in creds:
for instance in cred.get('project_data_per_instance', []):
process_instance(instance, cred.get('project_boundary', {}))
total_vcus += instance['project_instance']['net_ERR'].get('total_VCU_per_instance', 0)
cred['total_vcus'] = total_vcus
return document['credentialSubject'][0]
def process_baseline_emissions(baseline, **kwargs):
"""Process baseline emissions - Python version"""
gwp_ch4 = kwargs.get('GWP_CH4', 28)
for year_rec in baseline.get('yearly_data_for_baseline_GHG_emissions', []):
year_t = year_rec['year_t']
for stratum in year_rec.get('annual_stratum_parameters', []):
asl = stratum.get('annual_stratum_level_parameters', {})
# Calculate emissions with safe defaults
ch4_baseline = asl.get('CH4_BSL_soil_i_t', 0)
asl['GHGBSL_soil_CH4_i_t'] = ch4_baseline * gwp_ch4
// VM0033 Production Implementation: 25+ Functions in 6 Major Categories
// ── 1. DATA ACCESS UTILITIES (Lines 7-37) ──
adjustValues() // Document post-processing
getStartYear() // Find earliest monitoring year
getProjectBoundaryValue() // Extract project boundary settings
getQuantificationValue() // Get quantification approach parameters
getIndividualParam() // Access individual methodology parameters
getMonitoringValue() // Extract monitoring period data
getWoodProductValue() // Access wood product parameters
// ── 2. TEMPORAL BOUNDARY SYSTEM (Lines 39-350) ──
processMonitoringSubmergence() // Process submergence monitoring data
getDeltaCBSLAGBiomassForStratumAndYear() // Biomass delta calculations across time
calculatePDTSDT() // Peat & Soil Depletion Time calculations
getEndPDTPerStratum() // Stratum-specific PDT boundaries
getEndSDTPerStratum() // Stratum-specific SDT boundaries
calculate_peat_strata_input_coverage_100_years() // 100-year peat projections
calculate_non_peat_strata_input_coverage_100_years() // 100-year mineral soil projections
getCBSL_i_t0() // Initial baseline carbon stocks
calculateRemainingPercentage() // Remaining depletion percentages
// ── 3. SOC CALCULATION APPROACHES (Lines 352-516) ──
totalStockApproach() // VM0033 Total Stock Approach (Section 5.2.1)
stockLossApproach() // VM0033 Stock Loss Approach (Section 5.2.2)
SOC_MAX_calculation() // Soil Organic Carbon maximum calculations
// ── 4. EMISSION PROCESSING ENGINES (Lines 517-926) ──
processBaselineEmissions() // Complete baseline scenario processing
processProjectEmissions() // Complete project scenario processing
processNETERR() // Net emission reduction calculations
// ── 5. SPECIALIZED CALCULATORS (Lines 95-180) ──
computeDeductionAllochBaseline() // Allocation deductions for baseline
computeDeductionAllochProject() // Allocation deductions for project
getFireReductionPremiumPerYear() // Fire reduction premium by year
getGHGBSL/WPS/Biomass() // GHG emission getters by type
calculateNetERRChange() // VCU change between monitoring periods
calculateNetVCU() // Net VCU calculations
// ── 6. ORCHESTRATION & CONTROL (Lines 1121-1261) ──
calculateTotalVCUPerInstance() // Sum VCUs across monitoring periods
processInstance() // Main instance processing orchestrator
calc() // Entry point function
// From er-calculations.js:181-286 - VM0033 temporal boundary calculation
function calculatePDTSDT(baseline, isProjectQuantifyBSLReduction, temporalBoundary, crediting_period) {
if (isProjectQuantifyBSLReduction) {
// Work on earliest year for temporal boundary establishment
const baselineEmissionsSorted = (baseline.yearly_data_for_baseline_GHG_emissions || [])
.slice() // Prevent mutation of original array
.sort((a, b) => a.year_t - b.year_t);
if (!baselineEmissionsSorted.length) return;
baselineEmissionsSorted[0].annual_stratum_parameters.forEach(stratum => {
const sc = stratum.stratum_characteristics ?? {};
const asl = stratum.annual_stratum_level_parameters ?? {};
// Extract critical parameters from test artifact StratumLevelInput worksheet
const {
soil_disturbance_type, // From Column C in test data
drained_20_yr, // From Column D in test data
significant_soil_erosion_as_non_peat_soil, // From Column E
RateCloss_BSL_i // From Column F - soil carbon loss rate
} = sc;
let SDT = {}; // Soil organic carbon Depletion Time
let PDT = {}; // Peat Depletion Time
// VM0033 Equation 5.1.1 - Initial soil carbon calculation
SDT.CBSL_i_t0 = (isProjectQuantifyBSLReduction && sc.is_project_quantify_BSL_reduction)
? sc.depth_soil_i_t0 * sc.VC_I_mineral_soil_portion * 10 // Convert to tC/ha
: 0;
// VM0033 Equation 5.1.2 - Soil Depletion Time calculation
if (isProjectQuantifyBSLReduction && sc.is_project_quantify_BSL_reduction) {
if (significant_soil_erosion_as_non_peat_soil || drained_20_yr) {
// Immediate depletion scenarios
SDT.t_SDT_BSL_i = 0;
} else {
// Calculate remaining time after peat depletion
const duration = crediting_period - (sc.soil_type_t0 === 'Peatsoil'
? (sc.depth_peat_i_t0 / sc.Ratepeatloss_BSL_i) // Peat depletion duration
: 0
);
if (duration > 0) {
SDT.t_SDT_BSL_i = soil_disturbance_type === "Erosion"
? 5 // Fixed 5-year erosion period per methodology
: (RateCloss_BSL_i !== 0 ? SDT.CBSL_i_t0 / RateCloss_BSL_i : 0);
}
}
} else {
SDT.t_SDT_BSL_i = 0;
}
// VM0033 Equation 5.1.3 - Peat Depletion Time for peat soils
if (sc.soil_type_t0 === 'Peatsoil' && sc.is_project_quantify_BSL_reduction) {
PDT.t_PDT_BSL_i = sc.depth_peat_i_t0 / sc.Ratepeatloss_BSL_i; // Years until peat depleted
PDT.start_PDT = 0; // Peat depletion starts immediately
PDT.end_PDT = PDT.t_PDT_BSL_i; // When peat is fully depleted
} else {
// Non-peat soils have no peat depletion
PDT.t_PDT_BSL_i = 0;
PDT.start_PDT = 0;
PDT.end_PDT = 0;
}
// Coordinate PDT and SDT temporal boundaries
SDT.start_PDT = PDT.start_PDT;
SDT.end_PDT = Math.min(PDT.end_PDT, crediting_period); // Cap at crediting period
// Soil depletion starts after peat depletion ends
if (SDT.t_SDT_BSL_i > 0) {
SDT.start_SDT = SDT.end_PDT; // Start when peat depletion ends
} else {
SDT.start_SDT = 0; // No soil depletion
}
SDT.end_SDT = SDT.start_SDT + SDT.t_SDT_BSL_i; // When soil is depleted
// Store temporal boundary data for this stratum
temporalBoundary.push({
stratum_i: stratum.stratum_i,
peat_depletion_time: {
"t_PDT_BSL_i": PDT.t_PDT_BSL_i,
"start_PDT": PDT.start_PDT,
"end_PDT": PDT.end_PDT,
// Guardian metadata for schema validation
type: temporalBoundary[0]?.peat_depletion_time?.type,
'@context': temporalBoundary[0]?.peat_depletion_time?.['@context'] ?? [],
},
soil_organic_carbon_depletion_time: {
"t_SDT_BSL_i": SDT.t_SDT_BSL_i,
'CBSL_i_t0': SDT.CBSL_i_t0,
"start_SDT": SDT.start_SDT,
"end_SDT": SDT.end_SDT,
"start_PDT": SDT.start_PDT,
"end_PDT": SDT.end_PDT,
type: temporalBoundary[0]?.soil_organic_carbon_depletion_time?.type,
'@context': temporalBoundary[0]?.soil_organic_carbon_depletion_time?.['@context'] ?? [],
},
type: temporalBoundary?.[0]?.type,
'@context': temporalBoundary?.[0]?.['@context'] ?? [],
});
});
// Remove template element after processing
temporalBoundary.shift();
}
}
// From er-calculations.js:288-298 - Access PDT end time for specific stratum
function getEndPDTPerStratum(temporal_boundary, stratum_i) {
const stratumTemporalBoundary = temporal_boundary.find(
(boundary) => boundary.stratum_i === stratum_i
);
if (stratumTemporalBoundary) {
return stratumTemporalBoundary.soil_organic_carbon_depletion_time.end_PDT;
}
return 0; // Default if no temporal boundary found
}
// From er-calculations.js:300-310 - Access SDT end time for specific stratum
function getEndSDTPerStratum(temporal_boundary, stratum_i) {
const stratumTemporalBoundary = temporal_boundary.find(
(boundary) => boundary.stratum_i === stratum_i
);
if (stratumTemporalBoundary) {
return stratumTemporalBoundary.soil_organic_carbon_depletion_time.end_SDT;
}
return 0; // Default if no temporal boundary found
}
// From er-calculations.js:312-321 - Calculate peat carbon coverage over 100 years
function calculate_peat_strata_input_coverage_100_years(data, strata) {
const match = data.find(item => String(item.stratum_i) === String(strata));
return match ? Number(match.peat_strata_input_coverage_100_years) || 0 : 0;
}
// From er-calculations.js:322-331 - Calculate mineral soil carbon coverage over 100 years
function calculate_non_peat_strata_input_coverage_100_years(data, strata) {
const match = data.find(item => String(item.stratum_i) === String(strata));
return match ? Number(match.non_peat_strata_input_coverage_100_years) || 0 : 0;
}
// From er-calculations.js:332-338 - Get initial baseline carbon stock for stratum
function getCBSL_i_t0(temporalBoundary = [], strata) {
const match = temporalBoundary.find(item => String(item.stratum_i) === String(strata));
return match ? Number(match.soil_organic_carbon_depletion_time.CBSL_i_t0) || 0 : 0;
}
// From er-calculations.js:340-349 - Calculate remaining carbon after depletion
function calculateRemainingPercentage(match, D41) {
if (match === 0) return 100; // No depletion = 100% remaining
if (D41 === 0) return 0; // No carbon = 0% remaining
const percentage = (D41 / match) * 100;
return Math.min(percentage, 100); // Cap at 100%
}
// From er-calculations.js:352-458 - Total Stock Approach implementation
function totalStockApproach(
baseline,
total_stock_approach_parameters,
peat_strata_input_coverage_100_years,
non_peat_strata_input_coverage_100_years,
temporal_boundary
) {
let sumWPS = 0; // Σ C_WPS_i_t100 × A_WPS_i_t100 (project carbon at 100 years)
let sumBSL = 0; // Σ C_BSL_i_t100 × A_BSL_i_t100 (baseline carbon at 100 years)
// Process each stratum in the first-year baseline record
baseline.yearly_data_for_baseline_GHG_emissions[0].annual_stratum_parameters
.forEach((stratum) => {
const { stratum_i } = stratum;
const charac = stratum.stratum_characteristics ?? {};
// Extract parameters with safe defaults (defensive programming)
const depth_peat_i_t0 = Number(charac.depth_peat_i_t0) || 0;
const VC_I_peat_portion = Number(charac.VC_I_peat_portion) || 0;
const VC_I_mineral_soil_portion = Number(charac.VC_I_mineral_soil_portion) || 0;
const Ratepeatloss_BSL_i = Number(charac.Ratepeatloss_BSL_i) || 0;
const RateCloss_BSL_i = Number(charac.RateCloss_BSL_i) || 0;
const A_WPS_i_t100 = Number(charac.A_WPS_i_t100) || 0;
const A_BSL_i_t100 = Number(charac.A_BSL_i_t100) || 0;
// VM0033 Equation 5.2.1.1 - Project scenario carbon at 100 years
const depth_peat_WPS_t100 =
depth_peat_i_t0 -
calculate_peat_strata_input_coverage_100_years(
peat_strata_input_coverage_100_years,
stratum_i
);
// Project organic soil carbon (preserved peat)
const C_WPS_i_t100_organic_soil =
charac.soil_type_t0 === "Peatsoil"
? depth_peat_WPS_t100 * VC_I_peat_portion * 10 // Convert to tC/ha
: 0;
// Project mineral soil carbon (preserved mineral soil)
const C_WPS_i_t100_mineral_soil =
getCBSL_i_t0(temporal_boundary, stratum_i) -
calculate_non_peat_strata_input_coverage_100_years(
non_peat_strata_input_coverage_100_years,
stratum_i
);
const C_WPS_i_t100 =
C_WPS_i_t100_organic_soil + C_WPS_i_t100_mineral_soil;
// VM0033 Equation 5.2.1.2 - Baseline scenario carbon at 100 years
const depth_peat_BSL_t100 =
depth_peat_i_t0 - 100 * Ratepeatloss_BSL_i; // Peat lost over 100 years
const C_BSL_i_t100_organic_soil =
charac.soil_type_t0 === "Peatsoil"
? depth_peat_BSL_t100 * VC_I_peat_portion * 10
: 0;
// Calculate remaining years after peat depletion for mineral soil loss
const remaining_years_after_peat_depletion_BSL =
calculateRemainingPercentage(Ratepeatloss_BSL_i, depth_peat_i_t0);
const C_BSL_i_t100_mineral_soil =
getCBSL_i_t0(temporal_boundary, stratum_i) -
remaining_years_after_peat_depletion_BSL * RateCloss_BSL_i;
const C_BSL_i_t100 =
charac.soil_type_t0 === "Peatsoil"
? C_BSL_i_t100_organic_soil
: C_BSL_i_t100_mineral_soil;
// VM0033 Equation 5.2.1.3 - Area-weighted carbon stock sums
sumWPS += C_WPS_i_t100 * A_WPS_i_t100;
sumBSL += C_BSL_i_t100 * A_BSL_i_t100;
// Store detailed calculations for each stratum
total_stock_approach_parameters.push({
stratum_i,
C_WPS_i_t100,
depthpeat_WPS_i_t100: Math.max(depth_peat_WPS_t100, 0),
C_WPS_i_t100_organic_soil,
C_WPS_i_t100_mineral_soil: Math.max(C_WPS_i_t100_mineral_soil, 0),
Depthpeat_BSL_i_t100: Math.max(depth_peat_BSL_t100, 0),
C_BSL_i_t100_organic_soil,
remaining_years_after_peat_depletion_BSL,
C_BSL_i_t100_mineral_soil: Math.max(
getCBSL_i_t0(temporal_boundary, stratum_i) - 100 * RateCloss_BSL_i,
0
),
C_BSL_i_t100,
type: total_stock_approach_parameters?.[0]?.type,
"@context": total_stock_approach_parameters?.[0]?.["@context"] ?? [],
});
});
// Remove template element after processing
total_stock_approach_parameters.shift();
// VM0033 Equation 5.2.1.4 - Check if project stocks are ≥ 105% of baseline
const condition = sumWPS >= sumBSL * 1.05;
return {
condition,
sumWPS,
sumBSL,
diff: condition ? sumWPS - sumBSL : 0, // Only credit if condition met
};
}
// From er-calculations.js:39-69 - Process submergence monitoring data
function processMonitoringSubmergence(subInputs = {}) {
const years = subInputs.submergence_monitoring_data ?? [];
for (const yrRec of years) {
const {
monitoring_year,
submergence_measurements_for_each_stratum: strata = []
} = yrRec;
// Process each stratum's submergence data for this monitoring year
for (const s of strata) {
const {
stratum_i, // Stratum identifier
is_submerged, // Boolean: is this stratum submerged?
submergence_T, // Time period of submergence (years)
area_submerged_percentage, // Percentage of stratum area submerged
C_BSL_agbiomass_i_t_ar_tool_14, // Initial baseline above-ground biomass
C_BSL_agbiomass_i_t_to_T_ar_tool_14, // Baseline biomass at time T
delta_C_BSL_agbiomass_i_t // Calculated delta (output)
} = s;
if (is_submerged) {
// VM0033 Equation 6.1 - Calculate biomass change due to submergence
const tempDelta = (C_BSL_agbiomass_i_t_ar_tool_14 - C_BSL_agbiomass_i_t_to_T_ar_tool_14) / submergence_T;
const tempDeltaFinal = tempDelta * area_submerged_percentage;
// Apply methodology constraint: negative deltas set to zero
if (tempDeltaFinal < 0) {
s.delta_C_BSL_agbiomass_i_t = 0;
} else {
s.delta_C_BSL_agbiomass_i_t = tempDeltaFinal;
}
} else {
// No submergence = no biomass change
s.delta_C_BSL_agbiomass_i_t = 0;
}
}
}
}
// From er-calculations.js:71-91 - Retrieve biomass delta for specific stratum/year
function getDeltaCBSLAGBiomassForStratumAndYear(
subInputs = {},
stratumId,
year
) {
const results = [];
// Search through all monitoring year records
for (const yrRec of subInputs.submergence_monitoring_data ?? []) {
// Check each stratum measurement in this monitoring year
for (const s of yrRec.submergence_measurements_for_each_stratum ?? []) {
// Match stratum ID and year criteria
if (String(s.stratum_i) === String(stratumId) && (year < yrRec.monitoring_year)) {
results.push({
year: yrRec.monitoring_year,
delta: s.delta_C_BSL_agbiomass_i_t,
});
}
}
}
// Return results or default if no matches found
return results.length ? results : [{ year: null, delta: 0 }];
}
// From er-calculations.js:95-115 - Baseline allocation deduction calculation
function computeDeductionAllochBaseline(params) {
const {
baseline_soil_SOC, // Is baseline soil SOC included?
soil_insitu_approach, // Soil measurement approach
soil_type, // Soil type (Peatsoil vs others)
AU5, // Soil emissions value
AV5, // Allocation percentage
BB5 // Alternative emissions value
} = params;
// No deduction if soil SOC not included or peat soil
if (baseline_soil_SOC !== true) return 0;
if (soil_type === "Peatsoil") return 0;
const fraction = AV5 / 100; // Convert percentage to fraction
// Apply appropriate calculation based on measurement approach
if (soil_insitu_approach === "Proxies" || soil_insitu_approach === "Field-collected data") {
return AU5 * fraction;
}
return BB5 * fraction;
}
// From er-calculations.js:117-137 - Project allocation deduction calculation
function computeDeductionAllochProject(params) {
const {
project_soil_SOC, // Is project soil SOC included?
soil_insitu_approach, // Soil measurement approach
soil_type, // Soil type
AK5, // Project soil emissions value
AL5, // Allocation percentage
AR5 // Alternative emissions value
} = params;
// Same logic as baseline but for project scenario
if (project_soil_SOC !== true) return 0;
if (soil_type === "Peatsoil") return 0;
const fraction = AL5 / 100;
if (soil_insitu_approach === "Proxies" || soil_insitu_approach === "Field-collected data") {
return AK5 * fraction;
}
return AR5 * fraction;
}
// From er-calculations.js:1185-1221 - Monitoring data processing
// ── MONITORING PERIOD INPUTS (Maps to MonitoringPeriodInputs worksheet) ──
const IsBaselineAbovegroundNonTreeBiomass = getMonitoringValue(data, 'is_baseline_aboveground_non_tree_biomass');
const IsProjectAbovegroundNonTreeBiomass = getMonitoringValue(data, 'is_project_aboveground_non_tree_biomass');
// Initialize monitoring data arrays
let BaselineSoilCarbonStockMonitoringData = [];
let ProjectSoilCarbonStockMonitoringData = [];
let BaselineHerbaceousVegetationMonitoringData = [];
let ProjectHerbaceousVegetationMonitoringData = [];
// Extract submergence monitoring data (critical for VM0033)
const SubmergenceMonitoringData = getMonitoringValue(data, 'submergence_monitoring_data');
// Conditional data extraction based on project boundary and quantification approach
BaselineSoilCarbonStockMonitoringData = (BaselineSoil && QuantificationCO2EmissionsSoil === 'Field-collected data') ?
getMonitoringValue(data, 'baseline_soil_carbon_stock_monitoring_data') : [];
ProjectSoilCarbonStockMonitoringData = (ProjectSoil && QuantificationCO2EmissionsSoil === 'Field-collected data') ?
getMonitoringValue(data, 'project_soil_carbon_stock_monitoring_data') : [];
BaselineHerbaceousVegetationMonitoringData = IsBaselineAbovegroundNonTreeBiomass ?
getMonitoringValue(data, 'baseline_herbaceous_vegetation_monitoring_data') : [];
ProjectHerbaceousVegetationMonitoringData = IsProjectAbovegroundNonTreeBiomass ?
getMonitoringValue(data, 'project_herbaceous_vegetation_monitoring_data') : [];
// ── WOOD PRODUCT PROJECT SCENARIO (Maps to IF Wood Product Is Included worksheet) ──
let WoodProductDjCFjBCEF = [];
let WoodProductSLFty = [];
let WoodProductOfty = [];
let WoodProductVexPcomi = [];
let WoodProductCAVGTREEi = [];
// Only extract wood product data if project boundary includes it
if (ProjectWoodProducts) {
WoodProductDjCFjBCEF = getWoodProductValue(data, 'wood_product_Dj_CFj_BCEF');
WoodProductSLFty = getWoodProductValue(data, 'wood_product_SLFty');
WoodProductOfty = getWoodProductValue(data, 'wood_product_Ofty');
WoodProductVexPcomi = getWoodProductValue(data, 'wood_product_Vex_Pcomi');
WoodProductCAVGTREEi = getWoodProductValue(data, 'wood_product_CAVG_TREE_i');
}
// From er-calculations.js:1221-1241 - Calculation orchestration
// ── CALCULATION SEQUENCE ──
// Step 1: Process submergence monitoring data (required for biomass calculations)
processMonitoringSubmergence(data.monitoring_period_inputs);
// Step 2: Establish temporal boundaries (required for all subsequent calculations)
const temporalBoundary = data.temporal_boundary;
calculatePDTSDT(data.baseline_emissions, QuantificationBaselineCO2Reduction, temporalBoundary, CreditingPeriod);
// Step 3: Calculate baseline emissions (maps to 8.1BaselineEmissions worksheet)
processBaselineEmissions(
data.baseline_emissions,
CreditingPeriod,
BaselineMethaneProductionByMicrobes,
QuantificationCH4EmissionsSoil,
GWP_CH4,
BaselineDenitrificationNitrification,
QuantificationN2OEmissionsSoil,
GWP_N2O,
data.monitoring_period_inputs,
temporalBoundary
);
// Step 4: Calculate project emissions (maps to 8.2ProjectEmissions worksheet)
processProjectEmissions(
data.project_emissions,
ProjectMethaneProductionByMicrobes,
QuantificationCH4EmissionsSoil,
GWP_CH4,
ProjectDenitrificationNitrification,
QuantificationN2OEmissionsSoil,
GWP_N2O,
EF_N2O_Burn,
EF_CH4_Burn,
ProjectBurningBiomass
);
// Step 5: Calculate SOC_MAX using appropriate approach (maps to 5.2.4_Ineligible wetland areas worksheet)
SOC_MAX_calculation(
data.baseline_emissions,
data.peat_strata_input_coverage_100_years,
data.non_peat_strata_input_coverage_100_years,
temporalBoundary,
QuantificationSOCCapApproach,
data.ineligible_wetland_areas
);
// Step 6: Calculate final net emission reductions and VCUs (maps to 8.5NetERR worksheet)
processNETERR(
data.baseline_emissions,
data.project_emissions,
data.net_ERR,
data.ineligible_wetland_areas.SOC_MAX,
QuantificationBaselineCO2Reduction,
QuantificationFireReductionPremium,
FireReductionPremiumArray,
IsNERRWEMaxCap,
NERRWE_Max,
NERError,
AllowableUncertainty,
BufferPercent
);
}
// From er-calculations.js:1243-1261 - Guardian customLogicBlock entry point
function calc() {
const document = documents[0].document; // Guardian passes documents array
const creds = document.credentialSubject; // Extract credential subjects
let totalVcus = 0;
// Process each credential (can be multiple projects)
for (const cred of creds) {
// Process each project instance (can be multiple sites per project)
for (const instance of cred.project_data_per_instance) {
// This calls the complete processInstance orchestration we covered
processInstance(instance, cred.project_boundary);
// Accumulate VCUs from this instance
totalVcus += instance.project_instance.net_ERR.total_VCU_per_instance;
}
// Store total for this credential
cred.total_vcus = totalVcus;
}
// Guardian callback - return processed document
done(adjustValues(document.credentialSubject[0]));
}
// From er-calculations.js:517-713 - Complete baseline emissions processing
function processBaselineEmissions(baseline, crediting_period, baseline_soil_CH4, soil_CH4_approach,
GWP_CH4, baseline_soil_N2O, soil_N2O_approach, GWP_N2O, monitoring_submergence_data, temporal_boundary) {
// Process each monitoring year in the baseline scenario
for (const yearRec of baseline.yearly_data_for_baseline_GHG_emissions ?? []) {
const { year_t } = yearRec;
// Process each stratum within this year
for (const stratum of yearRec.annual_stratum_parameters ?? []) {
const { stratum_i } = stratum;
const sc = stratum.stratum_characteristics ?? {};
const asl = stratum.annual_stratum_level_parameters ?? {};
// ── AR TOOL INTEGRATION ────────────────────────────────────────
// Extract AR Tool 14 results (afforestation/reforestation calculations)
asl.delta_CTREE_BSL_i_t_ar_tool_14 = stratum.ar_tool_14.delta_C_TREE;
asl.delta_CSHRUB_BSL_i_t_ar_tool_14 = stratum.ar_tool_14.delta_C_SHRUB;
// Extract AR Tool 05 results (fuel consumption calculations)
asl.ET_FC_I_t_ar_tool_5_BSL = stratum.ar_tool_05.ET_FC_y;
// Check if this stratum quantifies baseline reduction
const isProjectQuantifyBSLReduction = sc.is_project_quantify_BSL_reduction;
// ── BIOMASS CALCULATIONS ───────────────────────────────────────
// Apply above-ground non-tree biomass logic
if (asl.is_aboveground_non_tree_biomass) {
asl.delta_CSHRUB_BSL_i_t_ar_tool_14 = 0; // Zero out shrubs if non-tree biomass included
}
// VM0033 Equation 8.1.2 - Tree and shrub biomass change
asl.delta_C_BSL_tree_or_shrub_i_t = const_12_by_44 * (
asl.delta_CTREE_BSL_i_t_ar_tool_14 + asl.delta_CSHRUB_BSL_i_t_ar_tool_14
);
// Handle herbaceous vegetation
if (asl.is_aboveground_non_tree_biomass) {
asl.delta_C_BSL_herb_i_t = 0; // Set to zero if already included above
}
// ── SOIL CO2 EMISSIONS ─────────────────────────────────────────
if (asl.is_soil) {
const method = sc.co2_emissions_from_soil;
switch (method) {
case "Field-collected data":
// VM0033 Equation 8.1.1 - Direct field measurements
asl.GHGBSL_soil_CO2_i_t = -(const_44_by_12 * asl.delta_C_BSL_soil_i_t);
break;
case "Proxies":
// Use proxy data when direct measurement not available
asl.GHGBSL_soil_CO2_i_t = asl.GHG_emission_proxy_GHGBSL_soil_CO2_i_t;
break;
default:
// Sum of individual emission sources
asl.GHGBSL_soil_CO2_i_t = (asl.GHGBSL_insitu_CO2_i_t ?? 0) +
(asl.GHGBSL_eroded_CO2_i_t ?? 0) +
(asl.GHGBSL_excav_CO2_i_t ?? 0);
}
} else {
asl.GHGBSL_soil_CO2_i_t = 0; // No soil emissions for this stratum
}
// ── ALLOCATION DEDUCTIONS ──────────────────────────────────────
// Calculate allocation deductions using the utility function
asl.Deduction_alloch = computeDeductionAllochBaseline({
baseline_soil_SOC: asl.is_soil,
soil_insitu_approach: sc.co2_emissions_from_soil,
soil_type: sc.soil_type_t0,
AU5: asl.GHGBSL_soil_CO2_i_t,
AV5: asl.is_soil ? asl.percentage_C_alloch_BSL : 0,
BB5: (asl.is_soil && sc.co2_emissions_from_soil === "Others") ?
asl.GHGBSL_insitu_CO2_i_t : 0
});
// ── CH4 EMISSIONS FROM SOIL ────────────────────────────────────
if (baseline_soil_CH4) {
const method = soil_CH4_approach;
switch (method) {
case "IPCC emission factors":
asl.GHGBSL_soil_CH4_i_t = asl.IPCC_emission_factor_ch4_BSL * GWP_CH4;
break;
case "Proxies":
asl.GHGBSL_soil_CH4_i_t = asl.GHG_emission_proxy_ch4_BSL * GWP_CH4;
break;
default:
asl.GHGBSL_soil_CH4_i_t = asl.CH4_BSL_soil_i_t * GWP_CH4;
}
} else {
asl.GHGBSL_soil_CH4_i_t = 0;
}
// ── N2O EMISSIONS FROM SOIL ────────────────────────────────────
if (baseline_soil_N2O) {
const method = soil_N2O_approach;
switch (method) {
case "IPCC emission factors":
asl.GHGBSL_soil_N2O_i_t = asl.IPCC_emission_factor_n2o_BSL * GWP_N2O;
break;
case "Proxies":
asl.GHGBSL_soil_N2O_i_t = asl.N2O_emission_proxy_BSL * GWP_N2O;
break;
default:
asl.GHGBSL_soil_N2O_i_t = asl.N2O_BSL_soil_I_t * GWP_N2O;
}
} else {
asl.GHGBSL_soil_N2O_i_t = 0;
}
// ── TEMPORAL BOUNDARY APPLICATION ──────────────────────────────
// This is where the PDT/SDT system gets applied to actual calculations
const endPDT = isProjectQuantifyBSLReduction ?
getEndPDTPerStratum(temporal_boundary, stratum_i) : crediting_period;
const endSDT = isProjectQuantifyBSLReduction ?
getEndSDTPerStratum(temporal_boundary, stratum_i) : crediting_period;
if (isProjectQuantifyBSLReduction) {
const emissionsArray = baseline.yearly_data_for_baseline_GHG_emissions || [];
const startYear = getStartYear(emissionsArray);
const period = year_t - startYear + 1;
// VM0033 Equation 8.1.26 - Apply temporal boundary constraints
if (period > endPDT && period > endSDT) {
// Beyond depletion periods - no soil emissions
asl.GHGBSL_soil_i_t = 0;
} else {
// Within depletion periods - calculate full soil emissions
asl.GHGBSL_soil_i_t = asl.A_i_t * (
asl.GHGBSL_soil_CO2_i_t - asl.Deduction_alloch +
asl.GHGBSL_soil_CH4_i_t + asl.GHGBSL_soil_N2O_i_t
);
}
} else {
// No temporal boundary constraints
asl.GHGBSL_soil_i_t = asl.A_i_t * (
asl.GHGBSL_soil_CO2_i_t - asl.Deduction_alloch +
asl.GHGBSL_soil_CH4_i_t + asl.GHGBSL_soil_N2O_i_t
);
}
// ── BIOMASS CALCULATION WITH SUBMERGENCE ──────────────────────
// VM0033 Equation 8.1.23 - Integrate submergence monitoring data
const monitoring_submergence = getDeltaCBSLAGBiomassForStratumAndYear(
monitoring_submergence_data, stratum_i, yearRec.year_t
);
asl.delta_C_BSL_biomass_𝑖_t = asl.delta_C_BSL_tree_or_shrub_i_t +
asl.delta_C_BSL_herb_i_t -
monitoring_submergence[0].delta;
// ── FUEL CONSUMPTION EMISSIONS ─────────────────────────────────
if (asl.is_fossil_fuel_use) {
asl.GHGBSL_fuel_i_t = asl.ET_FC_I_t_ar_tool_5_BSL; // From AR Tool 05
} else {
asl.GHGBSL_fuel_i_t = 0;
}
}
// ── YEAR-LEVEL AGGREGATIONS ────────────────────────────────────
// Sum biomass changes across all strata for this year
const sum_delta_C_BSL_biomass = yearRec.annual_stratum_parameters
.reduce((acc, s) => acc + (Number(s.annual_stratum_level_parameters
.delta_C_BSL_biomass_𝑖_t) || 0), 0);
// Convert carbon changes to CO2 equivalent
yearRec.GHG_BSL_biomass = -(sum_delta_C_BSL_biomass * const_44_by_12);
// Sum soil emissions across all strata
const sum_GHG_BSL_soil = yearRec.annual_stratum_parameters.reduce(
(acc, s) => acc + (Number(s.annual_stratum_level_parameters.GHGBSL_soil_i_t) || 0), 0
);
yearRec.GHG_BSL_soil = sum_GHG_BSL_soil;
// Sum fuel emissions across all strata
const sum_GHG_BSL_fuel = yearRec.annual_stratum_parameters.reduce(
(acc, s) => acc + (Number(s.annual_stratum_level_parameters.GHGBSL_fuel_i_t) || 0), 0
);
yearRec.GHG_BSL_fuel = sum_GHG_BSL_fuel;
}
// ── CUMULATIVE CALCULATIONS ────────────────────────────────────────
// Calculate cumulative totals across all years
baseline.yearly_data_for_baseline_GHG_emissions.reduce((acc, rec) => {
rec.GHG_BSL_biomass = acc + rec.GHG_BSL_biomass;
return rec.GHG_BSL_biomass;
}, 0);
baseline.yearly_data_for_baseline_GHG_emissions.reduce((acc, rec) => {
rec.GHG_BSL_soil = acc + rec.GHG_BSL_soil;
return rec.GHG_BSL_soil;
}, 0);
baseline.yearly_data_for_baseline_GHG_emissions.reduce((acc, rec) => {
rec.GHG_BSL_fuel = acc + rec.GHG_BSL_fuel;
return rec.GHG_BSL_fuel;
}, 0);
// Calculate total baseline emissions per year
baseline.yearly_data_for_baseline_GHG_emissions.reduce((acc, rec) => {
rec.GHG_BSL = rec.GHG_BSL_biomass + rec.GHG_BSL_soil + rec.GHG_BSL_fuel;
return rec.GHG_BSL;
}, 0);
}
function calculatePDTSDT(baseline, isProjectQuantifyBSLReduction, temporalBoundary, crediting_period) {
let PDT = null; // Peat Depletion Time
let SDT = null; // Soil organic carbon Depletion Time
// Processing each stratum's peat depth data from test artifact
baseline.stratum_data.forEach((stratum, stratum_index) => {
if (stratum.peat_depth_data && stratum.peat_depth_data.length > 0) {
stratum.peat_depth_data.forEach((peat_data, peat_index) => {
// VM0033 Equation 5.1 - Peat Depletion Time calculation
if (peat_data.peat_thickness_cm && peat_data.subsidence_rate_cm_yr) {
const calculated_PDT = peat_data.peat_thickness_cm / peat_data.subsidence_rate_cm_yr;
// Take minimum PDT across all strata (most conservative approach)
PDT = Math.min(PDT || calculated_PDT, calculated_PDT);
}
// VM0033 Equation 5.2 - Soil organic carbon Depletion Time
if (peat_data.soc_stock_t_ha && peat_data.soc_loss_rate_t_ha_yr) {
const calculated_SDT = peat_data.soc_stock_t_ha / peat_data.soc_loss_rate_t_ha_yr;
SDT = Math.min(SDT || calculated_SDT, calculated_SDT);
}
});
}
});
// Apply crediting period constraint from methodology
const temporal_boundary_years = Math.min(PDT || crediting_period, SDT || crediting_period, crediting_period);
return {
PDT: PDT,
SDT: SDT,
temporal_boundary_years: temporal_boundary_years
};
}
// From er-calculations.js:850-920 - fire emissions processing
function processFireEmissions(baseline, temporal_boundary) {
const fireEmissionsArray = {};
baseline.stratum_data.forEach((stratum, stratum_index) => {
if (stratum.fire_data && stratum.fire_data.length > 0) {
stratum.fire_data.forEach((fire_data, fire_index) => {
const year = parseInt(fire_data.year);
// Above-ground biomass fire emissions (VM0033 Equation 8.1.3)
if (fire_data.fire_area_ha && fire_data.AGB_tC_ha &&
fire_data.combustion_factor && fire_data.CF_root) {
// AGB fire emissions calculation
const fire_emissions_AGB = fire_data.fire_area_ha *
fire_data.AGB_tC_ha *
fire_data.combustion_factor *
(44/12); // CO2 conversion factor
// Below-ground biomass fire emissions (VM0033 Equation 8.1.4)
const fire_emissions_BGB = fire_data.fire_area_ha *
fire_data.BGB_tC_ha *
fire_data.CF_root *
(44/12);
// Dead wood fire emissions (VM0033 Equation 8.1.5)
const fire_emissions_DW = fire_data.fire_area_ha *
fire_data.dead_wood_tC_ha *
fire_data.CF_dead_wood *
(44/12);
// Litter fire emissions (VM0033 Equation 8.1.6)
const fire_emissions_litter = fire_data.fire_area_ha *
fire_data.litter_tC_ha *
fire_data.CF_litter *
(44/12);
// Total fire emissions for this event
const total_fire_emissions = fire_emissions_AGB +
fire_emissions_BGB +
fire_emissions_DW +
fire_emissions_litter;
// Apply temporal boundary constraints
if (year <= temporal_boundary.temporal_boundary_years) {
fireEmissionsArray[year] = (fireEmissionsArray[year] || 0) + total_fire_emissions;
}
// Debug output for validation against test artifact
debug(`Fire emissions Year ${year}:`, {
stratum: stratum_index,
fire_event: fire_index,
AGB_emissions: fire_emissions_AGB,
BGB_emissions: fire_emissions_BGB,
total_emissions: total_fire_emissions
});
}
});
}
});
return fireEmissionsArray;
}
// From er-calculations.js:1090-1144 - Advanced stock approach selection
function totalStockApproach(baseline, crediting_period, monitoring_submergence_data) {
const stockData = {};
const approachType = baseline.soil_carbon_quantification_approach;
if (approachType === "total_stock_approach") {
// Total Stock Approach: VM0033 Equation 5.2
baseline.stratum_data.forEach((stratum, stratum_index) => {
if (stratum.soil_carbon_data && stratum.soil_carbon_data.length > 0) {
stratum.soil_carbon_data.forEach((soc_data, soc_index) => {
const year = parseInt(soc_data.year);
// Calculate SOC_MAX using VM0033 Equation 5.2 parameters
if (soc_data.area_ha && soc_data.soc_stock_t_ha) {
// SOC_MAX = Area × SOC stock × CO2 conversion factor
const soc_max = soc_data.area_ha *
soc_data.soc_stock_t_ha *
(44/12); // tCO2 conversion
// Apply depth-weighted calculation if multiple soil layers
let depth_weighted_soc = soc_max;
if (soc_data.soil_layers && soc_data.soil_layers.length > 0) {
depth_weighted_soc = soc_data.soil_layers.reduce((total, layer) => {
return total + (layer.thickness_cm * layer.soc_density_tC_m3 *
soc_data.area_ha * 0.01 * (44/12));
}, 0);
}
stockData[year] = (stockData[year] || 0) + depth_weighted_soc;
// Validate against test artifact expected values
debug(`SOC calculation Year ${year}:`, {
stratum: stratum_index,
area_ha: soc_data.area_ha,
soc_stock_t_ha: soc_data.soc_stock_t_ha,
calculated_soc_max: depth_weighted_soc
});
}
});
}
});
} else if (approachType === "stock_loss_approach") {
// Stock Loss Approach: VM0033 Equation 5.3
baseline.stratum_data.forEach((stratum, stratum_index) => {
if (stratum.soil_carbon_data && stratum.soil_carbon_data.length > 0) {
stratum.soil_carbon_data.forEach((soc_data, soc_index) => {
const year = parseInt(soc_data.year);
// Calculate annual SOC loss using VM0033 Equation 5.3
if (soc_data.area_ha && soc_data.annual_soc_loss_rate_t_ha_yr) {
const annual_soc_loss = soc_data.area_ha *
soc_data.annual_soc_loss_rate_t_ha_yr *
(44/12); // tCO2 conversion
// Apply submergence factor if wetland is partially submerged
let submergence_factor = 1.0;
if (monitoring_submergence_data && monitoring_submergence_data[year]) {
submergence_factor = monitoring_submergence_data[year].submergence_fraction;
}
const adjusted_soc_loss = annual_soc_loss * submergence_factor;
stockData[year] = (stockData[year] || 0) + adjusted_soc_loss;
}
});
}
});
}
return stockData;
}