--- title: "MOVES emission factors in R, without a database" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{MOVES emission factors in R, without a database} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` `ef_moves()` uses local MOVES rate data to evaluate running-exhaust emission factors. It runs in R on Linux, macOS and Windows. There is no database connection, SQL query, Java process, network download or MOVES installation involved in evaluating factors. VEIN evaluates factors; it does not run MOVES. The bundled data are a small reference set. To produce factors for other counties, years, months, weather, fuels, pollutants or processes, use [movesr](https://github.com/atmoschem/movesr), a standalone R port of the MOVES rate engine that writes these tables with `movesr_export_vein()` (see [Generate factors with movesr](#generate-factors-with-movesr)). ## Vehicle type, fuel and age These inputs are explicit arguments of `ef_moves()`: | Argument | Meaning | Bundled reference data | | --- | --- | --- | | `source_type_id` | MOVES vehicle category | all 13 on-road types | | `fuel_type_id` | MOVES fuel type | every valid combination (names accepted) | | `model_year` | Vehicle manufacturing year | 2000--2025 | | `road_type_id` | MOVES road category | `5`: urban unrestricted | | `temperature` | Temperature in degrees C | 10, 20 or 30 | | `rel_humidity` | Relative humidity in percent | 50 | Use `moves_vehicles()` to list the available source-type and fuel combinations with their names and IDs. The bundled data include 38 of the 48 MOVES6-valid vehicle/fuel combinations. The five fuels are gasoline, diesel, E-85, CNG and electricity; not every fuel is valid for every vehicle type. Combinations with no modelled running-exhaust output, such as heavy electric trucks and CNG or electric motor homes, are not listed. Vehicle age is the inventory year minus `model_year`. The bundled inventory year is 2025, so `model_year = 2020` represents a five-year-old vehicle. For other datasets, choose the inventory year with `select = list(yearID = ...)`. ## Use the reference factors The small dataset shipped with VEIN contains MOVES6 running-exhaust rates for a documented U.S. reference scenario. The selected model year describes the vehicle's year of manufacture; `yearID` is the inventory year, which determines vehicle age. They must not be interchanged. ```{r} library(vein) x <- moves_factors() moves_vehicles() x$runs[, c("masterVersion", "defaultDatabaseUsed", "massUnits", "distanceUnits")] unique(x$rates[, c("yearID", "hourID", "temperature_C", "relHumidity")]) range(x$rates$modelYearID) ``` Select a vehicle and fuel by name or numeric ID. This example is a model-year 2020 diesel combination long-haul truck: ```{r} ef_moves( pollutant = "NOx", source_type_id = "Combination Long-haul Truck", fuel_type_id = "Diesel Fuel", model_year = 2020, speed = c(20, 40, 60), temperature = 20 ) ``` Here is NOx from a model-year 2020 gasoline passenger car at the hour-1 reference conditions. Numeric input speeds use km/h; results have units of g/km. ```{r} ef_moves("NOx", model_year = 2020, speed = c(20, 40, 60), factors = x, select = list(hourID = 1)) ``` `temperature` (degrees C) and `rel_humidity` (percent) can select meteorological points directly. Any remaining varying dimensions must be selected explicitly using `select`. The function never averages together different scenarios, model years, fuels, road types or temperatures. The reference fleet and fuel assumptions describe the U.S. scenario recorded in the dataset. Application to Ecuador requires a suitable vehicle/technology mapping and local fuel, meteorology, road load and inspection assumptions. The input metadata and production notes are installed with the data: ```{r} system.file("extdata", "moves", "README.md", package = "vein") ``` ## Speed functions for VEIN inventories Omitting `speed` returns a function of speed giving numeric g/km. Build one function per model year in the same order as the columns of the vehicle matrix. ```{r} years <- c(2020, 2010) curves <- EmissionFactorsList(lapply(years, function(y) { ef_moves("NOx", model_year = y, factors = x, select = list(hourID = 1)) })) veh <- data.frame(MY2020 = c(100, 200), MY2010 = c(30, 50)) lengths <- units::set_units(c(0.5, 1), "km") speeds <- data.frame(H1 = c(30, 50)) emissions <- emis(veh, lengths, ef = curves, speed = speeds, profile = matrix(1, 1, 1), simplify = TRUE, verbose = FALSE) emissions ``` ## Native bins and interpolation The default `method = "bin"` selects the exported MOVES rate for the corresponding speed bin. It preserves valid zeros and fails if a requested bin is absent. Positive speeds up to 75 mph (120.7008 km/h) are accepted. Stationary idle needs an hour-based factor and matching activity; a distance factor cannot describe it. With `method = "linear"`, factors are interpolated between nominal MOVES bin speeds: 2.5, 5, 10, ..., 75 mph. Both neighboring bins must exist. Extrapolation is disabled. This is an approximation between exported rates, so speed-bin lookup is the appropriate method for exact reproduction of the reference table. ```{r} f <- ef_moves("NOx", model_year = 2020, factors = x, select = list(hourID = 1), method = "linear") f(c(20, 40, 60)) attr(f, "moves_metadata")$scenario ``` Meteorology, model year and vehicle category are selected exactly; this first version does not interpolate those dimensions. ## Start and evaporative factors Start exhaust is **grams per engine start**, not g/km. Use the separate start data and function; there is deliberately no speed argument: ```{r} starts <- moves_start_factors() ef_moves_start( "NOx", model_year = 2020, source_type_id = "Passenger Car", fuel_type_id = "Gasoline", select = list(hourID = 1) ) ``` The evaporative data contain THC permeation (process 11), fuel vapor venting (process 12) and fuel leaks (process 13) of parked vehicles. All three are **grams per vehicle in the hour**: MOVES divides each hour's emissions by the vehicle population. MOVES writes venting to a separate table (RatePerProfile) labelled with its temperature-profile ID, but its rate has the same basis. ```{r} evap <- moves_evap_factors() ef_moves_evap( "THC", model_year = 2020, process = "all", source_type_id = "Passenger Car", fuel_type_id = "Gasoline", select = list(hourID = 1) ) ``` These event-based factors cannot be passed to `emis()`, whose factors are g/km. Multiply start factors by the number of starts, and evaporative factors by the number of vehicles in each hour. Start and evaporative data are separate MOVES runs; their provenance is recorded in `movesrun_events.csv`. MOVES builds vapor venting only from the hours selected in a run: a vehicle whose cold soak started outside those hours contributes nothing. The bundled venting comes from a three-hour run with artificial temperatures and is illustrative only; generate full-day factors (all 24 hours) for inventories. ## Brake, tire and evaporative emissions while driving Brakewear (process 9), tirewear (10) and evaporative emissions while driving (11--13) are also distance factors in MOVES `RatePerDistance`. Select them with `process`; PM2.5 and PM10 then refer to brake or tire particles (pollutants 116/106 and 117/107). The bundled reference set has running exhaust only, so this example needs factors exported by movesr or MOVES: ```{r eval=FALSE} x <- moves_factors("factors/rateperdistance.csv", "factors/movesrun.csv", "factors/vehicles.csv") ef_moves("PM2.5", model_year = 2020, speed = 50, factors = x, process = "brake", select = list(hourID = 16)) ef_moves("THC", model_year = 2020, speed = 50, factors = x, process = "venting", select = list(hourID = 16)) ``` ## Generate factors with movesr [movesr](https://github.com/atmoschem/movesr) reproduces MOVES6 rates in R (running and start exhaust, brake and tire wear, evaporative permeation, vapor venting and leaks, HC species, CO2, SO2 and PM species) from the MOVES default database stored as Parquet, without Java, MariaDB or MOVES. It reproduces the running and start factors bundled with VEIN. Local weather, fuel supply, barometric pressure and I/M program can replace the U.S. county defaults. ```{r eval=FALSE} # remotes::install_github("atmoschem/movesr") library(movesr) db <- movesr_database("movesdb-parquet") met <- movesr_default_meteorology(db, county_id = 11001, month_id = 7) running <- movesr_running_rates(db, county_id = 11001, month_id = 7, meteorology = met, pollutant_id = c(2, 3, 87)) start <- movesr_start_rates(db, county_id = 11001, month_id = 7, meteorology = met, pollutant_id = c(2, 3, 87)) evap <- movesr_evap(db, county_id = 11001, month_id = 7) movesr_export_vein("factors", running = running, start = start, evap = evap, database = db) x <- vein::moves_factors("factors/rateperdistance.csv", "factors/movesrun.csv", "factors/vehicles.csv") s <- vein::moves_start_factors("factors/rateperstart.csv", "factors/movesrun_events.csv", "factors/vehicles.csv") e <- vein::moves_evap_factors("factors/ratepervehicle.csv", "factors/rateperprofile.csv", "factors/movesrun_events.csv", "factors/vehicles.csv") ``` ## Supply additional local data Data producers can run MOVES (or movesr) once and export `RatePerDistance` and `MOVESRun` tables. R users only need those local files or a saved RDS object. The importer reads units from `MOVESRun`, preserves all original rate-table dimensions, converts pollutant mass rates to g/km, and checks for duplicate keys and missing provenance. It supports plain or gzip-compressed CSV and TSV files. ```{r eval=FALSE} x <- moves_factors("my_rateperdistance.csv", "my_movesrun.csv") saveRDS(x, "my_moves_factors.rds") # This file can be copied to an R installation on another OS. x <- readRDS("my_moves_factors.rds") f <- ef_moves("NOx", model_year = 2020, factors = x, select = list(MOVESRunID = 1, yearID = 2025, hourID = 1)) ``` This interface accepts the distance-based processes (running exhaust 1, brakewear 9, tirewear 10 and evaporative 11--13 while driving), road types 2--5, and pollutant mass rates. Starts and parked evaporative emissions have their own functions; stationary idle and energy need their own rate bases and activity denominators. MOVES base operating-mode rates also need activity weighting and adjustments before they become distance-based factors. EPA describes external use of rate tables in section 4.2.2 of the [MOVES6 technical guidance](https://www.epa.gov/moves/latest-version-motor-vehicle-emission-simulator-moves).