Data

# === SAMPLE FILTERS (adjust here) ===
MIN_CAPACITY_MCM <- 1       # Set to NA to skip capacity filter
REQUIRE_VOLUME   <- TRUE   # Set to TRUE to drop dams without volume_max
analysis_data <- readRDS(file.path(strategic_dir,
                                    "strategic_placement_analysis_data.rds"))

# Apply sample filters
n_full <- nrow(analysis_data)
if (REQUIRE_VOLUME) {
  analysis_data <- analysis_data %>% filter(!is.na(capacity_mcm))
}
if (!is.na(MIN_CAPACITY_MCM)) {
  analysis_data <- analysis_data %>%
    filter(is.na(capacity_mcm) | capacity_mcm >= MIN_CAPACITY_MCM)
}

# Check which version columns are available
has_v1 <- "suitability_ratio_v1" %in% names(analysis_data)
has_v3 <- "suitability_ratio_v3" %in% names(analysis_data)

# Check if constrained (same-country) columns are available
has_constrained <- "suitability_ratio_c" %in% names(analysis_data) &&
  sum(!is.na(analysis_data$suitability_ratio_c)) > 30
has_constrained_v1 <- has_constrained &&
  "suitability_ratio_c_v1" %in% names(analysis_data)
has_constrained_v3 <- has_constrained &&
  "suitability_ratio_c_v3" %in% names(analysis_data)

# Fix: Force SR_c = SR_u for domestic dams (centroid-based country assignment
# can misclassify border-adjacent segments, creating spurious SR_u != SR_c)
if (has_constrained) {
  dom <- analysis_data$transboundary == 0
  analysis_data$suitability_ratio_c[dom] <- analysis_data$suitability_ratio[dom]
  analysis_data$optimal_outside_country[dom] <- FALSE
  for (v in c("v1", "v2", "v3")) {
    col_c <- paste0("suitability_ratio_c_", v)
    col_u <- paste0("suitability_ratio_", v)
    col_out <- paste0("optimal_outside_", v)
    if (col_c %in% names(analysis_data))
      analysis_data[[col_c]][dom] <- analysis_data[[col_u]][dom]
    if (col_out %in% names(analysis_data))
      analysis_data[[col_out]][dom] <- FALSE
  }
}

# Transboundary subset with along-river border distance
tb_data <- analysis_data %>%
  filter(transboundary == 1, !is.na(dam_dist_border))

1. Sample Overview

This section describes the analysis sample after filtering from the Global Dam Tracker (GDAT) universe. Dams are retained if they can be matched to a HydroRIVERS segment, have a recorded completion year between 1950–2025, and have a known primary purpose. The suitability computation considers all candidate river segments within a $$200 km buffer along the river network, scoring each on hydropower potential (flow, catchment area, hydraulic head) and population exposure at the time of construction.

tibble(
  Statistic = c("Total dams", "Transboundary", "Domestic",
                "TB with along-river border distance",
                "Mean candidates per dam",
                "Median candidates per dam"),
  Value = c(
    nrow(analysis_data),
    sum(analysis_data$transboundary),
    sum(1 - analysis_data$transboundary),
    nrow(tb_data),
    sprintf("%.0f", mean(analysis_data$n_candidates, na.rm = TRUE)),
    sprintf("%.0f", median(analysis_data$n_candidates, na.rm = TRUE))
  )
) %>%
  kbl(caption = "Sample Summary") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Sample Summary
Statistic Value
Total dams 4901
Transboundary 2634
Domestic 2267
TB with along-river border distance 745
Mean candidates per dam 156
Median candidates per dam 59
analysis_data %>%
  count(purpose_category, transboundary) %>%
  mutate(transboundary = ifelse(transboundary == 1,
                                 "Transboundary", "Domestic")) %>%
  pivot_wider(names_from = transboundary, values_from = n, values_fill = 0) %>%
  mutate(Total = Domestic + Transboundary) %>%
  arrange(desc(Total)) %>%
  kbl(caption = "Dams by Purpose and Transboundary Status") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Dams by Purpose and Transboundary Status
purpose_category Domestic Transboundary Total
Other 692 688 1380
Flood Control 489 656 1145
Irrigation 456 598 1054
Hydropower 336 396 732
Water Supply 294 296 590
analysis_data %>%
  count(decade, transboundary) %>%
  mutate(transboundary = ifelse(transboundary == 1,
                                 "Transboundary", "Domestic")) %>%
  pivot_wider(names_from = transboundary, values_from = n, values_fill = 0) %>%
  mutate(Total = Domestic + Transboundary) %>%
  kbl(caption = "Dams by Decade") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Dams by Decade
decade Domestic Transboundary Total
1950 363 402 765
1960 671 705 1376
1970 538 651 1189
1980 383 436 819
1990 220 220 440
2000 67 131 198
2010 24 63 87
2020 1 26 27

2. Suitability Ratios Across Specifications

We compute three alternative suitability indices to ensure our results are not driven by a particular functional form:

  • V1 (Baseline): Raw product of discharge, upstream catchment area, and hydraulic head. Heavily right-skewed because a few candidate sites with large rivers dominate the score.
  • V2 (Gradient-weighted): Applies square-root transforms to flow and catchment area, giving less weight to extreme values. This is our primary specification.
  • V3 (Percentile Ranks): Converts each component to its within-river-system percentile rank before combining. This ensures each component contributes roughly equally and yields ratios on a more interpretable 0–1 scale.

The suitability ratio divides a dam’s actual site score by the best available score within $$200 km along the river network. A ratio of 1 means the dam sits at the optimal site; lower values indicate the dam was placed at a less suitable location relative to nearby alternatives.

versions <- c("v1", "v2", "v3")[c(has_v1, TRUE, has_v3)]

suit_summary <- analysis_data %>%
  mutate(tb_label = ifelse(transboundary == 1,
                            "Transboundary", "Domestic")) %>%
  group_by(tb_label) %>%
  summarise(
    n = n(),
    across(
      all_of(paste0("suitability_ratio_", versions)),
      list(mean = ~mean(.x, na.rm = TRUE),
           median = ~median(.x, na.rm = TRUE)),
      .names = "{.col}_{.fn}"
    ),
    .groups = "drop"
  )

