diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 0000000..619ef45 --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,10 @@ +^.dockerignore$ +^.lintr$ +^.github/ +^Dockerfile$ +^LICENSE\.md$ +^docker-compose.yml$ +^input_dir/ +^output_dir/ +^run-pacta.sh$ +^workflow.pacta.Rproj$ diff --git a/.dockerignore b/.dockerignore index 40160b5..70fe075 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,3 +4,8 @@ **/Dockerfile **/*.md **/*.Rproj +output_dir/**/* +output_dir +pacta-data/**/* +pacta-data +docker-compose.yml diff --git a/.github/workflows/R.yml b/.github/workflows/R.yml new file mode 100644 index 0000000..1023ad1 --- /dev/null +++ b/.github/workflows/R.yml @@ -0,0 +1,22 @@ +--- +# This example file will enable R language checks on push or PR to the main +# branch. +# It will also run the checks every weeknight at midnight UTC +# +# Note the @main in `uses:` on the last line. This will call the latest version +# of the workflow from the `main` brnach in the RMI-PACTA/actions repo. You can +# also specify a tag from that repo, or a commit SHA to pin action versions. +on: + pull_request: + push: + branches: [main] + schedule: + - cron: '0 0 * * 1,2,3,4,5' + workflow_dispatch: + +name: R + +jobs: + R-package: + name: R Package Checks + uses: RMI-PACTA/actions/.github/workflows/R.yml@main diff --git a/DESCRIPTION b/DESCRIPTION index d81c081..c01b343 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,18 +1,41 @@ Package: workflow.pacta -Version: 0.0.0.9000 -Type: PACTA -Description: Run PACTA. -Depends: - dplyr, - jsonlite, - logger, - pacta.portfolio.allocate, - pacta.portfolio.audit, - pacta.portfolio.import, - pacta.portfolio.utils, - readr +Title: Run PACTA +Version: 0.0.0.9001 +Authors@R: + c(person(given = "CJ", + family = "Yetman", + role = c("aut", "ctr"), + email = "cj@cjyetman.com", + comment = c(ORCID = "0000-0001-5099-9500")), + person(given = "Jackson", + family = "Hoffart", + email = "jackson.hoffart@gmail.com", + role = c("aut", "ctr"), + comment = c(ORCID = "0000-0002-8600-5042")), + person(given = "Alex", + family = "Axthelm", + role = c("aut", "cre", "ctr"), + email = "aaxthelm@rmi.org", + comment = c(ORCID = "0000-0001-8579-8565")), + person(given = "RMI", + role = c("cph", "fnd"), + email = "PACTA4investors@rmi.org")) +Description: Run the PACTA Analysis. +License: MIT + file LICENSE +Encoding: UTF-8 +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.3.1 +Imports: + dplyr, + jsonlite, + logger, + pacta.portfolio.allocate, + pacta.portfolio.audit, + pacta.portfolio.import, + pacta.portfolio.utils, + readr Remotes: - RMI-PACTA/pacta.portfolio.allocate, - RMI-PACTA/pacta.portfolio.audit, - RMI-PACTA/pacta.portfolio.import, - RMI-PACTA/pacta.portfolio.utils + RMI-PACTA/pacta.portfolio.allocate, + RMI-PACTA/pacta.portfolio.audit, + RMI-PACTA/pacta.portfolio.import, + RMI-PACTA/pacta.portfolio.utils diff --git a/Dockerfile b/Dockerfile index dd9bec6..3f8d7de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -76,8 +76,10 @@ RUN Rscript -e "\ pak::pak(c(gh_pkgs)); \ " -COPY . / +COPY . /workflow.pacta/ + +RUN Rscript -e "pak::local_install(root = '/workflow.pacta')" # set default run behavior -ENTRYPOINT ["/run-pacta.sh"] -CMD ["input_dir/default_config.json"] +ENTRYPOINT ["/workflow.pacta/run-pacta.sh"] +CMD ["/workflow.pacta/input_dir/default_config.json"] diff --git a/LICENSE b/LICENSE index 844b42c..7ec6c95 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,2 @@ -MIT License - -Copyright (c) 2023 RMI - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +YEAR: 2024 +COPYRIGHT HOLDER: RMI diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c3f0481 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2024 RMI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..422078b --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,7 @@ +# Generated by roxygen2: do not edit by hand + +importFrom(logger,log_debug) +importFrom(logger,log_error) +importFrom(logger,log_info) +importFrom(logger,log_trace) +importFrom(logger,log_warn) diff --git a/R/calc_weights_and_outputs.R b/R/calc_weights_and_outputs.R new file mode 100644 index 0000000..d06b598 --- /dev/null +++ b/R/calc_weights_and_outputs.R @@ -0,0 +1,172 @@ +calc_weights_and_outputs <- function( + total_portfolio, + portfolio_type, + output_dir, + data_dir, + equity_market_list, + scenario_sources_list, + scenario_geographies_list, + sector_list, + has_map +) { + + log_info("Starting {portfolio_type} calculations.") + + log_debug("Subsetting {portfolio_type} portfolio.") + port_raw_all <- pacta.portfolio.allocate::create_portfolio_subset( + portfolio = total_portfolio, + portfolio_type = portfolio_type + ) + + if (pacta.portfolio.utils::data_check(port_raw_all)) { + log_info( + "{portfolio_type} portfolio has data. ", + "Beginning {portfolio_type} calculations." + ) + + log_debug("Calculating {portfolio_type} portfolio weights.") + port <- pacta.portfolio.allocate::calculate_weights( + portfolio = port_raw_all, + portfolio_type = portfolio_type + ) + + log_debug( + "Merging ABCD data from database into {portfolio_type} portfolio." + ) + if (portfolio_type == "Bonds") { + id_col <- "credit_parent_ar_company_id" + } else { + id_col <- "id" + } + id_col <- "credit_parent_ar_company_id" + port <- pacta.portfolio.allocate::merge_abcd_from_db( + portfolio = port, + portfolio_type = portfolio_type, + db_dir = data_dir, + equity_market_list = equity_market_list, + scenario_sources_list = scenario_sources_list, + scenario_geographies_list = scenario_geographies_list, + sector_list = sector_list, + id_col = id_col + ) + + # Portfolio weight methodology + log_info("Calculating portfolio weight methodology.") + log_debug("Calculating portfolio weight allocation.") + port_pw <- pacta.portfolio.allocate::port_weight_allocation(port) + + log_debug("Aggregating companies for portfolio weight calculation.") + company_pw <- pacta.portfolio.allocate::aggregate_company(port_pw) + + log_debug("Aggregating portfolio for portfolio weight calculation.") + port_pw <- pacta.portfolio.allocate::aggregate_portfolio(company_pw) + + if (portfolio_type == "Bonds") { + log_info("Ownership weight calculation not defined for Bonds. Skipping.") + port_own <- NULL + company_own <- NULL + } else { + # Ownership weight methodology + log_info("Calculating ownership methodology.") + log_debug("Calculating ownership allocation.") + port_own <- pacta.portfolio.allocate::ownership_allocation(port) + + log_debug("Aggregating companies for ownership calculation.") + company_own <- pacta.portfolio.allocate::aggregate_company(port_own) + + log_debug("Aggregating portfolio for ownership calculation.") + port_own <- pacta.portfolio.allocate::aggregate_portfolio(company_own) + } + + # Create combined outputs + log_debug("Creating combined {portfolio_type} company outputs.") + company_all <- dplyr::bind_rows(company_pw, company_own) + + log_debug("Creating combined {portfolio_type} portfolio outputs.") + port_all <- dplyr::bind_rows(port_pw, port_own) + + if (has_map && pacta.portfolio.utils::data_check(company_all)) { + log_debug("Creating {portfolio_type} map outputs.") + abcd_raw <- pacta.portfolio.allocate::get_abcd_raw(portfolio_type) + log_debug("Merging geography data into {portfolio_type} map outputs.") + map <- pacta.portfolio.allocate::merge_in_geography( + portfolio = company_all, + ald_raw = abcd_raw + ) + log_trace("Removing abcd_raw object from memory.") + rm(abcd_raw) + + log_debug("Aggregating {portfolio_type} map data.") + map <- pacta.portfolio.allocate::aggregate_map_data(map) + } + + # Technology Share Calculation + if (nrow(port_all) > 0L) { + log_debug("Calculating {portfolio_type} portfolio technology share.") + port_all <- pacta.portfolio.allocate::calculate_technology_share( + df = port_all + ) + } + + if (nrow(company_all) > 0L) { + log_debug("Calculating {portfolio_type} company technology share.") + company_all <- pacta.portfolio.allocate::calculate_technology_share( + df = company_all + ) + } + + # Scenario alignment calculations + log_debug("Calculating {portfolio_type} portfolio scenario alignment.") + port_all <- pacta.portfolio.allocate::calculate_scenario_alignment( + df = port_all + ) + + log_debug("Calculating {portfolio_type} company scenario alignment.") + company_all <- pacta.portfolio.allocate::calculate_scenario_alignment( + df = company_all + ) + + results_company_filename <- file.path( + output_dir, + paste0(portfolio_type, "_results_company.rds") + ) + if (pacta.portfolio.utils::data_check(company_all)) { + log_debug("Saving {portfolio_type} company results.") + saveRDS( + company_all, + results_company_filename + ) + } + + results_portfolio_filename <- file.path( + output_dir, + paste0(portfolio_type, "_results_portfolio.rds") + ) + if (pacta.portfolio.utils::data_check(port_all)) { + log_debug("Saving {portfolio_type} portfolio results.") + saveRDS( + port_all, + results_portfolio_filename + ) + } + + if (has_map && pacta.portfolio.utils::data_check(map)) { + log_debug("Saving {portfolio_type} map results.") + results_map_filename <- file.path( + output_dir, + paste0(portfolio_type, "_results_map.rds") + ) + saveRDS( + map, + results_map_filename + ) + } + + } else { + log_trace( + "{portfolio_type} portfolio has no rows. ", + "Skipping {portfolio_type} calculations." + ) + } + +} diff --git a/R/run_analysis.R b/R/run_analysis.R new file mode 100644 index 0000000..bec1a0a --- /dev/null +++ b/R/run_analysis.R @@ -0,0 +1,59 @@ +run_analysis <- function( + data_dir, + output_dir, + equity_market_list, + scenario_sources_list, + scenario_geographies_list, + sector_list, + has_map +) { + + # defaulting to WARN to maintain current (silent) behavior. + logger::log_threshold(Sys.getenv("LOG_LEVEL", "WARN")) + logger::log_formatter(logger::formatter_glue) + + # ------------------------------------------------------------------------- + + log_info("Starting PACTA calculations.") + + # quit if there's no relevant PACTA assets -------------------------------- + + total_portfolio_path <- file.path(output_dir, "total_portfolio.rds") + if (file.exists(total_portfolio_path)) { + total_portfolio <- readRDS(total_portfolio_path) + log_trace( + "Checking for PACTA relevant data in file: \"{total_portfolio_path}\"." + ) + pacta.portfolio.utils::quit_if_no_pacta_relevant_data(total_portfolio) + } else { + log_warn("file \"{total_portfolio_path}\" does not exist.") + warning("File \"total_portfolio.rds\" file does not exist.") + } + + + calc_weights_and_outputs( + total_portfolio = total_portfolio, + portfolio_type = "Equity", + output_dir = output_dir, + data_dir = data_dir, + equity_market_list = equity_market_list, + scenario_sources_list = scenario_sources_list, + scenario_geographies_list = scenario_geographies_list, + sector_list = sector_list, + has_map = has_map + ) + + calc_weights_and_outputs( + total_portfolio = total_portfolio, + portfolio_type = "Bonds", + output_dir = output_dir, + data_dir = data_dir, + equity_market_list = equity_market_list, + scenario_sources_list = scenario_sources_list, + scenario_geographies_list = scenario_geographies_list, + sector_list = sector_list, + has_map = has_map + ) + + log_info("Finished PACTA calculations.") +} diff --git a/R/run_audit.R b/R/run_audit.R new file mode 100644 index 0000000..36f55ba --- /dev/null +++ b/R/run_audit.R @@ -0,0 +1,140 @@ +run_audit <- function( + data_dir, + portfolio_path, + output_dir +) { + + log_info("Starting portfolio audit") + + # load necessary input data ------------------------------------------------- + log_info("Loading input data.") + + log_debug("Loading currencies.") + currencies <- readRDS(file.path(data_dir, "currencies.rds")) + + log_debug("Loading fund data.") + fund_data <- readRDS(file.path(data_dir, "fund_data.rds")) + log_debug("Loading fund list data.") + total_fund_list <- readRDS( + file.path(data_dir, "total_fund_list.rds") + ) + log_debug("Loading ISIN to fund table.") + isin_to_fund_table <- readRDS( + file.path(data_dir, "isin_to_fund_table.rds") + ) + + log_debug("Loading financial data.") + fin_data <- readRDS(file.path(data_dir, "financial_data.rds")) + + log_debug("Loading entity info.") + entity_info <- pacta.portfolio.audit::get_entity_info(dir = data_dir) + + log_debug("Loading Equity ABCD flags.") + abcd_flags_equity <- readRDS( + file.path(data_dir, "abcd_flags_equity.rds") + ) + log_debug("Loading Bonds ABCD flags.") + abcd_flags_bonds <- readRDS( + file.path(data_dir, "abcd_flags_bonds.rds") + ) + + log_debug("Loading entity emission intensities.") + entity_emission_intensities <- readRDS( + file.path(data_dir, "iss_entity_emission_intensities.rds") + ) + log_debug("Loading average sector emission intensities.") + avg_sector_eis <- readRDS( + file.path(data_dir, "iss_average_sector_emission_intensities.rds") + ) + + + # Portfolios -------------------------------------------------------------- + + log_info("Reading portfolio from file: \"{portfolio_path}\".") + portfolio_raw <- pacta.portfolio.import::read_portfolio_csv( + filepaths = portfolio_path + ) + + log_info("Processing raw portfolio.") + portfolio <- pacta.portfolio.audit::process_raw_portfolio( + portfolio_raw = portfolio_raw, + fin_data = fin_data, + fund_data = fund_data, + entity_info = entity_info, + currencies = currencies, + total_fund_list = total_fund_list, + isin_to_fund_table = isin_to_fund_table + ) + + # this is necessary because pacta.portfolio.allocate::add_revenue_split() was + # removed in #142, but later we realized that it had a sort of hidden + # behavior where if there is no revenue data it maps the + # security_mapped_sector column of the portfolio data to financial_sector, + # which is necessary later + portfolio[["has_revenue_data"]] <- FALSE + portfolio[["financial_sector"]] <- portfolio[["security_mapped_sector"]] + + log_debug("Adding ABCD flags to portfolio.") + portfolio <- pacta.portfolio.audit::create_ald_flag( + portfolio, + comp_fin_data = abcd_flags_equity, + debt_fin_data = abcd_flags_bonds + ) + + log_debug("Adding portfolio flags to portfolio.") + portfolio_total <- pacta.portfolio.audit::add_portfolio_flags( + portfolio = portfolio, + currencies = currencies + ) + + log_debug("Summarizing portfolio.") + portfolio_overview <- pacta.portfolio.audit::portfolio_summary( + portfolio_total = portfolio_total + ) + + log_debug("Creating audit file.") + audit_file <- pacta.portfolio.audit::create_audit_file( + portfolio_total = portfolio_total, + has_revenue = FALSE + ) + + log_debug("Calculating financed emissions.") + emissions_totals <- + pacta.portfolio.audit::calculate_portfolio_financed_emissions( + portfolio_total, + entity_info, + entity_emission_intensities, + avg_sector_eis + ) + + # Saving ------------------------------------------------------------------- + + log_info("Saving output.") + log_debug("output directory: \"{output_dir}\".") + + log_debug("Exporting audit information.") + pacta.portfolio.audit::export_audit_information_data( + audit_file_ = audit_file, + portfolio_total_ = portfolio_total, + folder_path = output_dir + ) + + log_debug("Exporting portfolio total.") + saveRDS( + portfolio_total, + file.path(output_dir, "total_portfolio.rds") + ) + log_debug("Exporting portfolio overview.") + saveRDS( + portfolio_overview, + file.path(output_dir, "overview_portfolio.rds") + ) + log_debug("Exporting audit file RDS.") + saveRDS(audit_file, file.path(output_dir, "audit_file.rds")) + log_debug("Exporting audit file CSV.") + readr::write_csv(audit_file, file.path(output_dir, "audit_file.csv")) + log_debug("Exporting emissions.") + saveRDS(emissions_totals, file.path(output_dir, "emissions.rds")) + + log_info("Portfolio audit finished.") +} diff --git a/R/run_pacta.R b/R/run_pacta.R new file mode 100644 index 0000000..c8241ec --- /dev/null +++ b/R/run_pacta.R @@ -0,0 +1,30 @@ +run_pacta <- function( + cfg_path = commandArgs(trailingOnly = TRUE) +) { + log_info("Running PACTA") + + # Read Params + log_trace("Determining configuration file path") + if (length(cfg_path) == 0L || cfg_path == "") { + log_warn("No configuration file specified, using default") + cfg_path <- file.path("input_dir", "default_config.json") + } + log_debug("Loading configuration from file: \"{cfg_path}\".") + cfg <- jsonlite::fromJSON(cfg_path) + + run_audit( + data_dir = cfg[["data_dir"]], + portfolio_path = cfg[["portfolio_path"]], + output_dir = cfg[["output_dir"]] + ) + run_analysis( + data_dir = cfg[["data_dir"]], + output_dir = cfg[["output_dir"]], + equity_market_list = cfg[["equity_market_list"]], + scenario_sources_list = cfg[["scenario_sources_list"]], + scenario_geographies_list = cfg[["scenario_geographies_list"]], + sector_list = cfg[["sector_list"]], + has_map = cfg[["has_map"]] + ) + log_info("PACTA run complete.") +} diff --git a/R/workflow.pacta-package.R b/R/workflow.pacta-package.R new file mode 100644 index 0000000..e181405 --- /dev/null +++ b/R/workflow.pacta-package.R @@ -0,0 +1,11 @@ +#' @keywords internal +"_PACKAGE" + +## usethis namespace: start +#' @importFrom logger log_debug +#' @importFrom logger log_error +#' @importFrom logger log_info +#' @importFrom logger log_trace +#' @importFrom logger log_warn +## usethis namespace: end +NULL diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..52e3491 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +--- +services: + workflow.pacta: + build: . + # stdin_open: true + # tty: true + # entrypoint: ["R", "--args"] + command: '/input_dir/default_config.json' + environment: + LOG_LEVEL: TRACE + volumes: + - type: bind + source: ${data_dir:-./pacta-data} + target: /pacta-data + read_only: true + - type: bind + source: ${output_dir:-./output_dir} + target: /output_dir + read_only: false + - type: bind + source: ${input_dir:-./input_dir} + target: /input_dir + read_only: true diff --git a/man/workflow.pacta-package.Rd b/man/workflow.pacta-package.Rd new file mode 100644 index 0000000..7059e0e --- /dev/null +++ b/man/workflow.pacta-package.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/workflow.pacta-package.R +\docType{package} +\name{workflow.pacta-package} +\alias{workflow.pacta} +\alias{workflow.pacta-package} +\title{workflow.pacta: Run PACTA} +\description{ +Run the PACTA Analysis. +} +\author{ +\strong{Maintainer}: Alex Axthelm \email{aaxthelm@rmi.org} (\href{https://orcid.org/0000-0001-8579-8565}{ORCID}) [contractor] + +Authors: +\itemize{ + \item CJ Yetman \email{cj@cjyetman.com} (\href{https://orcid.org/0000-0001-5099-9500}{ORCID}) [contractor] + \item Jackson Hoffart \email{jackson.hoffart@gmail.com} (\href{https://orcid.org/0000-0002-8600-5042}{ORCID}) [contractor] +} + +Other contributors: +\itemize{ + \item RMI \email{PACTA4investors@rmi.org} [copyright holder, funder] +} + +} +\keyword{internal} diff --git a/pacta_01.R b/pacta_01.R deleted file mode 100644 index aad1dc5..0000000 --- a/pacta_01.R +++ /dev/null @@ -1,130 +0,0 @@ -suppressPackageStartupMessages({ - library(pacta.portfolio.utils) - library(pacta.portfolio.import) - library(pacta.portfolio.audit) - library(dplyr) - library(readr) - library(jsonlite) -}) - -# defaulting to WARN to maintain current (silent) behavior. -logger::log_threshold(Sys.getenv("LOG_LEVEL", "WARN")) -logger::log_formatter(logger::formatter_glue) - -# ------------------------------------------------------------------------- - -logger::log_info("Starting portfolio audit") - -logger::log_trace("Determining configuration file path") -cfg_path <- commandArgs(trailingOnly = TRUE) -if (length(cfg_path) == 0 || cfg_path == "") { - logger::log_warn("No configuration file specified, using default") - cfg_path <- "input_dir/default_config.json" -} -logger::log_debug("Loading configuration from file: \"{cfg_path}\".") -cfg <- fromJSON(cfg_path) - -# load necessary input data ---------------------------------------------------- - -logger::log_info("Loading input data.") - -logger::log_debug("Loading currencies.") -currencies <- readRDS(file.path(cfg$data_dir, "currencies.rds")) - -logger::log_debug("Loading fund data.") -fund_data <- readRDS(file.path(cfg$data_dir, "fund_data.rds")) -logger::log_debug("Loading fund list data.") -total_fund_list <- readRDS(file.path(cfg$data_dir, "total_fund_list.rds")) -logger::log_debug("Loading ISIN to fund table.") -isin_to_fund_table <- readRDS(file.path(cfg$data_dir, "isin_to_fund_table.rds")) - -logger::log_debug("Loading financial data.") -fin_data <- readRDS(file.path(cfg$data_dir, "financial_data.rds")) - -logger::log_debug("Loading entity info.") -entity_info <- get_entity_info(dir = cfg$data_dir) - -logger::log_debug("Loading Equity ABCD flags.") -abcd_flags_equity <- readRDS(file.path(cfg$data_dir, "abcd_flags_equity.rds")) -logger::log_debug("Loading Bonds ABCD flags.") -abcd_flags_bonds <- readRDS(file.path(cfg$data_dir, "abcd_flags_bonds.rds")) - -logger::log_debug("Loading entity emission intensities.") -entity_emission_intensities <- readRDS(file.path(cfg$data_dir, "iss_entity_emission_intensities.rds")) -logger::log_debug("Loading average sector emission intensities.") -average_sector_emission_intensities <- readRDS( - file.path(cfg$data_dir, "iss_average_sector_emission_intensities.rds") -) - - -# Portfolios ------------------------------------------------------------------- - -logger::log_info("Reading portfolio from file: \"{cfg$portfolio_path}\".") -portfolio_raw <- read_portfolio_csv(cfg$portfolio_path) - -logger::log_info("Processing raw portfolio.") -portfolio <- process_raw_portfolio( - portfolio_raw = portfolio_raw, - fin_data = fin_data, - fund_data = fund_data, - entity_info = entity_info, - currencies = currencies, - total_fund_list = total_fund_list, - isin_to_fund_table = isin_to_fund_table -) - -# FIXME: this is necessary because pacta.portfolio.allocate::add_revenue_split() -# was removed in #142, but later we realized that it had a sort of hidden -# behavior where if there is no revenue data it maps the security_mapped_sector -# column of the portfolio data to financial_sector, which is necessary later -portfolio <- - portfolio %>% - mutate( - has_revenue_data = FALSE, - financial_sector = .data$security_mapped_sector - ) - -logger::log_debug("Adding ABCD flags to portfolio.") -portfolio <- create_ald_flag(portfolio, comp_fin_data = abcd_flags_equity, debt_fin_data = abcd_flags_bonds) - -logger::log_debug("Adding portfolio flags to portfolio.") -portfolio_total <- add_portfolio_flags(portfolio) - -logger::log_debug("Summarizing portfolio.") -portfolio_overview <- portfolio_summary(portfolio_total) - -logger::log_debug("Creating audit file.") -audit_file <- create_audit_file(portfolio_total, has_revenue = FALSE) - -logger::log_debug("Calculating financed emissions.") -emissions_totals <- calculate_portfolio_financed_emissions( - portfolio_total, - entity_info, - entity_emission_intensities, - average_sector_emission_intensities -) - -# Saving ----------------------------------------------------------------------- - -logger::log_info("Saving output.") -logger::log_debug("output directory: \"{cfg$output_dir}\".") - -logger::log_debug("Exporting audit information.") -export_audit_information_data( - audit_file_ = audit_file, - portfolio_total_ = portfolio_total, - folder_path = cfg$output_dir -) - -logger::log_debug("Exporting portfolio total.") -saveRDS(portfolio_total, file.path(cfg$output_dir, "total_portfolio.rds")) -logger::log_debug("Exporting portfolio overview.") -saveRDS(portfolio_overview, file.path(cfg$output_dir, "overview_portfolio.rds")) -logger::log_debug("Exporting audit file RDS.") -saveRDS(audit_file, file.path(cfg$output_dir, "audit_file.rds")) -logger::log_debug("Exporting audit file CSV.") -write_csv(audit_file, file.path(cfg$output_dir, "audit_file.csv")) -logger::log_debug("Exporting emissions.") -saveRDS(emissions_totals, file.path(cfg$output_dir, "emissions.rds")) - -logger::log_info("Portfolio audit finished.") diff --git a/pacta_02.R b/pacta_02.R deleted file mode 100644 index cee4e05..0000000 --- a/pacta_02.R +++ /dev/null @@ -1,261 +0,0 @@ -suppressPackageStartupMessages({ - library(pacta.portfolio.utils) - library(pacta.portfolio.allocate) - library(dplyr) - library(jsonlite) -}) - -# defaulting to WARN to maintain current (silent) behavior. -logger::log_threshold(Sys.getenv("LOG_LEVEL", "WARN")) -logger::log_formatter(logger::formatter_glue) - -# ------------------------------------------------------------------------------ - -logger::log_info("Starting PACTA calculations.") - -logger::log_trace("Determining configuration file path") -cfg_path <- commandArgs(trailingOnly = TRUE) -if (length(cfg_path) == 0 || cfg_path == "") { - logger::log_warn("No configuration file specified, using default") - cfg_path <- "input_dir/default_config.json" -} -logger::log_debug("Loading configuration from file: \"{cfg_path}\".") -cfg <- fromJSON(cfg_path) - -# quit if there's no relevant PACTA assets ------------------------------------- - -total_portfolio_path <- file.path(cfg$output_dir, "total_portfolio.rds") -if (file.exists(total_portfolio_path)) { - total_portfolio <- readRDS(total_portfolio_path) - logger::log_trace("Checking for PACTA relevant data in file: \"{total_portfolio_path}\".") - quit_if_no_pacta_relevant_data(total_portfolio) -} else { - logger::log_warn("file \"{total_portfolio_path}\" does not exist.") - warning("This is weird... the `total_portfolio.rds` file does not exist in the `30_Processed_inputs` directory.") -} - - -# Equity ----------------------------------------------------------------------- - -logger::log_info("Starting equity calculations.") - -logger::log_debug("Subsetting equity portfolio.") -port_raw_all_eq <- create_portfolio_subset(total_portfolio, "Equity") - -if (inherits(port_raw_all_eq, "data.frame") && nrow(port_raw_all_eq) > 0) { - logger::log_info("Equity portfolio has data. Beginning equity calculations.") - map_eq <- NA - company_all_eq <- NA - port_all_eq <- NA - - logger::log_debug("Calculating quity portfolio weights.") - port_eq <- calculate_weights(port_raw_all_eq, "Equity") - - logger::log_debug("Merging ABCD data from database into equity portfolio.") - port_eq <- merge_abcd_from_db( - portfolio = port_eq, - portfolio_type= "Equity", - db_dir = cfg$data_dir, - equity_market_list = cfg$equity_market_list, - scenario_sources_list = cfg$scenario_sources_list, - scenario_geographies_list = cfg$scenario_geographies_list, - sector_list = cfg$sector_list, - id_col = "id" - ) - - # Portfolio weight methodology - logger::log_info("Calculating portfolio weight methodology.") - logger::log_debug("Calculating portfolio weight allocation.") - port_pw_eq <- port_weight_allocation(port_eq) - - logger::log_debug("Aggregating companies for portfolio weight calculation.") - company_pw_eq <- aggregate_company(port_pw_eq) - - logger::log_debug("Aggregating portfolio for portfolio weight calculation.") - port_pw_eq <- aggregate_portfolio(company_pw_eq) - - # Ownership weight methodology - logger::log_info("Calculating ownership methodology.") - logger::log_debug("Calculating ownership allocation.") - port_own_eq <- ownership_allocation(port_eq) - - logger::log_debug("Aggregating companies for ownership calculation.") - company_own_eq <- aggregate_company(port_own_eq) - - logger::log_debug("Aggregating portfolio for ownership calculation.") - port_own_eq <- aggregate_portfolio(company_own_eq) - - # Create combined outputs - logger::log_debug("Creating combined equity company outputs.") - company_all_eq <- bind_rows(company_pw_eq, company_own_eq) - - logger::log_debug("Creating combined equity portfolio outputs.") - port_all_eq <- bind_rows(port_pw_eq, port_own_eq) - - if (cfg$has_map) { - logger::log_debug("Creating equity map outputs.") - abcd_raw_eq <- get_abcd_raw("Equity") - logger::log_debug("Merging geography data into equity map outputs.") - map_eq <- merge_in_geography(company_all_eq, abcd_raw_eq) - logger::log_trace("Removing abcd_raw_eq object from memory.") - rm(abcd_raw_eq) - - logger::log_debug("Aggregating equity map data.") - map_eq <- aggregate_map_data(map_eq) - } - - # Technology Share Calculation - logger::log_debug("Calculating equity portfolio technology share.") - port_all_eq <- calculate_technology_share(port_all_eq) - - logger::log_debug("Calculating equity company technology share.") - company_all_eq <- calculate_technology_share(company_all_eq) - - # Scenario alignment calculations - logger::log_debug("Calculating equity portfolio scenario alignment.") - port_all_eq <- calculate_scenario_alignment(port_all_eq) - - logger::log_debug("Calculating equity company scenario alignment.") - company_all_eq <- calculate_scenario_alignment(company_all_eq) - - if (data_check(company_all_eq)) { - logger::log_debug("Saving equity company results.") - saveRDS(company_all_eq, file.path(cfg$output_dir, "Equity_results_company.rds")) - } - - if (data_check(port_all_eq)) { - logger::log_debug("Saving equity portfolio results.") - saveRDS(port_all_eq, file.path(cfg$output_dir, "Equity_results_portfolio.rds")) - } - - if (cfg$has_map) { - if (data_check(map_eq)) { - logger::log_debug("Saving equity map results.") - saveRDS(map_eq, file.path(cfg$output_dir, "Equity_results_map.rds")) - } - } - - logger::log_trace("Removing equity portfolio objects from memory.") - rm(port_raw_all_eq) - rm(port_eq) - rm(port_pw_eq) - rm(port_own_eq) - rm(port_all_eq) - rm(company_pw_eq) - rm(company_own_eq) - rm(company_all_eq) -} else { - logger::log_trace( - "Equity portfolio has no rows. Skipping equity calculations." - ) -} - -# Bonds ------------------------------------------------------------------------ - -logger::log_info("Starting bonds calculations.") -port_raw_all_cb <- create_portfolio_subset(total_portfolio, "Bonds") - -if (inherits(port_raw_all_cb, "data.frame") && nrow(port_raw_all_cb) > 0) { - logger::log_info("Bonds portfolio has data. Beginning bonds calculations.") - map_cb <- NA - company_all_cb <- NA - port_all_cb <- NA - - logger::log_debug("Calculating bonds portfolio weights.") - port_cb <- calculate_weights(port_raw_all_cb, "Bonds") - - logger::log_debug("Merging ABCD data from database into bonds portfolio.") - port_cb <- merge_abcd_from_db( - portfolio = port_cb, - portfolio_type = "Bonds", - db_dir = cfg$data_dir, - equity_market_list = cfg$equity_market_list, - scenario_sources_list = cfg$scenario_sources_list, - scenario_geographies_list = cfg$scenario_geographies_list, - sector_list = cfg$sector_list, - id_col = "credit_parent_ar_company_id" - ) - - # Portfolio weight methodology - logger::log_info("Calculating bonds portfolio weight methodology.") - logger::log_debug("Calculating bonds portfolio weight allocation.") - port_pw_cb <- port_weight_allocation(port_cb) - - logger::log_debug( - "Aggregating companies for bonds portfolio weight calculation." - ) - company_pw_cb <- aggregate_company(port_pw_cb) - - logger::log_debug( - "Aggregating portfolio for bonds portfolio weight calculation." - ) - port_pw_cb <- aggregate_portfolio(company_pw_cb) - - # Create combined outputs - logger::log_debug("Creating combined bonds company outputs.") - company_all_cb <- company_pw_cb - - logger::log_debug("Creating combined bonds portfolio outputs.") - port_all_cb <- port_pw_cb - - if (cfg$has_map) { - if (data_check(company_all_cb)) { - logger::log_debug("Creating bonds map outputs.") - abcd_raw_cb <- get_abcd_raw("Bonds") - logger::log_debug("Merging geography data into bonds map outputs.") - map_cb <- merge_in_geography(company_all_cb, abcd_raw_cb) - logger::log_trace("Removing abcd_raw_cb object from memory.") - rm(abcd_raw_cb) - - logger::log_debug("Aggregating bonds map data.") - map_cb <- aggregate_map_data(map_cb) - } - } - - # Technology Share Calculation - if (nrow(port_all_cb) > 0) { - logger::log_debug("Calculating bonds portfolio technology share.") - port_all_cb <- calculate_technology_share(port_all_cb) - } - - if (nrow(company_all_cb) > 0) { - logger::log_debug("Calculating bonds company technology share.") - company_all_cb <- calculate_technology_share(company_all_cb) - } - - # Scenario alignment calculations - logger::log_debug("Calculating bonds portfolio scenario alignment.") - port_all_cb <- calculate_scenario_alignment(port_all_cb) - - logger::log_debug("Calculating bonds company scenario alignment.") - company_all_cb <- calculate_scenario_alignment(company_all_cb) - - if (data_check(company_all_cb)) { - logger::log_debug("Saving bonds company results.") - saveRDS(company_all_cb, file.path(cfg$output_dir, "Bonds_results_company.rds")) - } - - if (data_check(port_all_cb)) { - logger::log_debug("Saving bonds portfolio results.") - saveRDS(port_all_cb, file.path(cfg$output_dir, "Bonds_results_portfolio.rds")) - } - - if (cfg$has_map) { - if (data_check(map_cb)) { - logger::log_debug("Saving bonds map results.") - saveRDS(map_cb, file.path(cfg$output_dir, "Bonds_results_map.rds")) - } - } - - logger::log_trace("Removing bonds portfolio objects from memory.") - rm(port_raw_all_cb) - rm(port_cb) - rm(port_pw_cb) - rm(port_all_cb) - rm(company_pw_cb) - rm(company_all_cb) -} else { - logger::log_trace("Bonds portfolio has no rows. Skipping bonds calculations.") -} - -logger::log_info("Finished PACTA calculations.") diff --git a/run-pacta.sh b/run-pacta.sh index f13fe59..00138df 100755 --- a/run-pacta.sh +++ b/run-pacta.sh @@ -3,5 +3,4 @@ # Set permissions so that new files can be deleted/overwritten outside docker umask 000 -Rscript --vanilla pacta_01.R "${1}" \ - && Rscript --vanilla pacta_02.R "${1}" +Rscript --vanilla -e 'workflow.pacta:::run_pacta()' "${1}"