1+ # ' Calculate Fishable Production with Automated Area Calculation
2+ # '
3+ # ' @param model A balanced Rpath model object.
4+ # ' @param ecosystem_name Character. The name of the ecosystem.
5+ # ' Accepted values: "Georges Bank", "Gulf of Maine", "Mid-Atlantic Bight".
6+ # ' @param base_year Numeric. Base year of the model.
7+ # ' @param citation Character. Citation for the model.
8+ # ' @param exclude_groups Character vector. Names of groups to exclude.
9+ # ' @param area_km2 Numeric. Optional. If provided, overrides the shapefile calculation.
10+ # ' @param shapefile_path Character. Path to the EPU shapefile.
11+ # ' @param fleet_name Character. Optional name of the dominant fleet.
12+ # ' @param kappa Numeric. Harvesting factor. Default is 0.175.
13+ # ' @export
14+ calc_fishable_production <- function (model , ecosystem_name , base_year ,
15+ citation , exclude_groups = c(),
16+ area_km2 = NULL ,
17+ shapefile_path = " data-raw/EPU_extended.shp" ,
18+ fleet_name = NULL , kappa = 0.175 ) {
19+
20+ # 1. Automated Area Calculation (if area_km2 is not provided)
21+ if (is.null(area_km2 )) {
22+ if (! file.exists(shapefile_path )) {
23+ stop(" Shapefile not found at " , shapefile_path , " . Please provide area_km2 manually or check the path." )
24+ }
25+
26+ # Map user-friendly names to the EPU codes in your shapefile
27+ epu_map <- c(" Georges Bank" = " GB" ,
28+ " Gulf of Maine" = " GOM" ,
29+ " Mid-Atlantic Bight" = " MAB" )
30+
31+ target_epu <- epu_map [ecosystem_name ]
32+
33+ if (is.na(target_epu )) {
34+ stop(" ecosystem_name must be 'Georges Bank', 'Gulf of Maine', or 'Mid-Atlantic Bight' to use automated area." )
35+ }
36+
37+ # Temporarily disable strict s2 spherical geometry to bypass vertex errors
38+ current_s2 <- sf :: sf_use_s2()
39+ suppressMessages(sf :: sf_use_s2(FALSE ))
40+
41+ # Read, fix geometry, and calculate area
42+ epu_sf <- sf :: st_read(shapefile_path , quiet = TRUE )
43+
44+ area_km2 <- epu_sf %> %
45+ dplyr :: filter(EPU == target_epu ) %> %
46+ sf :: st_make_valid() %> % # Repairs self-intersections
47+ sf :: st_area() %> %
48+ as.numeric() / 1000000 # Convert m2 to km2
49+
50+ # Restore s2 to its original state so we don't mess up the user's environment
51+ suppressMessages(sf :: sf_use_s2(current_s2 ))
52+
53+ message(paste(" Automated Area Lookup:" , round(area_km2 , 2 ), " km2 for" , ecosystem_name ))
54+ }
55+
56+ # 2. Extract parameters from Rpath model
57+ groups <- model $ Group
58+ TL <- model $ TL
59+ Biomass <- model $ Biomass
60+ PB <- model $ PB
61+ catch_mat <- model $ Landings + model $ Discards
62+
63+ # 3. Determine dominant fleet
64+ fleet_totals <- colSums(catch_mat , na.rm = TRUE )
65+ if (is.null(fleet_name )) {
66+ fleet_name <- names(fleet_totals )[which.max(fleet_totals )]
67+ }
68+
69+ # 4. Calculate Fleet Trophic Level
70+ fleet_catch <- catch_mat [, fleet_name ]
71+ fleet_TL <- sum(fleet_catch * TL , na.rm = TRUE ) / sum(fleet_catch , na.rm = TRUE )
72+
73+ # 5. Set TL Threshold & 90% Catch Coverage Loop
74+ initial_tl_threshold <- fleet_TL - 1.0
75+ current_tl_threshold <- initial_tl_threshold
76+ total_catch <- sum(catch_mat , na.rm = TRUE )
77+ perc_catch <- 0
78+
79+ while (perc_catch < 0.90 && current_tl_threshold > 0 ) {
80+ valid_taxa <- ! (groups %in% exclude_groups ) & (TL > = current_tl_threshold )
81+ catch_cov <- sum(catch_mat [valid_taxa , ], na.rm = TRUE )
82+ perc_catch <- catch_cov / total_catch
83+ if (perc_catch < 0.90 ) current_tl_threshold <- current_tl_threshold - 0.01
84+ }
85+
86+ # 6. Calculate Fishable Production
87+ valid_taxa_final <- ! (groups %in% exclude_groups ) & (TL > = current_tl_threshold )
88+ total_production <- sum(Biomass [valid_taxa_final ] * PB [valid_taxa_final ], na.rm = TRUE )
89+
90+ fishable_prod_km2 <- total_production * kappa
91+ fishable_prod_total <- fishable_prod_km2 * area_km2
92+
93+ # 7. Format Result Paragraph
94+ extension_text <- " "
95+ if (current_tl_threshold < initial_tl_threshold ) {
96+ extension_text <- sprintf(
97+ " This trophic level cutoff was extended down to TL %.2f, to ensure that at least 90%% of harvest in the base model was included." ,
98+ current_tl_threshold
99+ )
100+ }
101+
102+ template_text <- sprintf(
103+ " We applied the Fishable Production method to an Rpath food web model of %s, which represented a base year of %d. This model (%s) is intended to represent a spatial domain of %s km2. In this Rpath model, the single fleet or dominant fleet %s had a trophic level of %.2f, and hence we include production estimates of all species >= trophic level %.2f.%s We estimate Fishable production to be %.2f tons km-2, equivalent to %.2f million tons over the model spatial domain." ,
104+ ecosystem_name , base_year , citation , format(round(area_km2 ), big.mark = " ," ),
105+ fleet_name , fleet_TL , initial_tl_threshold , extension_text ,
106+ fishable_prod_km2 , fishable_prod_total / 1000000
107+ )
108+
109+ return (list (metrics = data.frame (Ecosystem = ecosystem_name , Area = area_km2 , Fishable_Prod_km2 = fishable_prod_km2 ),
110+ report_text = template_text ))
111+ }
0 commit comments