suit_summary %>%
  kbl(digits = 3,
      caption = "Suitability Ratio by Specification and TB Status") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE) %>%
  add_header_above(c(" " = 2,
                      "V1 (Baseline)" = 2,
                      "V2 (Gradient)" = 2,
                      "V3 (Percentile)" = 2))
Suitability Ratio by Specification and TB Status
V1 (Baseline)
V2 (Gradient)
V3 (Percentile)
tb_label n suitability_ratio_v1_mean suitability_ratio_v1_median suitability_ratio_v2_mean suitability_ratio_v2_median suitability_ratio_v3_mean suitability_ratio_v3_median
Domestic 2267 0.146 0.009 0.241 0.111 0.696 0.706
Transboundary 2634 0.080 0.002 0.140 0.057 0.647 0.655

Quick t-tests (no controls)

trb <- analysis_data %>% filter(transboundary == 1)
dom <- analysis_data %>% filter(transboundary == 0)

for (v in versions) {
  col <- paste0("suitability_ratio_", v)
  tt <- t.test(trb[[col]], dom[[col]])
  cat(sprintf(
    "**%s**: TRB mean = %.3f, DOM mean = %.3f, diff = %.3f (p = %.4f)  \n",
    toupper(v), tt$estimate[1], tt$estimate[2],
    tt$estimate[1] - tt$estimate[2], tt$p.value
  ))
}

V1: TRB mean = 0.080, DOM mean = 0.146, diff = -0.066 (p = 0.0000)
V2: TRB mean = 0.140, DOM mean = 0.241, diff = -0.101 (p = 0.0000)
V3: TRB mean = 0.647, DOM mean = 0.696, diff = -0.049 (p = 0.0000)

Across all three specifications, transboundary dams have lower mean suitability ratios than domestic dams, and the differences are statistically significant at conventional levels. The gap is largest for V1 and V2, which are more sensitive to extreme suitability scores on large rivers. V3 compresses the distribution by construction (percentile ranks are bounded between 0 and 1), so the raw gap is smaller, but it remains highly significant. This pattern is consistent with the hypothesis that transboundary dams face political or strategic constraints that lead to placement at sites that are sub-optimal from a purely engineering perspective.


3. Suitability Ratio Density Plots

The density plots below visualize the full distribution of suitability ratios for domestic and transboundary dams. Key patterns to look for: (1) whether the transboundary distribution is shifted leftward (toward worse sites), and (2) whether the shapes differ—for instance, a heavier left tail for transboundary dams would suggest a subset of particularly poorly-sited dams.

plot_density <- function(data, col, vlabel) {
  ggplot(data, aes(x = .data[[col]],
                    fill = factor(transboundary))) +
    geom_density(alpha = 0.6) +
    geom_vline(xintercept = 1, linetype = "dashed", color = "black") +
    scale_fill_manual(
      values = c("0" = "steelblue", "1" = "coral"),
      labels = c("Domestic", "Transboundary"),
      name = ""
    ) +
    labs(
      title = paste("Distribution of Suitability Ratios -", vlabel),
      subtitle = "Ratio of 1 = dam at optimal location; lower = worse site quality",
      x = "Suitability Ratio (Actual / Optimal)",
      y = "Density"
    ) +
    theme_minimal(base_size = 13) +
    theme(legend.position = "bottom")
}

V3 (Percentile Ranks)

plot_density(analysis_data, "suitability_ratio_v3",
             "V3 (Percentile Ranks)")

V2 (Gradient-weighted)

plot_density(analysis_data, "suitability_ratio_v2",
             "V2 (Gradient-weighted)")

V1 (Baseline)

plot_density(analysis_data, "suitability_ratio_v1",
             "V1 (Baseline)")


4. Suitability by Dam Purpose

Different dam purposes may have different site selection criteria. Hydropower dams, for example, are strongly constrained by topography and river flow, so siting distortions may be more visible. Irrigation dams may have more flexibility in placement. The bar charts below show mean suitability ratios by purpose category, separately for domestic and transboundary dams, with 95% confidence intervals.

plot_purpose <- function(data, col, vlabel) {
  pdata <- data %>%
    group_by(transboundary, purpose_category) %>%
    summarise(
      mean_suit = mean(.data[[col]], na.rm = TRUE),
      se_suit = sd(.data[[col]], na.rm = TRUE) / sqrt(n()),
      n = n(),
      .groups = "drop"
    ) %>%
    filter(n >= 5) %>%
    mutate(tb_label = ifelse(transboundary == 1,
                              "Transboundary", "Domestic"))

  ggplot(pdata, aes(x = purpose_category, y = mean_suit,
                     fill = tb_label)) +
    geom_col(position = position_dodge(width = 0.8), width = 0.7) +
    geom_errorbar(
      aes(ymin = mean_suit - 1.96 * se_suit,
          ymax = mean_suit + 1.96 * se_suit),
      position = position_dodge(width = 0.8), width = 0.3
    ) +
    scale_fill_manual(values = c("Domestic" = "steelblue",
                                  "Transboundary" = "coral")) +
    labs(
      title = paste("Suitability by Purpose -", vlabel),
      subtitle = "Error bars show 95% confidence intervals",
      x = "Dam Purpose", y = "Mean Suitability Ratio", fill = ""
    ) +
    theme_minimal(base_size = 13) +
    theme(legend.position = "bottom",
          axis.text.x = element_text(angle = 45, hjust = 1))
}

V3 (Percentile Ranks)

plot_purpose(analysis_data, "suitability_ratio_v3",
             "V3 (Percentile Ranks)")

V2 (Gradient-weighted)

plot_purpose(analysis_data, "suitability_ratio_v2",
             "V2 (Gradient-weighted)")

V1 (Baseline)

plot_purpose(analysis_data, "suitability_ratio_v1",
             "V1 (Baseline)")


5. Position Deviation from Optimal

Position deviation measures the distance (in km along the river) between where a dam was actually built and where the suitability-maximizing site would have been. Positive values mean the dam was placed upstream of the optimal site; negative values mean downstream. If transboundary dams are systematically displaced from optimal locations—particularly toward upstream positions where the river is still within the dam-building country’s territory—this would suggest strategic positioning motivations.

