Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: social value calculations #297

Merged
merged 7 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions app/components/Dashboard/Cards/WhatDifference.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@

import { Household } from "@/app/models/Household";
import { Drawer } from "../../ui/Drawer";
import GraphCard from "../../ui/GraphCard"

type CardsProps = {
household: Household;
}

type CardProps = {
title: string;
figure?: string;
subfigure?: string
}

type WhatDifferenceProps = {
data: Household;
}

const Card: React.FC<React.PropsWithChildren<CardProps>> = ({ title, figure, subfigure, children }) => (
<div className="flex flex-col w-1/5">
<p className="text-lg font-semibold mb-0">{title}</p>
Expand All @@ -27,43 +35,43 @@ const Highlight: React.FC<React.PropsWithChildren> = ({ children }) => (
<span className="text-green-500 font-semibold">{children}</span>
)

const Cards = () => (
const Cards: React.FC<CardsProps> = ({ household }) => (
<div className="flex flex-wrap gap-6">
<Card title="Money saved" figure="£30,000">
<p>On housing costs over 10 years, compared to conventional ownership</p>
<Card title="Money saved" figure={`£${Math.round(household.socialValue.moneySaved).toLocaleString()}`}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<p>If Fairhold Land Purchase, on housing costs over 10 years compared to conventional ownership</p>
</Card>
<Card title="Community wealth">
<p>
Every 10 years the house would contribute{" "}
<Highlight>£120,000</Highlight> to community infrastructure and services
<Highlight>£{Math.round(household.socialValue.communityWealthDecade).toLocaleString()}</Highlight> to community infrastructure and services
zz-hh-aa marked this conversation as resolved.
Show resolved Hide resolved
</p>
</Card>
<Card title="Embodied carbon savings" figure="40 TCO₂e">
<Card title="Embodied carbon savings" figure={`${household.socialValue.embodiedCarbonSavings} TCO₂e`}>
<p>If new build, compared to typical development</p>
</Card>
<Card title="Energy savings" figure="£500" subfigure="per year">
<Card title="Energy savings" figure={`£${Math.round(household.socialValue.savingsEnergyPoundsYearly).toLocaleString()}`} subfigure="per year">
<p>Every year, if new build or retrofitted</p>
</Card>
<Card title="Savings to NHS" figure="£2,000">
<Card title="Savings to NHS" figure={`£${Math.round(household.socialValue.savingsToNHSPerHeadYearly).toLocaleString()}`}>
<p>per person, per year of a healthy, well-maintained home</p>
</Card>
<Card title="Local economy">
<p>If a new build, the home would add <Highlight>£200,000</Highlight> to the local economy, and <Highlight>£3,000</Highlight> every year, supporting <Highlight>3.6</Highlight> jobs in total</p>
<p>If new build, the home would add <Highlight>£{Math.round(household.property.newBuildPrice).toLocaleString()}</Highlight> to the local economy, and <Highlight>£{Math.round(household.lifetime.lifetimeData[0].maintenanceCost[household.property.maintenanceLevel]).toLocaleString()}</Highlight> every year, supporting <Highlight>{household.socialValue.localJobs}</Highlight> jobs in total</p>
</Card>
<Card title="Annual carbon savings" figure="3 TCO₂e">
<p>If new build, compared to average home</p>
<Card title="Annual carbon savings" figure={`${household.socialValue.operationalCarbonSavingsYearly} TCO₂e`}>
<p>If new build or retrofit, compared to average home</p>
</Card>
</div>
)

export const WhatDifference: React.FC = () => {
export const WhatDifference: React.FC<WhatDifferenceProps> = ({ data }) => {
return (
<GraphCard
title="What difference would Fairhold make to me, my community, and the world?"
subtitle="Social, environmental and economic impacts"
>
<div className="flex flex-col h-full w-3/4 justify-between">
<Cards />
<Cards household={data}/>
<Drawer
buttonTitle="Find out more about how we estimated these"
title="How we estimated these figures"
Expand Down
2 changes: 1 addition & 1 deletion app/components/ui/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const Dashboard: React.FC<DashboardProps> = ({ inputData, processedData }) => {
<HowMuchPerMonth processedData={processedData} />
<CostOverTime processedData={processedData} />
<ResaleValue data={processedData} />
<WhatDifference />
<WhatDifference data={processedData}/>
<WhatWouldYouChoose />
</div>
<Carousel
Expand Down
10 changes: 10 additions & 0 deletions app/models/Household.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ForecastParameters } from "./ForecastParameters";
import { socialRentAdjustmentTypes } from "../data/socialRentAdjustmentsRepo";
import { Lifetime, LifetimeParams } from "./Lifetime";
import { KWH_M2_YR_EXISTING_BUILDS, KWH_M2_YR_NEWBUILDS_RETROFIT } from "./constants" ;
import { SocialValue } from "./SocialValue";

const HEADS_PER_HOUSEHOLD = 2.4;

Expand Down Expand Up @@ -44,6 +45,7 @@ export class Household {
billExistingBuildYearly: number;
billNewBuildOrRetrofitYearly: number;
}
public socialValue: SocialValue;

constructor(params: ConstructorParams) {
this.incomePerPersonYearly = params.incomePerPersonYearly;
Expand All @@ -54,6 +56,7 @@ export class Household {
this.gasDemand = this.calculateGasDemand(params)
this.tenure = this.calculateTenures(params);
this.lifetime = this.calculateLifetime(params);
this.socialValue = this.calculateSocialValue();
}

private calculateTenures({
Expand Down Expand Up @@ -165,4 +168,11 @@ export class Household {
const kwhYearly = params.property.size * KWH_M2_YR_NEWBUILDS_RETROFIT[params.property.houseType]
return kwhYearly
}

private calculateSocialValue() {
return new SocialValue({
household: this
})
}

}
27 changes: 27 additions & 0 deletions app/models/SocialValue.test.ts
zz-hh-aa marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { SocialValue } from "./SocialValue";
import { createTestHousehold } from "./testHelpers";

const household = createTestHousehold();
const socialValue = household.socialValue
describe('Social Value', () => {
})
it("can be instantiated", () => {
expect(socialValue).toBeInstanceOf(SocialValue)
})

it("calculates money saved", () => {
expect(socialValue.moneySaved).toBeCloseTo(184266.707)
})
it("calculates money to community", () => {
expect(socialValue.communityWealthDecade).toBeCloseTo(5917.45)
})
it("calculates energy bill savings", () => {
expect(socialValue.savingsEnergyPoundsYearly).toBeCloseTo(554.4)
})
it("calculates amount generated for local jobs", () => {
expect(socialValue.localJobs).toBeCloseTo(3.2)
})
it("calculates operational carbon savings", () => {
expect(socialValue.operationalCarbonSavingsYearly).toBeCloseTo(1.465)
})

64 changes: 64 additions & 0 deletions app/models/SocialValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Household } from "./Household"
import { KG_CO2_PER_KWH, NHS_SAVINGS_PER_HEAD_PER_YEAR, FTE_SPEND } from "./constants";

type ConstructorParams = {
household: Household};
zz-hh-aa marked this conversation as resolved.
Show resolved Hide resolved

export class SocialValue {
public moneySaved: number;
public communityWealthDecade: number;
public embodiedCarbonSavings: number;
public savingsEnergyPoundsYearly: number;
public savingsToNHSPerHeadYearly: number;
public localJobs: number;
public operationalCarbonSavingsYearly: number;

constructor(params: ConstructorParams) {
this.moneySaved = this.calculateMoneySavedFHLP(params);
this.communityWealthDecade = this.calculateCommunityWealth(params);
this.embodiedCarbonSavings = 31.89; // TODO: update figures, not placing in constants.ts because it's placeholder; static number comparing average brick & block emissions vs. timber on slab
zz-hh-aa marked this conversation as resolved.
Show resolved Hide resolved
this.savingsEnergyPoundsYearly = this.calculateSavingsEnergyPoundsYearly(params);
this.savingsToNHSPerHeadYearly = NHS_SAVINGS_PER_HEAD_PER_YEAR
this.localJobs = this.calculateLocalJobsSupported(params);
this.operationalCarbonSavingsYearly = this.calculateCarbonSavingsYearly(params);
}

private calculateMoneySavedFHLP(params: ConstructorParams) {
let marketPurchaseTotal = 0;
let fairholdLandPurchaseTotal = 0;
const lifetime = params.household.lifetime.lifetimeData
for (let i = 0; i < 10; i++) { // TODO: should this include bills? TODO: do we want to show 10 years only? using 10 here because designs showed savings over 10 year period, not lifetime
marketPurchaseTotal += (lifetime[i].marketLandMortgageYearly + lifetime[i].newbuildHouseMortgageYearly)
fairholdLandPurchaseTotal += (lifetime[i].fairholdLandMortgageYearly + lifetime[i].depreciatedHouseMortgageYearly)
}
const moneySaved = marketPurchaseTotal - fairholdLandPurchaseTotal
return moneySaved;
}
private calculateCommunityWealth(params: ConstructorParams) {
const lifetime = params.household.lifetime.lifetimeData
let communityWealth = 0
for (let i = 0; i < 10; i++) { // TODO: decide on time period
zz-hh-aa marked this conversation as resolved.
Show resolved Hide resolved
communityWealth += lifetime[i].fairholdLandRentYearly
}
return communityWealth;
}
private calculateSavingsEnergyPoundsYearly(params: ConstructorParams) {
const gasBillSavings = params.household.gasDemand.billExistingBuildYearly - params.household.gasDemand.billNewBuildOrRetrofitYearly
return gasBillSavings;
}
// private calculateLocalEconomyBoost(params) {}
private calculateCarbonSavingsYearly(params: ConstructorParams) {
const operationalEmissionsExistingBuild = KG_CO2_PER_KWH * params.household.gasDemand.kwhExistingBuildYearly
const operationalEmissionsNewBuildOrRetrofit = KG_CO2_PER_KWH * params.household.gasDemand.kwhNewBuildOrRetrofitYearly
const operationalEmissionsSavedTCo2e = (operationalEmissionsExistingBuild - operationalEmissionsNewBuildOrRetrofit) / 1000
return operationalEmissionsSavedTCo2e
}

private calculateLocalJobsSupported(params: ConstructorParams) {
const maintenanceLevel = params.household.property.maintenanceLevel
const totalSpend = params.household.property.newBuildPrice + params.household.lifetime.lifetimeData[0].maintenanceCost[maintenanceLevel]
let jobsSupported = totalSpend / FTE_SPEND
jobsSupported = parseFloat(jobsSupported.toFixed(1));
zz-hh-aa marked this conversation as resolved.
Show resolved Hide resolved
return jobsSupported
}
}
8 changes: 7 additions & 1 deletion app/models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,10 @@ export const KWH_M2_YR_NEWBUILDS_RETROFIT = {
T: 20,
S: 51,
D: 55
}
}

export const KG_CO2_PER_KWH = 0.185 // from Carbon Independent https://www.carbonindependent.org/15.html#:~:text=Natural%20gas&text=Older%20gas%20meters%20measure%20gas,kg%20%2F%20kWh%20%5B9%5D%20.

export const NHS_SAVINGS_PER_HEAD_PER_YEAR = 24.78 // from BRE report in 2021, NHS first-year care costs due to poor housing in England individualised
zz-hh-aa marked this conversation as resolved.
Show resolved Hide resolved

export const FTE_SPEND = 60000