Files
access_log/src/swd.cr
f 84ca5394f2 feat: get consumer device co2 intensity
we differ on swd model because we're including the carbon intensity of
the country of origin of the visit
2023-01-22 20:43:27 -03:00

127 lines
3.8 KiB
Crystal

# This is an adaptation of the Sustainable Web Design algorithm to
# calculate CO2 emissions per visit.
#
# In this case if we know the country of origin of the visit we can
# adapt the device emissions to the country, same as the datacenter
# variable on the original proposal.
#
# Also the CO2 per visit is adapted to the actual cache, since we know
# the status code. 304 visits are cached so the energy usage is much
# smaller and we don't need to make assumptions.
#
# @see https://sustainablewebdesign.org/calculating-digital-emissions/
require "./swd/intensity_data"
class SWD
getter bytes : Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64
getter renewable : Bool
getter datacenter_iso_code : String?
getter consumer_device_iso_code : String?
getter intensity_calculator : Symbol
GIGABYTE = 1000.0 * 1000.0 * 1000.0
KWH_PER_GB = 0.81
END_USER_DEVICE_ENERGY = 0.52
NETWORK_ENERGY = 0.14
DATACENTER_ENERGY = 0.15
PRODUCTION_ENERGY = 0.19
GLOBAL_GRID_INTENSITY = 442.0
RENEWABLES_GRID_INTENSITY = 50.0
@transfered_bytes_to_gb : Float32? | Float64? = nil
@energy_usage : Float32? | Float64? = nil
@consumer_device_energy : Float32? | Float64? = nil
@network_energy : Float32? | Float64? = nil
@production_energy : Float32? | Float64? = nil
@datacenter_energy : Float32? | Float64? = nil
@total_energy : Float32? | Float64? = nil
@consumer_device_co2 : Float32? | Float64? = nil
@network_co2 : Float32? | Float64? = nil
@production_co2 : Float32? | Float64? = nil
@datacenter_co2 : Float32? | Float64? = nil
@total_co2 : Float32? | Float64? = nil
def initialize(@bytes, @renewable = false, @datacenter_iso_code = nil, @consumer_device_iso_code = nil, @intensity_calculator = :marginal); end
def transfered_bytes_to_gb
@transfered_bytes_to_gb ||= @bytes / GIGABYTE
end
def energy_usage
@energy_usage ||= transfered_bytes_to_gb / KWH_PER_GB
end
def consumer_device_energy
@consumer_device_energy ||= energy_usage * END_USER_DEVICE_ENERGY
end
def network_energy
@network_energy ||= energy_usage * NETWORK_ENERGY
end
def production_energy
@production_energy ||= energy_usage * PRODUCTION_ENERGY
end
def total_energy
@total_energy ||= production_energy + network_energy + datacenter_energy + consumer_device_energy
end
def datacenter_energy
@datacenter_energy ||= energy_usage * DATACENTER_ENERGY
end
# Returns the carbon intensity depending on renewable status and
# location, by default uses global grid intensity.
def datacenter_carbon_intensity : Float
renewable ? RENEWABLES_GRID_INTENSITY : (localized_datacenter_intensity || GLOBAL_GRID_INTENSITY)
end
# Returns the carbon intensity of user devices depending on location,
# by default uses global grid intensity
def device_carbon_intensity : Float
localized_consumer_device_intensity || GLOBAL_GRID_INTENSITY
end
def network_carbon_intensity : Float
GLOBAL_GRID_INTENSITY
end
def global_emissions : Float
GLOBAL_GRID_INTENSITY
end
def datacenter_co2
@datacenter_co2 ||= datacenter_carbon_intensity * datacenter_energy
end
def consumer_device_co2
@consumer_device_co2 ||= device_carbon_intensity * consumer_device_energy
end
def network_co2
@network_co2 ||= network_carbon_intensity * network_energy
end
def production_co2
@production_co2 ||= production_energy * global_emissions
end
def total_co2
@total_co2 ||= production_co2 + network_co2 + datacenter_co2 + consumer_device_co2
end
def localized_datacenter_intensity : Float64?
return unless datacenter_iso_code
SWD::IntensityData.by_iso(datacenter_iso_code || "", intensity_calculator)
end
def localized_consumer_device_intensity : Float64?
return unless consumer_device_iso_code
SWD::IntensityData.by_iso(consumer_device_iso_code || "", intensity_calculator)
end
end