plot_posdev <- function(data, col, vlabel) {
  ggplot(data, aes(x = .data[[col]],
                    fill = factor(transboundary))) +
    geom_density(alpha = 0.6) +
    geom_vline(xintercept = 0, linetype = "dashed", color = "black") +
    scale_fill_manual(
      values = c("0" = "steelblue", "1" = "coral"),
      labels = c("Domestic", "Transboundary"),
      name = ""
    ) +
    coord_cartesian(xlim = c(-200, 200)) +
    labs(
      title = paste("Position Deviation from Optimal -", vlabel),
      subtitle = "Positive = upstream of optimal; negative = downstream",
      x = "Position Deviation (km)", y = "Density"
    ) +
    theme_minimal(base_size = 13) +
    theme(legend.position = "bottom")
}

V3 (Percentile Ranks)

plot_posdev(analysis_data, "position_deviation_km_v3",
            "V3 (Percentile Ranks)")

V2 (Gradient-weighted)

plot_posdev(analysis_data, "position_deviation_km_v2",
            "V2 (Gradient-weighted)")

V1 (Baseline)

plot_posdev(analysis_data, "position_deviation_km_v1",
            "V1 (Baseline)")


6. Transboundary Dams: Suitability vs Border Distance

For the subset of transboundary dams where we can measure along-river distance to the nearest international border crossing, we examine whether proximity to the border is associated with worse site quality. If the transboundary suitability penalty is driven by geographic constraints near borders (e.g., narrow valleys, unfavorable terrain at boundary locations), we would expect dams closer to the border to have systematically lower suitability. If instead the penalty reflects broader political-strategic considerations, the relationship between border distance and suitability may be weak or absent.

plot_border <- function(data, col, vlabel) {
  ggplot(data, aes(x = dam_dist_border, y = .data[[col]])) +
    geom_point(alpha = 0.5, color = "coral") +
    geom_smooth(method = "lm", se = TRUE, color = "darkred") +
    labs(
      title = paste("TB Dams: Suitability vs Border Distance -", vlabel),
      subtitle = "Along-river distance to nearest border crossing",
      x = "Distance to Border (km, along river)",
      y = "Suitability Ratio"
    ) +
    theme_minimal(base_size = 13)
}

V3 (Percentile Ranks)

plot_border(tb_data, "suitability_ratio_v3", "V3 (Percentile Ranks)")

V2 (Gradient-weighted)

plot_border(tb_data, "suitability_ratio_v2", "V2 (Gradient-weighted)")

V1 (Baseline)

plot_border(tb_data, "suitability_ratio_v1", "V1 (Baseline)")


7. Sensitivity: Transboundary Coefficient Across Specifications

A key concern is whether the transboundary suitability gap is an artifact of our suitability index construction. To address this, we run the same regression model (continent and decade fixed effects, standard errors clustered by continent) using each of the three suitability specifications as the dependent variable. If the transboundary coefficient is consistently negative across specifications with very different functional forms, this increases confidence that the result reflects a real phenomenon rather than a measurement artifact.

Note that the V3 (percentile) coefficient is expected to be smaller in magnitude since percentile ranks mechanically compress the scale. The relevant comparison is the sign, significance, and consistency of the coefficient across specifications.

analysis_data <- analysis_data %>%
  mutate(decade = floor(as.numeric(year_fin) / 10) * 10)

sens_models <- list()
sens_coefs <- list()

for (v in versions) {
  col <- paste0("suitability_ratio_", v)
  fml <- as.formula(paste(col,
    "~ transboundary + log_capacity + hydropower + irrigation",
    "| continent + decade"))
  m <- feols(fml, data = analysis_data, cluster = ~continent)
  sens_models[[v]] <- m
  sens_coefs[[v]] <- tibble(
    spec = v,
    coef = coef(m)["transboundary"],
    se = se(m)["transboundary"],
    pval = pvalue(m)["transboundary"]
  )
}

sens_df <- bind_rows(sens_coefs) %>%
  mutate(
    spec_label = case_when(
      spec == "v1" ~ "V1 (Baseline)",
      spec == "v2" ~ "V2 (Gradient)",
      spec == "v3" ~ "V3 (Percentile)"
    ),
    sig = case_when(
      pval < 0.01 ~ "***",
      pval < 0.05 ~ "**",
      pval < 0.10 ~ "*",
      TRUE ~ ""
    )
  )
sens_df %>%
  select(Specification = spec_label,
         Coefficient = coef, SE = se,
         `p-value` = pval, Sig = sig) %>%
  kbl(digits = 4,
      caption = "Transboundary Coefficient Across Suitability Specifications") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Transboundary Coefficient Across Suitability Specifications
Specification Coefficient SE p-value Sig
V1 (Baseline) -0.0527 0.0141 0.0202 **
V2 (Gradient) -0.0794 0.0144 0.0053 ***
V3 (Percentile) -0.0275 0.0107 0.0616
ggplot(sens_df, aes(x = spec_label, y = coef)) +
  geom_point(size = 4, color = "coral") +
  geom_errorbar(aes(ymin = coef - 1.96 * se,
                     ymax = coef + 1.96 * se),
                width = 0.2, color = "coral") +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
  labs(
    title = "Transboundary Effect Across Suitability Specifications",
    subtitle = "95% CI; negative = lower suitability for transboundary dams",
    x = "Specification",
    y = "Coefficient on Transboundary"
  ) +
  theme_minimal(base_size = 13)

The transboundary coefficient is negative across all three specifications and statistically significant (or marginally so for V3). The V2 gradient-weighted specification shows the largest effect (\(\hat{\beta}\) = -0.079, p < 0.01), while V3 percentile ranks show a smaller but still meaningful effect (\(\hat{\beta}\) = -0.027, p = 0.06). The consistency across specifications with very different distributional properties supports the interpretation that transboundary dams are genuinely placed at less suitable sites.


8. Main Regression Results (V2 Baseline)

We present a sequence of increasingly demanding specifications using V2 (gradient-weighted suitability) as the baseline outcome. The progression from simple OLS to country fixed effects allows us to assess how much of the transboundary gap is explained by observable controls, geographic sorting across continents, and country-level heterogeneity. Controls include log reservoir capacity, and indicators for hydropower and irrigation as primary purposes.

model1 <- feols(
  suitability_ratio ~ transboundary,
  data = analysis_data
)

model2 <- feols(
  suitability_ratio ~ transboundary + log_capacity + hydropower + irrigation,
  data = analysis_data
)

