From 20ec7e57b0560fc9f15dd91e887e18c9ce3dca50 Mon Sep 17 00:00:00 2001 From: "Dr. Wilson" <167116769+awilnoaa@users.noreply.github.com> Date: Wed, 18 Mar 2026 17:55:03 +0000 Subject: [PATCH 01/12] Fix scalar handling f or log_M and population M --- .../rcpp/rcpp_objects/rcpp_models.hpp | 19 +++++++++++------ inst/include/models/functors/catch_at_age.hpp | 21 +++++++++++-------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index c588e52a0..a5505c2b3 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -364,9 +364,8 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { ss << " \"id\":" << population_interface->log_M.id_m << ",\n"; ss << " \"type\": \"vector\",\n"; ss << " \"dimensionality\": {\n"; - ss << " \"header\": [" << "\"n_years\", \"n_ages\"" << "],\n"; - ss << " \"dimensions\": [" << population_interface->n_years.get() << ", " - << population_interface->n_ages.get() << "]\n},\n"; + ss << " \"header\": [" << "na" << "],\n"; + ss << " \"dimensions\": [" << population_interface->log_M.size() << "]\n},\n"; ss << " \"values\": " << population_interface->log_M << "\n\n"; ss << "},\n"; @@ -1002,11 +1001,19 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["mortality_M"] = fims::Vector( population->n_years.get() * population->n_ages.get()); + fims::Vector dim_names; + if(population->M.size() == population->n_years.get() * population->n_ages.get()){ + dim_names.resize(2); + dim_names[0] = "n_years"; + dim_names[1] = "n_ages"; + } else { + dim_names.resize(1); + dim_names[0] = "scalar" + } derived_quantities_dim_info["mortality_M"] = fims_popdy::DimensionInfo( "mortality_M", - fims::Vector{population->n_years.get(), - population->n_ages.get()}, - fims::Vector{"n_years", "n_ages"}); + fims::Vector{population->M.size()}, + dim_names); derived_quantities["mortality_Z"] = fims::Vector( population->n_years.get() * population->n_ages.get()); diff --git a/inst/include/models/functors/catch_at_age.hpp b/inst/include/models/functors/catch_at_age.hpp index ade4889f0..68e89c8c5 100644 --- a/inst/include/models/functors/catch_at_age.hpp +++ b/inst/include/models/functors/catch_at_age.hpp @@ -203,8 +203,8 @@ class CatchAtAge : public FisheryModelBase { for (size_t age = 0; age < population->n_ages; age++) { for (size_t year = 0; year < population->n_years; year++) { size_t i_age_year = age * population->n_years + year; - population->M[i_age_year] = - fims_math::exp(population->log_M[i_age_year]); + population->M.get_force_scalar(i_age_year) = + fims_math::exp(population->log_M.get_force_scalar(i_age_year)); } } @@ -349,14 +349,14 @@ class CatchAtAge : public FisheryModelBase { // using M from previous age/year dq_["unfished_numbers_at_age"][i_age_year] = dq_["unfished_numbers_at_age"][i_agem1_yearm1] * - (fims_math::exp(-population->M[i_agem1_yearm1])); + (fims_math::exp(-population->M.get_force_scalar(i_agem1_yearm1))); // Plus group calculation if (age == (population->n_ages - 1)) { dq_["unfished_numbers_at_age"][i_age_year] = dq_["unfished_numbers_at_age"][i_age_year] + dq_["unfished_numbers_at_age"][i_agem1_yearm1 + 1] * - (fims_math::exp(-population->M[i_agem1_yearm1 + 1])); + (fims_math::exp(-population->M.get_force_scalar(i_agem1_yearm1 + 1))); } } @@ -408,10 +408,10 @@ class CatchAtAge : public FisheryModelBase { dq_["sum_selectivity"][i_age_year] += s; } - dq_["mortality_M"][i_age_year] = population->M[i_age_year]; + dq_["mortality_M"].get_force_scalar(i_age_year) = population->M.get_force_scalar(i_age_year); dq_["mortality_Z"][i_age_year] = - population->M[i_age_year] + dq_["mortality_F"][i_age_year]; + population->M.get_force_scalar(i_age_year) + dq_["mortality_F"][i_age_year]; } /** @@ -577,16 +577,19 @@ class CatchAtAge : public FisheryModelBase { dq_["proportion_mature_at_age"][0] * population->growth->evaluate(0, population->ages[0]); for (size_t a = 1; a < (population->n_ages - 1); a++) { - numbers_spr[a] = numbers_spr[a - 1] * fims_math::exp(-population->M[a]); + //pull out M from the first year + size_t i_age_year = 0 * population->n_ages + a; + numbers_spr[a] = numbers_spr[a - 1] * fims_math::exp(-population->M.get_force_scalar(i_age_year)); phi_0 += numbers_spr[a] * population->proportion_female[a] * dq_["proportion_mature_at_age"][a] * population->growth->evaluate(0, population->ages[a]); } numbers_spr[population->n_ages - 1] = + // M will be from first year (numbers_spr[population->n_ages - 2] * - fims_math::exp(-population->M[population->n_ages - 2])) / - (1 - fims_math::exp(-population->M[population->n_ages - 1])); + fims_math::exp(-population->M.get_force_scalar(population->n_ages - 2))) / + (1 - fims_math::exp(-population->M.get_force_scalar(population->n_ages - 1))); phi_0 += numbers_spr[population->n_ages - 1] * population->proportion_female[population->n_ages - 1] * dq_["proportion_mature_at_age"][population->n_ages - 1] * From 2102611afcf10ada2dc0d0928467c2486912b54d Mon Sep 17 00:00:00 2001 From: "Dr. Wilson" <167116769+awilnoaa@users.noreply.github.com> Date: Wed, 18 Mar 2026 18:12:52 +0000 Subject: [PATCH 02/12] Fix JSON header quoting and missing semicolon --- inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index a5505c2b3..c6fe7956f 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -364,7 +364,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { ss << " \"id\":" << population_interface->log_M.id_m << ",\n"; ss << " \"type\": \"vector\",\n"; ss << " \"dimensionality\": {\n"; - ss << " \"header\": [" << "na" << "],\n"; + ss << " \"header\": [\"na\"],\n"; ss << " \"dimensions\": [" << population_interface->log_M.size() << "]\n},\n"; ss << " \"values\": " << population_interface->log_M << "\n\n"; ss << "},\n"; @@ -1008,7 +1008,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { dim_names[1] = "n_ages"; } else { dim_names.resize(1); - dim_names[0] = "scalar" + dim_names[0] = "scalar"; } derived_quantities_dim_info["mortality_M"] = fims_popdy::DimensionInfo( "mortality_M", From f345240a761cd0ae338f5b7ebe297a4579fb0478 Mon Sep 17 00:00:00 2001 From: "Dr. Wilson" <167116769+awilnoaa@users.noreply.github.com> Date: Wed, 18 Mar 2026 18:38:10 +0000 Subject: [PATCH 03/12] Use log_M size for population scalar dimension check --- inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index c6fe7956f..cf1e569f7 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -1002,7 +1002,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["mortality_M"] = fims::Vector( population->n_years.get() * population->n_ages.get()); fims::Vector dim_names; - if(population->M.size() == population->n_years.get() * population->n_ages.get()){ + if(population->log_M.size() == population->n_years.get() * population->n_ages.get()){ dim_names.resize(2); dim_names[0] = "n_years"; dim_names[1] = "n_ages"; @@ -1012,7 +1012,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { } derived_quantities_dim_info["mortality_M"] = fims_popdy::DimensionInfo( "mortality_M", - fims::Vector{population->M.size()}, + fims::Vector{population->log_M.size()}, dim_names); derived_quantities["mortality_Z"] = fims::Vector( From 546bc8223fa92603068c9d7511f837299301e1b0 Mon Sep 17 00:00:00 2001 From: "Dr. Wilson" <167116769+awilnoaa@users.noreply.github.com> Date: Wed, 18 Mar 2026 18:56:03 +0000 Subject: [PATCH 04/12] Add static_cast to log_M scalar dimension --- inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index cf1e569f7..64b1c8ff3 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -1012,7 +1012,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { } derived_quantities_dim_info["mortality_M"] = fims_popdy::DimensionInfo( "mortality_M", - fims::Vector{population->log_M.size()}, + fims::Vector{static_cast(population->log_M.size())}, dim_names); derived_quantities["mortality_Z"] = fims::Vector( From 82ef762f7e02536de0663c003fe37c885bcf952e Mon Sep 17 00:00:00 2001 From: awilnoaa <167116769+awilnoaa@users.noreply.github.com> Date: Wed, 18 Mar 2026 19:22:37 +0000 Subject: [PATCH 05/12] chore: format feature branch --- .../rcpp/rcpp_objects/rcpp_models.hpp | 6 ++++-- inst/include/models/functors/catch_at_age.hpp | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 64b1c8ff3..872e63d03 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -365,7 +365,8 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { ss << " \"type\": \"vector\",\n"; ss << " \"dimensionality\": {\n"; ss << " \"header\": [\"na\"],\n"; - ss << " \"dimensions\": [" << population_interface->log_M.size() << "]\n},\n"; + ss << " \"dimensions\": [" << population_interface->log_M.size() + << "]\n},\n"; ss << " \"values\": " << population_interface->log_M << "\n\n"; ss << "},\n"; @@ -1002,7 +1003,8 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["mortality_M"] = fims::Vector( population->n_years.get() * population->n_ages.get()); fims::Vector dim_names; - if(population->log_M.size() == population->n_years.get() * population->n_ages.get()){ + if (population->log_M.size() == + population->n_years.get() * population->n_ages.get()) { dim_names.resize(2); dim_names[0] = "n_years"; dim_names[1] = "n_ages"; diff --git a/inst/include/models/functors/catch_at_age.hpp b/inst/include/models/functors/catch_at_age.hpp index 68e89c8c5..81826fbf7 100644 --- a/inst/include/models/functors/catch_at_age.hpp +++ b/inst/include/models/functors/catch_at_age.hpp @@ -356,7 +356,8 @@ class CatchAtAge : public FisheryModelBase { dq_["unfished_numbers_at_age"][i_age_year] = dq_["unfished_numbers_at_age"][i_age_year] + dq_["unfished_numbers_at_age"][i_agem1_yearm1 + 1] * - (fims_math::exp(-population->M.get_force_scalar(i_agem1_yearm1 + 1))); + (fims_math::exp( + -population->M.get_force_scalar(i_agem1_yearm1 + 1))); } } @@ -408,10 +409,12 @@ class CatchAtAge : public FisheryModelBase { dq_["sum_selectivity"][i_age_year] += s; } - dq_["mortality_M"].get_force_scalar(i_age_year) = population->M.get_force_scalar(i_age_year); + dq_["mortality_M"].get_force_scalar(i_age_year) = + population->M.get_force_scalar(i_age_year); dq_["mortality_Z"][i_age_year] = - population->M.get_force_scalar(i_age_year) + dq_["mortality_F"][i_age_year]; + population->M.get_force_scalar(i_age_year) + + dq_["mortality_F"][i_age_year]; } /** @@ -577,9 +580,11 @@ class CatchAtAge : public FisheryModelBase { dq_["proportion_mature_at_age"][0] * population->growth->evaluate(0, population->ages[0]); for (size_t a = 1; a < (population->n_ages - 1); a++) { - //pull out M from the first year + // pull out M from the first year size_t i_age_year = 0 * population->n_ages + a; - numbers_spr[a] = numbers_spr[a - 1] * fims_math::exp(-population->M.get_force_scalar(i_age_year)); + numbers_spr[a] = + numbers_spr[a - 1] * + fims_math::exp(-population->M.get_force_scalar(i_age_year)); phi_0 += numbers_spr[a] * population->proportion_female[a] * dq_["proportion_mature_at_age"][a] * population->growth->evaluate(0, population->ages[a]); @@ -588,8 +593,10 @@ class CatchAtAge : public FisheryModelBase { numbers_spr[population->n_ages - 1] = // M will be from first year (numbers_spr[population->n_ages - 2] * - fims_math::exp(-population->M.get_force_scalar(population->n_ages - 2))) / - (1 - fims_math::exp(-population->M.get_force_scalar(population->n_ages - 1))); + fims_math::exp( + -population->M.get_force_scalar(population->n_ages - 2))) / + (1 - fims_math::exp( + -population->M.get_force_scalar(population->n_ages - 1))); phi_0 += numbers_spr[population->n_ages - 1] * population->proportion_female[population->n_ages - 1] * dq_["proportion_mature_at_age"][population->n_ages - 1] * From d58585bdd1f89951db323307f7fe52cc113c2888 Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:41:06 +0000 Subject: [PATCH 06/12] - fix dimensions of log_M and derived_quantility[["mortality_M"] ] in output --- .../interface/rcpp/rcpp_objects/rcpp_models.hpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 872e63d03..6023cacc1 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -364,9 +364,17 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { ss << " \"id\":" << population_interface->log_M.id_m << ",\n"; ss << " \"type\": \"vector\",\n"; ss << " \"dimensionality\": {\n"; - ss << " \"header\": [\"na\"],\n"; - ss << " \"dimensions\": [" << population_interface->log_M.size() - << "]\n},\n"; + if (population_interface->log_M.size() == + population_interface->n_years.get() * population_interface->n_ages.get()) { + ss << " \"header\": [\"n_years\", \"n_ages\"],\n"; + ss << " \"dimensions\": [" << population_interface->n_years.get() << ", " + << population_interface->n_ages.get() << "]\n"; + } else { + ss << " \"header\": [\"scalar\"],\n"; + ss << " \"dimensions\": [" << population_interface->log_M.size() + << "]\n"; + } + ss << "},\n"; ss << " \"values\": " << population_interface->log_M << "\n\n"; ss << "},\n"; @@ -1001,7 +1009,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { fims::Vector{"n_years", "n_ages"}); derived_quantities["mortality_M"] = fims::Vector( - population->n_years.get() * population->n_ages.get()); + this->log_M.size()); fims::Vector dim_names; if (population->log_M.size() == population->n_years.get() * population->n_ages.get()) { From 1c3ab1ef06c359018da002c664b1d7d5f0042100 Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:49:45 +0000 Subject: [PATCH 07/12] fix pointer --- inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 6023cacc1..50583acd0 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -1009,7 +1009,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { fims::Vector{"n_years", "n_ages"}); derived_quantities["mortality_M"] = fims::Vector( - this->log_M.size()); + population->log_M.size()); fims::Vector dim_names; if (population->log_M.size() == population->n_years.get() * population->n_ages.get()) { From e0dd23326be149694eaaf3b8d80f78410496a5bf Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:53:45 +0000 Subject: [PATCH 08/12] chore: format feature branch --- .../interface/rcpp/rcpp_objects/rcpp_models.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 50583acd0..b8ca6d3ea 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -365,14 +365,15 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { ss << " \"type\": \"vector\",\n"; ss << " \"dimensionality\": {\n"; if (population_interface->log_M.size() == - population_interface->n_years.get() * population_interface->n_ages.get()) { + population_interface->n_years.get() * + population_interface->n_ages.get()) { ss << " \"header\": [\"n_years\", \"n_ages\"],\n"; - ss << " \"dimensions\": [" << population_interface->n_years.get() << ", " - << population_interface->n_ages.get() << "]\n"; + ss << " \"dimensions\": [" << population_interface->n_years.get() + << ", " << population_interface->n_ages.get() << "]\n"; } else { ss << " \"header\": [\"scalar\"],\n"; ss << " \"dimensions\": [" << population_interface->log_M.size() - << "]\n"; + << "]\n"; } ss << "},\n"; ss << " \"values\": " << population_interface->log_M << "\n\n"; @@ -1008,8 +1009,8 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { population->n_ages.get()}, fims::Vector{"n_years", "n_ages"}); - derived_quantities["mortality_M"] = fims::Vector( - population->log_M.size()); + derived_quantities["mortality_M"] = + fims::Vector(population->log_M.size()); fims::Vector dim_names; if (population->log_M.size() == population->n_years.get() * population->n_ages.get()) { From 36f91d03ec33981c95acad319bb3a5e133f16ce7 Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Sat, 21 Mar 2026 19:12:39 +0000 Subject: [PATCH 09/12] fix setting mortality_M dimensions --- inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index b8ca6d3ea..2f9b6d794 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -1011,19 +1011,25 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["mortality_M"] = fims::Vector(population->log_M.size()); + fims::Vector dim_sizes; fims::Vector dim_names; if (population->log_M.size() == population->n_years.get() * population->n_ages.get()) { + dim_sizes.resize(2); + dim_sizes[0] = static_cast(population->n_years.get()); + dim_sizes[1] = static_cast(population->n_ages.get()); dim_names.resize(2); dim_names[0] = "n_years"; dim_names[1] = "n_ages"; } else { + dim_sizes.resize(1); + dim_sizes[0] = static_cast(population->log_M.size()); dim_names.resize(1); dim_names[0] = "scalar"; } derived_quantities_dim_info["mortality_M"] = fims_popdy::DimensionInfo( "mortality_M", - fims::Vector{static_cast(population->log_M.size())}, + dim_sizes, dim_names); derived_quantities["mortality_Z"] = fims::Vector( From ec1a0f917a4ccc1f906bbce72798641e26a6a97a Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Sat, 21 Mar 2026 21:01:17 +0000 Subject: [PATCH 10/12] - add casts to remove warnings - add dimension check in rcpp_population - change log_M to be scalar in default model - add tests to check log_M dimensions --- R/create_default_parameters.R | 6 +- .../rcpp/rcpp_objects/rcpp_models.hpp | 6 +- .../rcpp/rcpp_objects/rcpp_population.hpp | 10 ++ ...st-integration-fleet-log-obs-error-input.R | 119 ++++++++++++++++++ 4 files changed, 133 insertions(+), 8 deletions(-) diff --git a/R/create_default_parameters.R b/R/create_default_parameters.R index b72cd2f67..943806ed6 100644 --- a/R/create_default_parameters.R +++ b/R/create_default_parameters.R @@ -304,15 +304,11 @@ create_default_Population <- function( init_naa[n_ages] <- init_naa[n_ages] / M_value # sum of infinite series # Create a list of default parameters - default <- create_default_parameters_template( - n_parameters = n_years * n_ages - ) |> + default <- create_default_parameters_template() |> # Add the module type, label, value, and estimation type dplyr::mutate( label = "log_M", value = log(M_value), - age = rep(ages, n_years), - time = rep(years, each = n_ages), estimation_type = "constant" ) |> dplyr::add_row( diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 2f9b6d794..1d1085ed8 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -365,8 +365,8 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { ss << " \"type\": \"vector\",\n"; ss << " \"dimensionality\": {\n"; if (population_interface->log_M.size() == - population_interface->n_years.get() * - population_interface->n_ages.get()) { + static_cast(population_interface->n_years.get() * + population_interface->n_ages.get())) { ss << " \"header\": [\"n_years\", \"n_ages\"],\n"; ss << " \"dimensions\": [" << population_interface->n_years.get() << ", " << population_interface->n_ages.get() << "]\n"; @@ -1014,7 +1014,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { fims::Vector dim_sizes; fims::Vector dim_names; if (population->log_M.size() == - population->n_years.get() * population->n_ages.get()) { + static_cast(population->n_years.get() * population->n_ages.get())) { dim_sizes.resize(2); dim_sizes[0] = static_cast(population->n_years.get()); dim_sizes[1] = static_cast(population->n_ages.get()); diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp index edb73f1af..e170622de 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp @@ -328,6 +328,16 @@ class PopulationInterface : public PopulationInterfaceBase { population->growth_id = this->growth_id.get(); population->recruitment_id = this->recruitment_id.get(); population->maturity_id = this->maturity_id.get(); + if (this->log_M.size() != static_cast(this->n_years.get() * this->n_ages.get()) && + this->log_M.size() != 1) { + throw std::invalid_argument( + "Population log_M size mismatch." + "Population log_M is of size " + + fims::to_string(this->log_M.size()) + + ". Population log_M can only be either of size 1 or n_years * " + + "n_ages, and the number of n_years * n_ages is " + + fims::to_string(this->n_years.get() * this->n_ages.get())); + } population->log_M.resize(this->log_M.size()); if (this->log_f_multiplier.size() == diff --git a/tests/testthat/test-integration-fleet-log-obs-error-input.R b/tests/testthat/test-integration-fleet-log-obs-error-input.R index 2374592a8..34ff75910 100644 --- a/tests/testthat/test-integration-fleet-log-obs-error-input.R +++ b/tests/testthat/test-integration-fleet-log-obs-error-input.R @@ -183,3 +183,122 @@ test_that("`log_Fmort` returns correct error messages when wrong dimensions", { clear() }) + + +## IO correctness ---- +test_that("`log_M` output dimensions follow size rules", { + + # Check scalar log_M input + parameters_4_model <- default_parameters |> + tidyr::unnest(cols = data) + + test_fit <- parameters_4_model |> + initialize_fims(data = data_4_model) |> + fit_fims(optimize = FALSE) + + output <- get_estimates(test_fit) |> + dplyr::mutate( + uncertainty_label = "se", + year = year_i, + estimate = estimated + ) + + log_m_input <- parameters_4_model |> + dplyr::filter(label == "log_M") |> + dplyr::pull(value) + + mortality_m_output <- output |> + dplyr::filter(label == "mortality_M") |> + dplyr::pull(estimated) + + log_m_output <- output |> + dplyr::filter(label == "log_M") |> + dplyr::pull(estimated) + + #' @description Test that log_M input is scalar. + expect_true(length(log_m_input) == 1) + #' @description Test that log_M output is scalar. + expect_true(length(log_m_output) == 1) + #' @description Test that mortality_M output is scalar. + expect_true(length(mortality_m_output) == 1) + + # Check log_M input with n_years * n_ages dimensions + n_years <- get_n_years(data_4_model) + n_ages <- get_n_ages(data_4_model) + + parameters_4_model <- default_parameters |> + tidyr::unnest(cols = data) + + log_m_template <- parameters_4_model |> + dplyr::filter(label == "log_M") |> + dplyr::select(-time, -age, -value) + + log_m_grid <- tidyr::expand_grid( + time = 1:n_years, + age = 1:n_ages + ) |> + dplyr::mutate(value = log_m_input[1]) |> + dplyr::bind_cols(log_m_template[rep(1, n_years * n_ages), ]) + + parameters_4_model <- parameters_4_model |> + dplyr::filter(!(module_name == "Population" & label == "log_M")) |> + dplyr::bind_rows(log_m_grid) + + test_fit <- parameters_4_model |> + initialize_fims(data = data_4_model) |> + fit_fims(optimize = FALSE) + + output <- get_estimates(test_fit) |> + dplyr::mutate( + uncertainty_label = "se", + year = year_i, + estimate = estimated + ) + + log_m_input <- parameters_4_model |> + dplyr::filter(label == "log_M") |> + dplyr::pull(value) + + mortality_m_output <- output |> + dplyr::filter(label == "mortality_M") |> + dplyr::pull(estimated) + + log_m_output <- output |> + dplyr::filter(label == "log_M") |> + dplyr::pull(estimated) + + #' @description Test that log_M input has n_years * n_ages dimensions. + expect_equal(length(log_m_input), n_years * n_ages) + #' @description Test that log_M output matches n_years * n_ages. + expect_equal(length(log_m_output), n_years * n_ages) + #' @description Test that mortality_M output matches n_years * n_ages. + expect_equal(length(mortality_m_output), n_years * n_ages) + + clear() + +}) + + +## Error handling ---- +test_that("`log_M` returns correct error messages when wrong dimensions", { + + #' @description Test that returns correct error message when log_M is too long. + parameters_4_model <- default_parameters |> + tidyr::unnest(cols = data) |> + dplyr::add_row( + model_family = "catch_at_age", + module_name = "Population", + estimation_type = "constant", + label = "log_M", + value = -3 + ) + + expect_error( + { + test_fit <- parameters_4_model |> + initialize_fims(data = data_4_model) |> + fit_fims(optimize = FALSE) + }, + regexp = "Population log_M size mismatch" + ) +}) From 5e4c9c2250c03bb2ce12e0ce0963b42fa81be973 Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Sat, 21 Mar 2026 21:05:48 +0000 Subject: [PATCH 11/12] chore: format feature branch --- .../interface/rcpp/rcpp_objects/rcpp_models.hpp | 11 +++++------ .../interface/rcpp/rcpp_objects/rcpp_population.hpp | 3 ++- .../test-integration-fleet-log-obs-error-input.R | 11 ++++------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 1d1085ed8..e8b848700 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -366,7 +366,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { ss << " \"dimensionality\": {\n"; if (population_interface->log_M.size() == static_cast(population_interface->n_years.get() * - population_interface->n_ages.get())) { + population_interface->n_ages.get())) { ss << " \"header\": [\"n_years\", \"n_ages\"],\n"; ss << " \"dimensions\": [" << population_interface->n_years.get() << ", " << population_interface->n_ages.get() << "]\n"; @@ -1014,7 +1014,8 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { fims::Vector dim_sizes; fims::Vector dim_names; if (population->log_M.size() == - static_cast(population->n_years.get() * population->n_ages.get())) { + static_cast(population->n_years.get() * + population->n_ages.get())) { dim_sizes.resize(2); dim_sizes[0] = static_cast(population->n_years.get()); dim_sizes[1] = static_cast(population->n_ages.get()); @@ -1027,10 +1028,8 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { dim_names.resize(1); dim_names[0] = "scalar"; } - derived_quantities_dim_info["mortality_M"] = fims_popdy::DimensionInfo( - "mortality_M", - dim_sizes, - dim_names); + derived_quantities_dim_info["mortality_M"] = + fims_popdy::DimensionInfo("mortality_M", dim_sizes, dim_names); derived_quantities["mortality_Z"] = fims::Vector( population->n_years.get() * population->n_ages.get()); diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp index e170622de..3219f9e2d 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp @@ -328,7 +328,8 @@ class PopulationInterface : public PopulationInterfaceBase { population->growth_id = this->growth_id.get(); population->recruitment_id = this->recruitment_id.get(); population->maturity_id = this->maturity_id.get(); - if (this->log_M.size() != static_cast(this->n_years.get() * this->n_ages.get()) && + if (this->log_M.size() != + static_cast(this->n_years.get() * this->n_ages.get()) && this->log_M.size() != 1) { throw std::invalid_argument( "Population log_M size mismatch." diff --git a/tests/testthat/test-integration-fleet-log-obs-error-input.R b/tests/testthat/test-integration-fleet-log-obs-error-input.R index 34ff75910..213ce053a 100644 --- a/tests/testthat/test-integration-fleet-log-obs-error-input.R +++ b/tests/testthat/test-integration-fleet-log-obs-error-input.R @@ -187,7 +187,6 @@ test_that("`log_Fmort` returns correct error messages when wrong dimensions", { ## IO correctness ---- test_that("`log_M` output dimensions follow size rules", { - # Check scalar log_M input parameters_4_model <- default_parameters |> tidyr::unnest(cols = data) @@ -201,7 +200,7 @@ test_that("`log_M` output dimensions follow size rules", { uncertainty_label = "se", year = year_i, estimate = estimated - ) + ) log_m_input <- parameters_4_model |> dplyr::filter(label == "log_M") |> @@ -228,7 +227,7 @@ test_that("`log_M` output dimensions follow size rules", { parameters_4_model <- default_parameters |> tidyr::unnest(cols = data) - + log_m_template <- parameters_4_model |> dplyr::filter(label == "log_M") |> dplyr::select(-time, -age, -value) @@ -254,7 +253,7 @@ test_that("`log_M` output dimensions follow size rules", { year = year_i, estimate = estimated ) - + log_m_input <- parameters_4_model |> dplyr::filter(label == "log_M") |> dplyr::pull(value) @@ -273,15 +272,13 @@ test_that("`log_M` output dimensions follow size rules", { expect_equal(length(log_m_output), n_years * n_ages) #' @description Test that mortality_M output matches n_years * n_ages. expect_equal(length(mortality_m_output), n_years * n_ages) - - clear() + clear() }) ## Error handling ---- test_that("`log_M` returns correct error messages when wrong dimensions", { - #' @description Test that returns correct error message when log_M is too long. parameters_4_model <- default_parameters |> tidyr::unnest(cols = data) |> From 133a9e0b880d694510efccbd1e111ce0009b27b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 17:18:45 +0000 Subject: [PATCH 12/12] fix: add space in error message between concatenated sentences Co-authored-by: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Agent-Logs-Url: https://github.com/NOAA-FIMS/FIMS/sessions/a7489e2b-9488-42b1-ba55-ff062157642f --- inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp index 3219f9e2d..2648d9c75 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp @@ -332,7 +332,7 @@ class PopulationInterface : public PopulationInterfaceBase { static_cast(this->n_years.get() * this->n_ages.get()) && this->log_M.size() != 1) { throw std::invalid_argument( - "Population log_M size mismatch." + "Population log_M size mismatch. " "Population log_M is of size " + fims::to_string(this->log_M.size()) + ". Population log_M can only be either of size 1 or n_years * " +