Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeh committed Nov 18, 2024
1 parent e7b6b7e commit a90421a
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 11 deletions.
131 changes: 125 additions & 6 deletions api/src/modules/calculations/cost.calculator.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* @description: Once we understand how the cost is calculated, we might move the common logic to this class, and extend it for each specific project type
*/
import { Injectable } from '@nestjs/common';
import { ConservationProjectInput } from '@api/modules/custom-projects/input-factory/conservation-project.input';
import { SequestrationRatesCalculator } from '@api/modules/calculations/sequestration-rate.calculator';
import { RevenueProfitCalculator } from '@api/modules/calculations/revenue-profit.calculators';
import { RestorationProjectInput } from '@api/modules/custom-projects/input-factory/restoration-project.input';
import { BaseSize } from '@shared/entities/base-size.entity';
import { BaseIncrease } from '@shared/entities/base-increase.entity';
import { CostInputs } from '@api/modules/custom-projects/dto/project-cost-inputs.dto';
import {
CostInputs,
PROJECT_DEVELOPMENT_TYPE,
} from '@api/modules/custom-projects/dto/project-cost-inputs.dto';

type CostPlanMap = {
[year: number]: number;
Expand Down Expand Up @@ -44,6 +44,8 @@ export enum COST_KEYS {
BASELINE_REASSESSMENT = 'baselineReassessment',
MRV = 'mrv',
LONG_TERM_PROJECT_OPERATING_COST = 'longTermProjectOperatingCost',
IMPLEMENTATION_LABOR = 'implementationLabor',
MAINTENANCE = 'maintenance',
}

type ProjectInput = ConservationProjectInput | RestorationProjectInput;
Expand Down Expand Up @@ -78,6 +80,7 @@ export class CostCalculator {
this.opexTotalCostPlan = this.initializeTotalCostPlan(
this.defaultProjectLength,
);
return this;
}

/**
Expand Down Expand Up @@ -110,29 +113,145 @@ export class CostCalculator {
this.projectInput.projectSizeHa - this.startingPointScaling;
const scalingFactor = Math.max(Math.round(sizeDifference / baseCost), 0);
const totalBaseCost = baseCost + increasedBy * scalingFactor * baseCost;

this.throwIfValueIsNotValid(totalBaseCost, costType);
return totalBaseCost;
}

private calculateFeasibilityAnalysisCosts() {
private feasibilityAnalysisCosts() {
const totalBaseCost = this.getTotalBaseCost(COST_KEYS.FEASIBILITY_ANALYSIS);
const feasibilityAnalysisCostPlan = this.createSimpleCostPlan(
totalBaseCost,

[-4],
);
return feasibilityAnalysisCostPlan;
}

private conservationPlanningAndAdminCosts() {
const totalBaseCost = this.getTotalBaseCost(
COST_KEYS.CONSERVATION_PLANNING_AND_ADMIN,
);
const conservationPlanningAndAdminCostPlan = this.createSimpleCostPlan(
totalBaseCost,
[-4, -3, -2, -1],
);
return conservationPlanningAndAdminCostPlan;
}

private dataCollectionAndFieldCosts() {
const totalBaseCost = this.getTotalBaseCost(
COST_KEYS.DATA_COLLECTION_AND_FIELD_COST,
);
const dataCollectionAndFieldCostPlan = this.createSimpleCostPlan(
totalBaseCost,
[-4, -3, -2],
);
return dataCollectionAndFieldCostPlan;
}

private blueCarbonProjectPlanningCosts() {
const totalBaseCost = this.getTotalBaseCost(
COST_KEYS.BLUE_CARBON_PROJECT_PLANNING,
);
const blueCarbonProjectPlanningCostPlan = this.createSimpleCostPlan(
totalBaseCost,
[-4, -3, -2],
);
return blueCarbonProjectPlanningCostPlan;
}

private communityRepresentationCosts() {
const totalBaseCost = this.getTotalBaseCost(
COST_KEYS.COMMUNITY_REPRESENTATION,
);
const projectDevelopmentType =
this.projectInput.costInputs.otherCommunityCashFlow;
const initialCost =
projectDevelopmentType === PROJECT_DEVELOPMENT_TYPE.DEVELOPMENT
? 0
: totalBaseCost;
const communityRepresentationCostPlan = this.createSimpleCostPlan(
totalBaseCost,
[-4, -3, -2, -1],
);
communityRepresentationCostPlan[-4] = initialCost;
return communityRepresentationCostPlan;
}

private establishingCarbonRightsCosts() {
const totalBaseCost = this.getTotalBaseCost(
COST_KEYS.ESTABLISHING_CARBON_RIGHTS,
);
const establishingCarbonRightsCostPlan = this.createSimpleCostPlan(
totalBaseCost,
[-3, -2, -1],
);
return establishingCarbonRightsCostPlan;
}

private validationCosts() {
const totalBaseCost = this.getTotalBaseCost(COST_KEYS.VALIDATION);
const validationCostPlan = this.createSimpleCostPlan(totalBaseCost, [-1]);
return validationCostPlan;
}

private implementationLaborCosts() {
// TODO: This needs sequestration credits calculator
// const totalBaseCost = this.getTotalBaseCost(COST_KEYS.IMPLEMENTATION_LABOR);
// const implementationLaborCostPlan = this.createSimpleCostPlan(
// totalBaseCost,
// [-1],
// );
// return implementationLaborCostPlan;
console.warn('Implementation labor costs not implemented');
return this.createSimpleCostPlan(0, [-1]);
}

private calculateMonitoringCosts() {
const totalBaseCost = this.getTotalBaseCost(COST_KEYS.MONITORING);
const monitoringCostPlan: CostPlanMap = {};
// TODO: How is this plan different from the others?
for (let year = -4; year <= this.defaultProjectLength; year++) {
if (year !== 0) {
monitoringCostPlan[year] =
year >= 1 && year <= this.projectInput.modelAssumptions.projectLength
? totalBaseCost
: 0;
}
}
return monitoringCostPlan;
}

private calculateMaintenanceCosts() {
const totalBaseCost = this.getTotalBaseCost(COST_KEYS.MAINTENANCE);
console.log('totalBaseCost', totalBaseCost);
// TODO: We need Maintenance and MaintenanceDuration values, which are present in BaseDataView but not in CostInputs.
// Are these actually CostInputs? Can be overriden? If not, we need to change the approach, and have CostInputs and BaseData values as well
const maintenanceCostPlan: CostPlanMap = {};
return this.implementationLaborCosts();
}

private throwIfValueIsNotValid(value: number, costKey: COST_KEYS): void {
if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
console.error(`Invalid number: ${value} produced for ${costKey}`);
throw new Error(`Invalid number: ${value} produced for ${costKey}`);
}
}

calculateCosts() {
// @ts-ignore
this.costPlans = {
feasibilityAnalysis: this.calculateFeasibilityAnalysisCosts(),
// feasibilityAnalysis: this.feasibilityAnalysisCosts(),
// conservationPlanningAndAdmin: this.conservationPlanningAndAdminCosts(),
// dataCollectionAndFieldCost: this.dataCollectionAndFieldCosts(),
// blueCarbonProjectPlanning: this.blueCarbonProjectPlanningCosts(),
// communityRepresentation: this.communityRepresentationCosts(),
// establishingCarbonRights: this.establishingCarbonRightsCosts(),
// validation: this.validationCosts(),
// implementationLabor: this.implementationLaborCosts(),
// monitoring: this.calculateMonitoringCosts(),
maintenance: this.calculateMaintenanceCosts(),
};
}
}
4 changes: 1 addition & 3 deletions api/src/modules/custom-projects/custom-projects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export class CustomProjectsService extends AppBaseService<
);
// TODO: Don't know where this values should come from. i.e default project length comes from the assumptions based on activity? In the python calcs, the same
// value is used regardless of the activity.

const DEFAULT_PROJECT_LENGTH = 40;
const CONSERVATION_STARTING_POINT_SCALING = 500;
const RESTORATION_STARTING_POINT_SCALING = 20000;
Expand All @@ -58,8 +57,7 @@ export class CustomProjectsService extends AppBaseService<
baseIncrease,
);

calculator.initializeCostPlans();
calculator.calculateCosts();
calculator.initializeCostPlans().calculateCosts();
return calculator.costPlans;
}

Expand Down
4 changes: 2 additions & 2 deletions shared/entities/base-data.view.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ValueTransformer, ViewColumn, ViewEntity } from "typeorm";
import { PROJECT_DEVELOPMENT_TYPE } from "@api/modules/custom-projects/dto/project-cost-inputs.dto";

export const decimalTransformer: ValueTransformer = {
to: (value: number | null) => value,
Expand Down Expand Up @@ -278,7 +279,6 @@ export class BaseDataView {

@ViewColumn({
name: "other_community_cash_flow",
transformer: decimalTransformer,
})
otherCommunityCashFlow: string;
otherCommunityCashFlow: string | PROJECT_DEVELOPMENT_TYPE;
}

0 comments on commit a90421a

Please sign in to comment.