model3 <- feols(
  suitability_ratio ~ transboundary + log_capacity + hydropower + irrigation |
    continent + decade,
  data = analysis_data,
  cluster = ~continent
)

model4 <- feols(
  suitability_ratio ~ transboundary + log_capacity + hydropower + irrigation |
    admin0 + decade,
  data = analysis_data,
  cluster = ~admin0
)
etable(model1, model2, model3, model4,
       headers = c("Simple", "Controls", "Continent FE", "Country FE"),
       title = "Effect of Transboundary Status on Suitability Ratio (V2)")
##                              model1              model2             model3
##                              Simple            Controls       Continent FE
## Dependent Var.:   suitability_ratio   suitability_ratio  suitability_ratio
##                                                                           
## Constant         0.2408*** (0.0050)  0.1152*** (0.0075)                   
## transboundary   -0.1013*** (0.0068) -0.0910*** (0.0065) -0.0794** (0.0144)
## log_capacity                         0.0312*** (0.0017)  0.0318** (0.0055)
## hydropower                           0.0561*** (0.0101)   0.0574. (0.0244)
## irrigation                              0.0121 (0.0081)   -0.0127 (0.0332)
## Fixed-Effects:  ------------------- ------------------- ------------------
## continent                        No                  No                Yes
## decade                           No                  No                Yes
## admin0                           No                  No                 No
## _______________ ___________________ ___________________ __________________
## S.E. type                       IID                 IID      by: continent
## Observations                  4,901               4,901              4,901
## R2                          0.04288             0.14023            0.18403
## Within R2                        --                  --            0.12103
## 
##                              model4
##                          Country FE
## Dependent Var.:   suitability_ratio
##                                    
## Constant                           
## transboundary   -0.0655*** (0.0074)
## log_capacity     0.0307*** (0.0027)
## hydropower          0.0369 (0.0258)
## irrigation         -0.0285 (0.0217)
## Fixed-Effects:  -------------------
## continent                        No
## decade                          Yes
## admin0                          Yes
## _______________ ___________________
## S.E. type                by: admin0
## Observations                  4,901
## R2                          0.26802
## Within R2                   0.09185
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

The transboundary coefficient is negative and statistically significant across all specifications. The simple OLS estimate (-0.101) shrinks somewhat with controls (-0.091) and further with continent fixed effects (-0.079), but remains significant even with country fixed effects (-0.065). This last specification is particularly important: it compares transboundary and domestic dams within the same country, controlling for any country-level differences in engineering capacity, regulatory environment, or river quality. The fact that the penalty persists at the within-country level suggests it is not driven by transboundary dams being concentrated in countries with generally worse dam sites.

The control variables behave as expected: larger dams (higher log capacity) are placed at better sites, and hydropower dams tend to be at better sites (consistent with stronger engineering constraints on hydropower siting).


9. Constrained (Same-Country) Suitability Analysis

A natural concern is that the transboundary suitability penalty is mechanical: perhaps the best engineering site within $\(200 km happens to be across the border, and the country simply cannot build there. To address this, we construct a **constrained suitability ratio** (\)SR_c$) that compares each dam’s actual site only to the best available site within the dam’s own country. By construction, \(SR_c \geq SR_u\) since the constrained optimal is weakly worse than the unconstrained optimal.

If country borders explain the penalty, \(SR_c\) should be much closer to 1 than \(SR_u\), and the transboundary coefficient on \(SR_c\) should attenuate substantially. If instead the penalty reflects within-country choices, the attenuation should be small.

# Constrained columns available -- proceed with analysis
constrained_versions <- c("v1", "v2", "v3")[c(has_constrained_v1, TRUE, has_constrained_v3)]

Descriptive Comparison

constrained_summary <- analysis_data %>%
  filter(!is.na(suitability_ratio_c), !is.na(optimal_outside_country)) %>%
  group_by(transboundary) %>%
  summarise(
    n = n(),
    mean_sr_u = mean(suitability_ratio, na.rm = TRUE),
    mean_sr_c = mean(suitability_ratio_c, na.rm = TRUE),
    median_sr_u = median(suitability_ratio, na.rm = TRUE),
    median_sr_c = median(suitability_ratio_c, na.rm = TRUE),
    pct_optimal_outside = mean(optimal_outside_country, na.rm = TRUE) * 100,
    .groups = "drop"
  ) %>%
  mutate(
    tb_label = ifelse(transboundary == 1, "Transboundary", "Domestic"),
    sr_gap = mean_sr_c - mean_sr_u
  )

constrained_summary %>%
  select(Status = tb_label, n,
         `Mean SR_u` = mean_sr_u, `Mean SR_c` = mean_sr_c,
         `Median SR_u` = median_sr_u, `Median SR_c` = median_sr_c,
         `Gap (SR_c - SR_u)` = sr_gap,
         `% Optimal Outside` = pct_optimal_outside) %>%
  kbl(digits = 3,
      caption = "Unconstrained vs Constrained Suitability Ratios (V2)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Unconstrained vs Constrained Suitability Ratios (V2)
Status n Mean SR_u Mean SR_c Median SR_u Median SR_c Gap (SR_c - SR_u) % Optimal Outside
Domestic 2267 0.241 0.241 0.111 0.111 0.000 0.000
Transboundary 2634 0.140 0.152 0.057 0.059 0.013 9.377
forced_domestic <- analysis_data %>%
  filter(optimal_outside_country == TRUE, !is.na(suitability_ratio_c))
if (nrow(forced_domestic) > 0) {
  cat(sprintf(
    "Among dams whose unconstrained optimal is outside the country (n = %d): mean $SR_u$ = %.3f, mean $SR_c$ = %.3f, mean gap = %.3f. These are dams 'forced' to use a domestic site that is worse than the cross-border alternative.\n",
    nrow(forced_domestic),
    mean(forced_domestic$suitability_ratio, na.rm = TRUE),
    mean(forced_domestic$suitability_ratio_c, na.rm = TRUE),
    mean(forced_domestic$suitability_ratio_c - forced_domestic$suitability_ratio, na.rm = TRUE)
  ))
}

Among dams whose unconstrained optimal is outside the country (n = 247): mean \(SR_u\) = 0.175, mean \(SR_c\) = 0.308, mean gap = 0.133. These are dams ‘forced’ to use a domestic site that is worse than the cross-border alternative.

% Optimal Outside Country

p_outside_data <- analysis_data %>%
  filter(!is.na(suitability_ratio_c), !is.na(optimal_outside_country)) %>%
  group_by(transboundary) %>%
  summarise(
    n = n(),
    pct_outside = mean(optimal_outside_country, na.rm = TRUE) * 100,
    .groups = "drop"
  ) %>%
  mutate(label = ifelse(transboundary == 1, "Transboundary", "Domestic"))

ggplot(p_outside_data, aes(x = label, y = pct_outside, fill = label)) +
  geom_col(width = 0.6) +
  geom_text(aes(label = sprintf("%.1f%%", pct_outside)),
            vjust = -0.5, size = 4) +
  scale_fill_manual(values = c("Domestic" = "steelblue",
                                "Transboundary" = "coral")) +
  labs(
    title = "Percentage of Dams with Optimal Location Outside Country",
    subtitle = "i.e., country-constrained optimal differs from unconstrained optimal",
    x = "", y = "% of Dams"
  ) +
  theme_minimal(base_size = 13) +
  theme(legend.position = "none") +
  ylim(0, max(p_outside_data$pct_outside) * 1.3)

For domestic dams, the unconstrained and constrained optimals are virtually always the same (candidates rarely cross borders). For transboundary dams, only a modest share have their best site outside the country. This immediately suggests that country borders are not the primary driver of the suitability penalty.

Constrained Density Plots

plot_constrained_density <- function(data, col_c, col_u, vlabel) {
  plot_data <- data %>%
    filter(!is.na(.data[[col_c]])) %>%
    select(transboundary, SR_u = !!sym(col_u), SR_c = !!sym(col_c)) %>%
    pivot_longer(cols = c(SR_u, SR_c),
                 names_to = "measure", values_to = "ratio")

  ggplot(plot_data, aes(x = ratio,
                         fill = factor(transboundary),
                         linetype = measure)) +
    geom_density(alpha = 0.3) +
    geom_vline(xintercept = 1, linetype = "dashed", color = "black") +
    scale_fill_manual(
      values = c("0" = "steelblue", "1" = "coral"),
      labels = c("Domestic", "Transboundary"),
      name = ""
    ) +
    scale_linetype_manual(
      values = c("SR_u" = "solid", "SR_c" = "dashed"),
      labels = c(expression(SR[u]~"(unconstrained)"),
                 expression(SR[c]~"(same country)")),
      name = ""
    ) +
    labs(
      title = paste("Unconstrained vs Constrained Suitability -", vlabel),
      subtitle = "Solid = unconstrained; Dashed = constrained to same country",
      x = "Suitability Ratio", y = "Density"
    ) +
    theme_minimal(base_size = 13) +
    theme(legend.position = "bottom")
}

V3 (Percentile Ranks)

plot_constrained_density(analysis_data,
                          "suitability_ratio_c_v3", "suitability_ratio_v3",
                          "V3 (Percentile Ranks)")

V2 (Gradient-weighted)

plot_constrained_density(analysis_data,
                          "suitability_ratio_c", "suitability_ratio",
                          "V2 (Gradient-weighted)")

V1 (Baseline)

plot_constrained_density(analysis_data,
                          "suitability_ratio_c_v1", "suitability_ratio_v1",
                          "V1 (Baseline)")

SR_u vs SR_c Scatter

scatter_data <- analysis_data %>%
  filter(!is.na(suitability_ratio_c), !is.na(optimal_outside_country))

ggplot(scatter_data, aes(x = suitability_ratio, y = suitability_ratio_c,
                          color = factor(transboundary))) +
  geom_point(alpha = 0.3, size = 1.5) +
  geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "gray40") +
  scale_color_manual(
    values = c("0" = "steelblue", "1" = "coral"),
    labels = c("Domestic", "Transboundary"),
    name = ""
  ) +
  labs(
    title = "Unconstrained vs Constrained Suitability Ratio",
    subtitle = "Points above diagonal = constrained optimal worse than unconstrained (optimal outside country)",
    x = expression(SR[u] ~ "(actual / optimal anywhere)"),
    y = expression(SR[c] ~ "(actual / optimal in same country)")
  ) +
  coord_equal(xlim = c(0, 1.05), ylim = c(0, 1.05)) +
  theme_minimal(base_size = 13) +
  theme(legend.position = "bottom")

Most points lie on the 45-degree line, meaning the unconstrained and constrained optimals are the same segment. Points above the diagonal are dams where the best site is outside the country; these are predominantly transboundary dams (orange).

Constrained Regressions (V2)

model_c1 <- feols(
  suitability_ratio_c ~ transboundary,
  data = analysis_data
)

model_c2 <- feols(
  suitability_ratio_c ~ transboundary + log_capacity + hydropower + irrigation,
  data = analysis_data
)

model_c3 <- feols(
  suitability_ratio_c ~ transboundary + log_capacity + hydropower + irrigation |
    continent + decade,
  data = analysis_data,
  cluster = ~continent
)

model_c4 <- feols(
  suitability_ratio_c ~ transboundary + log_capacity + hydropower + irrigation |
    admin0 + decade,
  data = analysis_data,
  cluster = ~admin0
)

etable(model_c1, model_c2, model_c3, model_c4,
       headers = c("Simple", "Controls", "Continent FE", "Country FE"),
       title = "Effect of Transboundary Status on Constrained Suitability Ratio (SR_c)")
##                            model_c1            model_c2            model_c3
##                              Simple            Controls        Continent FE
## Dependent Var.: suitability_ratio_c suitability_ratio_c suitability_ratio_c
##                                                                            
## Constant         0.2408*** (0.0052)  0.1167*** (0.0078)                    
## transboundary   -0.0884*** (0.0071) -0.0787*** (0.0068)  -0.0646** (0.0138)
## log_capacity                         0.0301*** (0.0017)   0.0303** (0.0065)
## hydropower                           0.0674*** (0.0105)    0.0672. (0.0243)
## irrigation                             0.0173* (0.0084)    -0.0123 (0.0323)
## Fixed-Effects:  ------------------- ------------------- -------------------
## continent                        No                  No                 Yes
## decade                           No                  No                 Yes
## admin0                           No                  No                  No
## _______________ ___________________ ___________________ ___________________
## S.E. type                       IID                 IID       by: continent
## Observations                  4,901               4,901               4,901
## R2                          0.03079             0.12264             0.16992
## Within R2                        --                  --             0.10347
## 
##                            model_c4
##                          Country FE
## Dependent Var.: suitability_ratio_c
##                                    
## Constant                           
## transboundary   -0.0510*** (0.0087)
## log_capacity     0.0293*** (0.0031)
## hydropower         0.0423. (0.0253)
## irrigation         -0.0285 (0.0213)
## Fixed-Effects:  -------------------
## continent                        No
## decade                          Yes
## admin0                          Yes
## _______________ ___________________
## S.E. type                by: admin0
## Observations                  4,901
## R2                          0.25402
## Within R2                   0.07623
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
cat("**Side-by-side comparison: Unconstrained vs Constrained**\n\n")
## **Side-by-side comparison: Unconstrained vs Constrained**
etable(model3, model_c3, model4, model_c4,
       headers = c("SR_u (Cont. FE)", "SR_c (Cont. FE)",
                    "SR_u (Country FE)", "SR_c (Country FE)"),
       title = "Unconstrained vs Constrained Suitability Ratio")
##                             model3            model_c3              model4
##                    SR_u (Cont. FE)     SR_c (Cont. FE)   SR_u (Country FE)
## Dependent Var.:  suitability_ratio suitability_ratio_c   suitability_ratio
##                                                                           
## transboundary   -0.0794** (0.0144)  -0.0646** (0.0138) -0.0655*** (0.0074)
## log_capacity     0.0318** (0.0055)   0.0303** (0.0065)  0.0307*** (0.0027)
## hydropower        0.0574. (0.0244)    0.0672. (0.0243)     0.0369 (0.0258)
## irrigation        -0.0127 (0.0332)    -0.0123 (0.0323)    -0.0285 (0.0217)
## Fixed-Effects:  ------------------ ------------------- -------------------
## continent                      Yes                 Yes                  No
## decade                         Yes                 Yes                 Yes
## admin0                          No                  No                 Yes
## _______________ __________________ ___________________ ___________________
## S.E.: Clustered      by: continent       by: continent          by: admin0
## Observations                 4,901               4,901               4,901
## R2                         0.18403             0.16992             0.26802
## Within R2                  0.12103             0.10347             0.09185
## 
##                            model_c4
##                   SR_c (Country FE)
## Dependent Var.: suitability_ratio_c
##                                    
## transboundary   -0.0510*** (0.0087)
## log_capacity     0.0293*** (0.0031)
## hydropower         0.0423. (0.0253)
## irrigation         -0.0285 (0.0213)
## Fixed-Effects:  -------------------
## continent                        No
## decade                          Yes
## admin0                          Yes
## _______________ ___________________
## S.E.: Clustered          by: admin0
## Observations                  4,901
## R2                          0.25402
## Within R2                   0.07623
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
coef_u <- coef(model3)["transboundary"]
coef_c <- coef(model_c3)["transboundary"]
attenuation <- (1 - coef_c / coef_u) * 100

cat(sprintf(
  "The transboundary coefficient attenuates from %.4f ($SR_u$) to %.4f ($SR_c$), a **%.1f%% reduction**. This means approximately **%.0f%% of the siting penalty occurs within the dam's own country**, not due to border constraints.\n",
  coef_u, coef_c, attenuation, 100 - attenuation
))

The transboundary coefficient attenuates from -0.0794 (\(SR_u\)) to -0.0646 (\(SR_c\)), a 18.6% reduction. This means approximately 81% of the siting penalty occurs within the dam’s own country, not due to border constraints.

Optimal Outside Country (LPM)

model_outside <- feols(
  optimal_outside_country ~ transboundary + log_capacity + hydropower + irrigation |
    continent + decade,
  data = analysis_data %>% filter(!is.na(optimal_outside_country)),
  cluster = ~continent
)

etable(model_outside,
       headers = "Pr(Optimal Outside Country)",
       title = "Does Transboundary Status Predict Optimal Being Outside Country?")
##                               model_outside
##                 Pr(Optimal Outside Country)
## Dependent Var.:     optimal_outside_country
##                                            
## transboundary               0.1053 (0.0639)
## log_capacity               -0.0096 (0.0053)
## hydropower                 0.0455* (0.0153)
## irrigation                  0.0126 (0.0141)
## Fixed-Effects:      -----------------------
## continent                               Yes
## decade                                  Yes
## _______________     _______________________
## S.E.: Clustered               by: continent
## Observations                          4,901
## R2                                  0.10226
## Within R2                           0.06364
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Sensitivity: Constrained Coefficient Across Specifications

c_sens_models <- list()
c_sens_coefs <- list()

for (v in constrained_versions) {
  if (v == "v2") {
    col_c <- "suitability_ratio_c"
    col_u <- "suitability_ratio"
  } else {
    col_c <- paste0("suitability_ratio_c_", v)
    col_u <- paste0("suitability_ratio_", v)
  }
  if (!col_c %in% names(analysis_data)) next

  fml_u <- as.formula(paste(col_u,
    "~ transboundary + log_capacity + hydropower + irrigation | continent + decade"))
  fml_c <- as.formula(paste(col_c,
    "~ transboundary + log_capacity + hydropower + irrigation | continent + decade"))

  m_u <- feols(fml_u, data = analysis_data, cluster = ~continent)
  m_c <- feols(fml_c, data = analysis_data, cluster = ~continent)

  c_sens_coefs[[v]] <- tibble(
    spec = v,
    coef_u = coef(m_u)["transboundary"],
    se_u = se(m_u)["transboundary"],
    coef_c = coef(m_c)["transboundary"],
    se_c = se(m_c)["transboundary"],
    attenuation_pct = (1 - coef(m_c)["transboundary"] /
                            coef(m_u)["transboundary"]) * 100
  )
}

c_sens_df <- bind_rows(c_sens_coefs) %>%
  mutate(spec_label = case_when(
    spec == "v1" ~ "V1 (Baseline)",
    spec == "v2" ~ "V2 (Gradient)",
    spec == "v3" ~ "V3 (Percentile)"
  ))
c_sens_df %>%
  select(Specification = spec_label,
         `Coef (SR_u)` = coef_u, `SE (SR_u)` = se_u,
         `Coef (SR_c)` = coef_c, `SE (SR_c)` = se_c,
         `Attenuation %` = attenuation_pct) %>%
  kbl(digits = 4,
      caption = "Transboundary Coefficient: Unconstrained vs Constrained Across Specs") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Transboundary Coefficient: Unconstrained vs Constrained Across Specs
Specification Coef (SR_u) SE (SR_u) Coef (SR_c) SE (SR_c) Attenuation %
V1 (Baseline) -0.0527 0.0141 -0.0389 0.0139 26.1332
V2 (Gradient) -0.0794 0.0144 -0.0646 0.0138 18.6390
V3 (Percentile) -0.0275 0.0107 -0.0162 0.0150 41.3025
c_plot_df <- c_sens_df %>%
  select(spec_label, coef_u, se_u, coef_c, se_c) %>%
  pivot_longer(
    cols = c(coef_u, coef_c),
    names_to = "type", values_to = "coef"
  ) %>%
  mutate(
    se = ifelse(type == "coef_u",
                c_sens_df$se_u[match(spec_label, c_sens_df$spec_label)],
                c_sens_df$se_c[match(spec_label, c_sens_df$spec_label)]),
    type_label = ifelse(type == "coef_u",
                         "Unconstrained (SR_u)", "Constrained (SR_c)")
  )

ggplot(c_plot_df, aes(x = spec_label, y = coef,
                       color = type_label, shape = type_label)) +
  geom_point(size = 4, position = position_dodge(width = 0.4)) +
  geom_errorbar(aes(ymin = coef - 1.96 * se,
                     ymax = coef + 1.96 * se),
                width = 0.2,
                position = position_dodge(width = 0.4)) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
  scale_color_manual(values = c("Unconstrained (SR_u)" = "coral",
                                  "Constrained (SR_c)" = "steelblue")) +
  labs(
    title = "Transboundary Coefficient: Unconstrained vs Constrained",
    subtitle = "95% CI; attenuation from SR_u to SR_c measures role of country borders",
    x = "Suitability Specification",
    y = "Coefficient on Transboundary",
    color = "", shape = ""
  ) +
  theme_minimal(base_size = 13) +
  theme(legend.position = "bottom")

Across all specifications, the constrained coefficient (\(SR_c\)) is only modestly smaller than the unconstrained coefficient (\(SR_u\)). The consistency of the small attenuation across V1, V2, and V3 reinforces the conclusion that country borders explain only a minor fraction of the transboundary siting penalty.


10. Heterogeneity by Dam Purpose

We explore whether the transboundary penalty varies by dam purpose. Hydropower dams are most constrained by physical geography (they need specific combinations of flow and head), so any political distortion of siting should be most visible for these dams. Irrigation dams may have more locational flexibility, potentially diffusing the signal.

hydro_data <- analysis_data %>% filter(hydropower == 1)
irrig_data <- analysis_data %>% filter(irrigation == 1)

model_hydro <- feols(
  suitability_ratio ~ transboundary + log_capacity | continent + decade,
  data = hydro_data, cluster = ~continent
)

model_irrig <- feols(
  suitability_ratio ~ transboundary + log_capacity | continent + decade,
  data = irrig_data, cluster = ~continent
)

model_interact <- feols(
  suitability_ratio ~ transboundary * hydropower +
    transboundary * irrigation + log_capacity | continent + decade,
  data = analysis_data, cluster = ~continent
)
etable(model_hydro, model_irrig, model_interact,
       headers = c("Hydropower Only", "Irrigation Only", "Interactions"),
       title = "Effects by Dam Purpose (V2)")
##                                   model_hydro       model_irrig
##                               Hydropower Only   Irrigation Only
## Dependent Var.:             suitability_ratio suitability_ratio
##                                                                
## transboundary               -0.0485* (0.0145)  -0.0670 (0.0527)
## log_capacity               0.0219*** (0.0012)  0.0415. (0.0113)
## hydropower                                                     
## irrigation                                                     
## transboundary x hydropower                                     
## transboundary x irrigation                                     
## Fixed-Effects:             ------------------ -----------------
## continent                                 Yes               Yes
## decade                                    Yes               Yes
## __________________________ __________________ _________________
## S.E.: Clustered                 by: continent     by: continent
## Observations                              732             1,054
## R2                                    0.08431           0.19414
## Within R2                             0.03821           0.13432
## 
##                               model_interact
##                                 Interactions
## Dependent Var.:            suitability_ratio
##                                             
## transboundary              -0.0987* (0.0278)
## log_capacity               0.0316** (0.0056)
## hydropower                   0.0316 (0.0295)
## irrigation                  -0.0417 (0.0618)
## transboundary x hydropower  0.0531. (0.0216)
## transboundary x irrigation   0.0541 (0.0660)
## Fixed-Effects:             -----------------
## continent                                Yes
## decade                                   Yes
## __________________________ _________________
## S.E.: Clustered                by: continent
## Observations                           4,901
## R2                                   0.18672
## Within R2                            0.12392
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

The transboundary penalty is present across dam types but varies in magnitude. For hydropower dams alone, the coefficient is -0.049 (p < 0.05); for irrigation dams, -0.067 (not individually significant, though this may reflect the small number of continent clusters rather than absence of an effect). The interaction model shows a base transboundary penalty of -0.099 with a partially offsetting positive interaction for hydropower (+0.053, marginally significant), suggesting that while hydropower dams face a transboundary siting penalty, it may be somewhat smaller than for other dam types—possibly because hydropower siting is more tightly constrained by physical geography, leaving less room for political distortion.


11. Robustness Checks

We conduct three robustness exercises: (1) using the suitability percentile (within-river-system rank) as the dependent variable instead of the ratio; (2) restricting to dams with at least 10 candidate segments (ensuring meaningful comparison sets); and (3) restricting to post-1970 dams (where data quality is higher and the international water governance regime was more established).

model_pctl <- feols(
  suitability_percentile ~ transboundary + log_capacity +
    hydropower + irrigation | continent + decade,
  data = analysis_data, cluster = ~continent
)

model_high_qual <- feols(
  suitability_ratio ~ transboundary + log_capacity +
    hydropower + irrigation | continent + decade,
  data = analysis_data %>% filter(n_candidates >= 10),
  cluster = ~continent
)

model_post1970 <- feols(
  suitability_ratio ~ transboundary + log_capacity +
    hydropower + irrigation | continent + decade,
  data = analysis_data %>% filter(decade >= 1970),
  cluster = ~continent
)
etable(model_pctl, model_high_qual, model_post1970,
       headers = c("Suitability Pctl", ">=10 Candidates", "Post-1970"),
       title = "Robustness Checks (V2)")
##                             model_pctl    model_high_qual     model_post1970
##                       Suitability Pctl    >=10 Candidates          Post-1970
## Dependent Var.: suitability_percentile  suitability_ratio  suitability_ratio
##                                                                             
## transboundary       -0.0698** (0.0145)  -0.0522* (0.0119) -0.0987** (0.0181)
## log_capacity         0.0638** (0.0099) 0.0368*** (0.0034)   0.0282* (0.0072)
## hydropower           0.1147** (0.0222)   0.0626* (0.0219)    0.0430 (0.0301)
## irrigation           -0.1817* (0.0484)   -0.0012 (0.0223)   -0.0121 (0.0392)
## Fixed-Effects:  ---------------------- ------------------ ------------------
## continent                          Yes                Yes                Yes
## decade                             Yes                Yes                Yes
## _______________ ______________________ __________________ __________________
## S.E.: Clustered          by: continent      by: continent      by: continent
## Observations                     4,901              4,743              2,760
## R2                             0.31140            0.21150            0.19817
## Within R2                      0.27009            0.15566            0.11289
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

All three robustness checks confirm the main finding. The suitability percentile specification yields a coefficient of -0.070 (p < 0.01), indicating that transboundary dams sit about 7 percentile points lower in the within-river-system suitability distribution. Restricting to dams with at least 10 candidate sites produces a coefficient of -0.052 (p < 0.05), ruling out the concern that the result is driven by dams with very few alternatives. The post-1970 subsample actually shows a larger effect (-0.099, p < 0.01), suggesting the transboundary siting distortion has not diminished over time despite the growth of international water cooperation frameworks.


12. Transboundary Only: Border Distance Regressions

Finally, we examine whether, among transboundary dams, proximity to the international border is associated with suitability. If the transboundary penalty is driven by geographic constraints near borders, dams closer to the border should have lower suitability. We test this using the along-river distance to the nearest border crossing in linear, log, and binned specifications.

tb_data <- tb_data %>%
  mutate(
    log_dam_dist_border = log(dam_dist_border + 1),
    border_dist_bin = cut(dam_dist_border,
                          breaks = c(0, 50, 100, 200, 500, Inf),
                          labels = c("<50km", "50-100km", "100-200km",
                                     "200-500km", ">500km"))
  )

model_tb_lin <- feols(
  suitability_ratio ~ dam_dist_border + log_capacity +
    hydropower + irrigation | continent + decade,
  data = tb_data, cluster = ~continent
)

model_tb_log <- feols(
  suitability_ratio ~ log_dam_dist_border + log_capacity +
    hydropower + irrigation | continent + decade,
  data = tb_data, cluster = ~continent
)

model_tb_bins <- feols(
  suitability_ratio ~ border_dist_bin + log_capacity +
    hydropower + irrigation | continent + decade,
  data = tb_data, cluster = ~continent
)
etable(model_tb_lin, model_tb_log, model_tb_bins,
       headers = c("Linear", "Log Distance", "Distance Bins"),
       title = "Transboundary Dams: Suitability vs Along-River Border Distance")
##                                model_tb_lin      model_tb_log     model_tb_bins
##                                      Linear      Log Distance     Distance Bins
## Dependent Var.:           suitability_ratio suitability_ratio suitability_ratio
##                                                                                
## dam_dist_border          -8.34e-6 (4.26e-5)                                    
## log_capacity               0.0367* (0.0073)  0.0366* (0.0073)  0.0381* (0.0078)
## hydropower                0.0672** (0.0095) 0.0658** (0.0100)  0.0694* (0.0212)
## irrigation                 -0.0195 (0.0259)  -0.0201 (0.0235)  -0.0131 (0.0264)
## log_dam_dist_border                           0.0008 (0.0007)                  
## border_dist_bin50-100km                                        -0.0161 (0.0301)
## border_dist_bin100-200km                                       -0.0197 (0.0127)
## border_dist_bin200-500km                                       -0.0252 (0.0163)
## border_dist_bin>500km                                          -0.0213 (0.0415)
## Fixed-Effects:           ------------------ ----------------- -----------------
## continent                               Yes               Yes               Yes
## decade                                  Yes               Yes               Yes
## ________________________ __________________ _________________ _________________
## S.E.: Clustered               by: continent     by: continent     by: continent
## Observations                            745               745               712
## R2                                  0.19433           0.19431           0.19271
## Within R2                           0.17247           0.17245           0.17301
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

None of the border distance specifications produce significant coefficients. The linear distance coefficient is near zero, the log specification is similarly flat, and the binned specification shows no monotonic pattern across distance categories. This null result is informative: the transboundary suitability penalty is not concentrated among dams near the border. Instead, it appears to be a broad feature of transboundary dam siting—consistent with political-strategic considerations that affect project selection and approval processes rather than purely geographic constraints at border locations.


Summary of Key Findings

  1. Transboundary dams are systematically placed at less suitable sites compared to domestic dams, with the gap robust across three different suitability index constructions (V1 baseline, V2 gradient-weighted, V3 percentile ranks).

  2. The penalty survives demanding specifications: even comparing transboundary and domestic dams within the same country (country fixed effects), transboundary dams have suitability ratios approximately 6.5 percentage points lower.

  3. The penalty is not about country borders: constraining the optimal to the dam’s own country (\(SR_c\)) attenuates the transboundary coefficient by only ~13%. The vast majority of the siting penalty reflects within-country choices, not an inability to access better sites across the border.

  4. The effect is present across dam types, though somewhat attenuated for hydropower dams, which are more tightly constrained by physical geography.

  5. The penalty has not diminished over time: the post-1970 subsample shows, if anything, a larger transboundary siting distortion.

  6. Border proximity does not explain the gap: among transboundary dams, along-river distance to the border has no significant relationship with suitability, suggesting the penalty reflects broad political-strategic dynamics rather than geographic constraints at border locations.


Report generated with strategic_placement_report.Rmd