From 641bb33cfc2fedbc2e55ef8dae40f4eddb48aec1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:41:17 +0000 Subject: [PATCH 1/5] Initial plan From d961369d08db36a12af555f777e68f4aee6e4d1f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:49:51 +0000 Subject: [PATCH 2/5] feat: add informative fims::Vector bounds errors with vector names --- inst/include/common/fims_vector.hpp | 31 +++++++++++-- .../rcpp/rcpp_objects/rcpp_distribution.hpp | 9 ++++ .../rcpp/rcpp_objects/rcpp_fleet.hpp | 9 ++++ .../rcpp/rcpp_objects/rcpp_maturity.hpp | 6 +++ .../rcpp/rcpp_objects/rcpp_models.hpp | 43 +++++++++++++++++++ .../rcpp/rcpp_objects/rcpp_population.hpp | 15 +++++++ .../rcpp/rcpp_objects/rcpp_recruitment.hpp | 15 +++++++ .../rcpp/rcpp_objects/rcpp_selectivity.hpp | 18 ++++++++ tests/gtest/CMakeLists.txt | 11 +++++ ...est_fimsVector_Vector_operatorBrackets.cpp | 39 +++++++++++++++++ 10 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 tests/gtest/test_fimsVector_Vector_operatorBrackets.cpp diff --git a/inst/include/common/fims_vector.hpp b/inst/include/common/fims_vector.hpp index 176ae7c11..772c2233d 100644 --- a/inst/include/common/fims_vector.hpp +++ b/inst/include/common/fims_vector.hpp @@ -9,8 +9,9 @@ #define FIMS_VECTOR_HPP #include "../interface/interface.hpp" -#include #include +#include +#include namespace fims { @@ -82,6 +83,8 @@ class Vector { for (size_t i = 0; i < this->vec_m.size(); i++) { this->vec_m[i] = other[i]; } + this->tag_m = other.tag_m; + this->variable_name_m = other.variable_name_m; } /** @@ -142,7 +145,7 @@ class Vector { */ inline Type &operator[](size_t pos) { if (pos >= this->size()) { - throw std::invalid_argument("fims::Vector out of bounds"); + throw std::invalid_argument(this->get_out_of_bounds_error_message(pos)); } return this->vec_m[pos]; } @@ -153,7 +156,7 @@ class Vector { */ inline const Type &operator[](size_t n) const { if (n >= this->size()) { - throw std::invalid_argument("fims::Vector out of bounds"); + throw std::invalid_argument(this->get_out_of_bounds_error_message(n)); } return this->vec_m[n]; } @@ -480,8 +483,30 @@ class Vector { */ void set_tag(const std::string &tag) { this->tag_m = tag; } + /** + * @brief Gets the variable name for the vector. + * @return The variable name. + */ + std::string get_variable_name() const { return this->variable_name_m; } + + /** + * @brief Sets the variable name for the vector. + */ + void set_variable_name(const std::string &name) { this->variable_name_m = name; } + private: + std::string get_out_of_bounds_error_message(size_t pos) const { + std::string error_msg = "fims::Vector out of bounds"; + if (!this->variable_name_m.empty()) { + error_msg += " for " + this->variable_name_m; + } + error_msg += ": index " + std::to_string(pos + 1) + " >= size " + + std::to_string(this->size()); + return error_msg; + } + std::string tag_m; /*!< The tag for the vector. */ + std::string variable_name_m; /*!< The name of the variable represented by the vector. */ }; // end fims::Vector class /** diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp index 6822530f5..766b5098c 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp @@ -471,6 +471,9 @@ class DnormDistributionsInterface : public DistributionsInterfaceBase { distribution->expected_values[i] = this->expected_values[i].initial_value_m; } + ss.str(""); + ss << "dnorm." << this->id_m << ".log_sd"; + distribution->log_sd.set_variable_name(ss.str()); distribution->log_sd.resize(this->log_sd.size()); for (size_t i = 0; i < this->log_sd.size(); i++) { distribution->log_sd[i] = this->log_sd[i].initial_value_m; @@ -487,6 +490,9 @@ class DnormDistributionsInterface : public DistributionsInterfaceBase { info->variable_map[this->log_sd.id_m] = &(distribution)->log_sd; distribution->use_mean = this->use_mean_m.get(); + ss.str(""); + ss << "dnorm." << this->id_m << ".expected_mean"; + distribution->expected_mean.set_variable_name(ss.str()); distribution->expected_mean.resize(this->expected_mean.size()); for (size_t i = 0; i < this->expected_mean.size(); i++) { distribution->expected_mean[i] = this->expected_mean[i].initial_value_m; @@ -810,6 +816,9 @@ class DlnormDistributionsInterface : public DistributionsInterfaceBase { distribution->expected_values[i] = this->expected_values[i].initial_value_m; } + ss.str(""); + ss << "dlnorm." << this->id_m << ".log_sd"; + distribution->log_sd.set_variable_name(ss.str()); distribution->log_sd.resize(this->log_sd.size()); for (size_t i = 0; i < this->log_sd.size(); i++) { distribution->log_sd[i] = this->log_sd[i].initial_value_m; diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp index c52d09556..32f5bb097 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp @@ -454,6 +454,9 @@ class FleetInterface : public FleetInterfaceBase { fleet->fleet_selectivity_id_m = interface_selectivity_id_m.get(); + ss.str(""); + ss << "Fleet." << this->id << ".log_q"; + fleet->log_q.set_variable_name(ss.str()); fleet->log_q.resize(this->log_q.size()); for (size_t i = 0; i < this->log_q.size(); i++) { fleet->log_q[i] = this->log_q[i].initial_value_m; @@ -484,6 +487,9 @@ class FleetInterface : public FleetInterfaceBase { fims::to_string(this->n_years.get())); } fleet->log_Fmort.resize(static_cast(this->log_Fmort.size())); + ss.str(""); + ss << "Fleet." << this->id << ".log_Fmort"; + fleet->log_Fmort.set_variable_name(ss.str()); for (size_t i = 0; i < log_Fmort.size(); i++) { fleet->log_Fmort[i] = this->log_Fmort[i].initial_value_m; @@ -504,6 +510,9 @@ class FleetInterface : public FleetInterfaceBase { info->variable_map[this->log_Fmort.id_m] = &(fleet)->log_Fmort; if (this->n_lengths.get() > 0) { + ss.str(""); + ss << "Fleet." << this->id << ".age_to_length_conversion"; + fleet->age_to_length_conversion.set_variable_name(ss.str()); fleet->age_to_length_conversion.resize( this->age_to_length_conversion.size()); diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp index f0a782687..e06d55aad 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp @@ -226,6 +226,9 @@ class LogisticMaturityInterface : public MaturityInterfaceBase { // set relative info maturity->id = this->id; std::stringstream ss; + ss.str(""); + ss << "Maturity." << this->id << ".inflection_point"; + maturity->inflection_point.set_variable_name(ss.str()); maturity->inflection_point.resize(this->inflection_point.size()); for (size_t i = 0; i < this->inflection_point.size(); i++) { maturity->inflection_point[i] = this->inflection_point[i].initial_value_m; @@ -247,6 +250,9 @@ class LogisticMaturityInterface : public MaturityInterfaceBase { } } + ss.str(""); + ss << "Maturity." << this->id << ".slope"; + maturity->slope.set_variable_name(ss.str()); maturity->slope.resize(this->slope.size()); for (size_t i = 0; i < this->slope.size(); i++) { maturity->slope[i] = this->slope[i].initial_value_m; diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 8feb6b0b8..158a6088b 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -999,9 +999,15 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { model->GetPopulationDimensionInfo(population_interface->id); std::stringstream ss; + auto set_population_dq_name = [&](const std::string &name) { + ss.str(""); + ss << "Population." << population_interface->id << "." << name; + derived_quantities[name].set_variable_name(ss.str()); + }; derived_quantities["total_landings_weight"] = fims::Vector(population_interface->n_years.get()); + set_population_dq_name("total_landings_weight"); derived_quantities_dim_info["total_landings_weight"] = fims_popdy::DimensionInfo( @@ -1013,6 +1019,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["total_landings_numbers"] = fims::Vector(population_interface->n_years.get()); + set_population_dq_name("total_landings_numbers"); derived_quantities_dim_info["total_landings_numbers"] = fims_popdy::DimensionInfo( @@ -1025,6 +1032,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["mortality_F"] = fims::Vector(population_interface->n_years.get() * population_interface->n_ages.get()); + set_population_dq_name("mortality_F"); derived_quantities_dim_info["mortality_F"] = fims_popdy::DimensionInfo( "mortality_F", fims::Vector{population_interface->n_years.get(), @@ -1036,6 +1044,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["mortality_M"] = fims::Vector(population_interface->n_years.get() * population_interface->n_ages.get()); + set_population_dq_name("mortality_M"); derived_quantities_dim_info["mortality_M"] = fims_popdy::DimensionInfo( "mortality_M", fims::Vector{population_interface->n_years.get(), @@ -1047,6 +1056,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["mortality_Z"] = fims::Vector(population_interface->n_years.get() * population_interface->n_ages.get()); + set_population_dq_name("mortality_Z"); derived_quantities_dim_info["mortality_Z"] = fims_popdy::DimensionInfo( "mortality_Z", fims::Vector{population_interface->n_years.get(), @@ -1058,6 +1068,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["numbers_at_age"] = fims::Vector((population_interface->n_years.get() + 1) * population_interface->n_ages.get()); + set_population_dq_name("numbers_at_age"); derived_quantities_dim_info["numbers_at_age"] = fims_popdy::DimensionInfo( "numbers_at_age", fims::Vector{(population_interface->n_years.get() + 1), @@ -1069,6 +1080,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["unfished_numbers_at_age"] = fims::Vector((population_interface->n_years.get() + 1) * population_interface->n_ages.get()); + set_population_dq_name("unfished_numbers_at_age"); derived_quantities_dim_info["unfished_numbers_at_age"] = fims_popdy::DimensionInfo( "unfished_numbers_at_age", @@ -1080,6 +1092,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["biomass"] = fims::Vector((population_interface->n_years.get() + 1)); + set_population_dq_name("biomass"); derived_quantities_dim_info["biomass"] = fims_popdy::DimensionInfo( "biomass", fims::Vector{(population_interface->n_years.get() + 1)}, @@ -1089,6 +1102,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["spawning_biomass"] = fims::Vector((population_interface->n_years.get() + 1)); + set_population_dq_name("spawning_biomass"); derived_quantities_dim_info["spawning_biomass"] = fims_popdy::DimensionInfo( "spawning_biomass", @@ -1099,6 +1113,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["unfished_biomass"] = fims::Vector((population_interface->n_years.get() + 1)); + set_population_dq_name("unfished_biomass"); derived_quantities_dim_info["unfished_biomass"] = fims_popdy::DimensionInfo( "unfished_biomass", @@ -1109,6 +1124,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["unfished_spawning_biomass"] = fims::Vector((population_interface->n_years.get() + 1)); + set_population_dq_name("unfished_spawning_biomass"); derived_quantities_dim_info["unfished_spawning_biomass"] = fims_popdy::DimensionInfo( "unfished_spawning_biomass", @@ -1120,6 +1136,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["proportion_mature_at_age"] = fims::Vector((population_interface->n_years.get() + 1) * population_interface->n_ages.get()); + set_population_dq_name("proportion_mature_at_age"); derived_quantities_dim_info["proportion_mature_at_age"] = fims_popdy::DimensionInfo( "proportion_mature_at_age", @@ -1131,6 +1148,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["expected_recruitment"] = fims::Vector((population_interface->n_years.get() + 1)); + set_population_dq_name("expected_recruitment"); derived_quantities_dim_info["expected_recruitment"] = fims_popdy::DimensionInfo( "expected_recruitment", @@ -1142,6 +1160,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["sum_selectivity"] = fims::Vector(population_interface->n_years.get() * population_interface->n_ages.get()); + set_population_dq_name("sum_selectivity"); derived_quantities_dim_info["sum_selectivity"] = fims_popdy::DimensionInfo( "sum_selectivity", @@ -1182,11 +1201,18 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { std::map &derived_quantities_dim_info = model->GetFleetDimensionInfo(fleet_interface->id); + std::stringstream ss; + auto set_fleet_dq_name = [&](const std::string &name) { + ss.str(""); + ss << "Fleet." << fleet_interface->id << "." << name; + derived_quantities[name].set_variable_name(ss.str()); + }; // initialize derive quantities // landings derived_quantities["landings_numbers_at_age"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_ages.get()); + set_fleet_dq_name("landings_numbers_at_age"); derived_quantities_dim_info["landings_numbers_at_age"] = fims_popdy::DimensionInfo( "landings_numbers_at_age", @@ -1198,6 +1224,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["landings_weight_at_age"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_ages.get()); + set_fleet_dq_name("landings_weight_at_age"); derived_quantities_dim_info["landings_weight_at_age"] = fims_popdy::DimensionInfo( "landings_weight_at_age", @@ -1209,6 +1236,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["landings_numbers_at_length"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_lengths.get()); + set_fleet_dq_name("landings_numbers_at_length"); derived_quantities_dim_info["landings_numbers_at_length"] = fims_popdy::DimensionInfo( "landings_numbers_at_length", @@ -1220,6 +1248,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["landings_weight"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("landings_weight"); derived_quantities_dim_info["landings_weight"] = fims_popdy::DimensionInfo( "landings_weight", @@ -1230,6 +1259,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["landings_numbers"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("landings_numbers"); derived_quantities_dim_info["landings_numbers"] = fims_popdy::DimensionInfo( "landings_numbers", @@ -1240,6 +1270,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["landings_expected"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("landings_expected"); derived_quantities_dim_info["landings_expected"] = fims_popdy::DimensionInfo( "landings_expected", @@ -1250,6 +1281,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["log_landings_expected"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("log_landings_expected"); derived_quantities_dim_info["log_landings_expected"] = fims_popdy::DimensionInfo( "log_landings_expected", @@ -1260,6 +1292,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["agecomp_proportion"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_ages.get()); + set_fleet_dq_name("agecomp_proportion"); derived_quantities_dim_info["agecomp_proportion"] = fims_popdy::DimensionInfo( "agecomp_proportion", @@ -1271,6 +1304,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["lengthcomp_proportion"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_lengths.get()); + set_fleet_dq_name("lengthcomp_proportion"); derived_quantities_dim_info["lengthcomp_proportion"] = fims_popdy::DimensionInfo( "lengthcomp_proportion", @@ -1283,6 +1317,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { // index derived_quantities["index_numbers_at_age"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_ages.get()); + set_fleet_dq_name("index_numbers_at_age"); derived_quantities_dim_info["index_numbers_at_age"] = fims_popdy::DimensionInfo( "index_numbers_at_age", @@ -1294,6 +1329,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["index_weight_at_age"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_ages.get()); + set_fleet_dq_name("index_weight_at_age"); derived_quantities_dim_info["index_weight_at_age"] = fims_popdy::DimensionInfo( "index_weight_at_age", @@ -1305,6 +1341,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["index_numbers_at_length"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_lengths.get()); + set_fleet_dq_name("index_numbers_at_length"); derived_quantities_dim_info["index_numbers_at_length"] = fims_popdy::DimensionInfo( "index_numbers_at_length", @@ -1316,6 +1353,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["index_weight"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("index_weight"); derived_quantities_dim_info["index_weight"] = fims_popdy::DimensionInfo( "index_weight", fims::Vector{(fleet_interface->n_years.get())}, fims::Vector{"n_years"}); @@ -1324,6 +1362,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["index_numbers"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("index_numbers"); derived_quantities_dim_info["index_numbers"] = fims_popdy::DimensionInfo( "index_numbers", fims::Vector{(fleet_interface->n_years.get())}, fims::Vector{"n_years"}); @@ -1332,6 +1371,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["index_expected"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("index_expected"); derived_quantities_dim_info["index_expected"] = fims_popdy::DimensionInfo( "index_expected", fims::Vector{(fleet_interface->n_years.get())}, fims::Vector{"n_years"}); @@ -1340,6 +1380,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["log_index_expected"] = fims::Vector(fleet_interface->n_years.get()); + set_fleet_dq_name("log_index_expected"); derived_quantities_dim_info["log_index_expected"] = fims_popdy::DimensionInfo( "log_index_expected", @@ -1350,6 +1391,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["agecomp_expected"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_ages.get()); + set_fleet_dq_name("agecomp_expected"); derived_quantities_dim_info["agecomp_expected"] = fims_popdy::DimensionInfo( "agecomp_expected", @@ -1361,6 +1403,7 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { derived_quantities["lengthcomp_expected"] = fims::Vector( fleet_interface->n_years.get() * fleet_interface->n_lengths.get()); + set_fleet_dq_name("lengthcomp_expected"); derived_quantities_dim_info["lengthcomp_expected"] = fims_popdy::DimensionInfo( "lengthcomp_expected", diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp index 255af7bab..5e1a4541b 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp @@ -459,9 +459,15 @@ class PopulationInterface : public PopulationInterfaceBase { population->spawning_biomass_ratio.resize( this->spawning_biomass_ratio.size()); } + ss.str(""); + ss << "Population." << this->id << ".spawning_biomass_ratio"; + population->spawning_biomass_ratio.set_variable_name(ss.str()); info->variable_map[this->spawning_biomass_ratio.id_m] = &(population)->spawning_biomass_ratio; + ss.str(""); + ss << "Population." << this->id << ".log_M"; + population->log_M.set_variable_name(ss.str()); population->log_init_naa.resize(this->log_init_naa.size()); for (size_t i = 0; i < log_M.size(); i++) { population->log_M[i] = this->log_M[i].initial_value_m; @@ -480,6 +486,9 @@ class PopulationInterface : public PopulationInterfaceBase { } info->variable_map[this->log_M.id_m] = &(population)->log_M; + ss.str(""); + ss << "Population." << this->id << ".log_f_multiplier"; + population->log_f_multiplier.set_variable_name(ss.str()); for (size_t i = 0; i < log_f_multiplier.size(); i++) { population->log_f_multiplier[i] = this->log_f_multiplier[i].initial_value_m; @@ -503,6 +512,9 @@ class PopulationInterface : public PopulationInterfaceBase { info->variable_map[this->log_f_multiplier.id_m] = &(population)->log_f_multiplier; + ss.str(""); + ss << "Population." << this->id << ".log_init_naa"; + population->log_init_naa.set_variable_name(ss.str()); for (size_t i = 0; i < log_init_naa.size(); i++) { population->log_init_naa[i] = this->log_init_naa[i].initial_value_m; if (this->log_init_naa[i].estimation_type_m.get() == "fixed_effects") { @@ -536,6 +548,9 @@ class PopulationInterface : public PopulationInterfaceBase { population->proportion_female.resize(this->proportion_female.size()); } + ss.str(""); + ss << "Population." << this->id << ".proportion_female"; + population->proportion_female.set_variable_name(ss.str()); for (size_t i = 0; i < this->proportion_female.size(); i++) { if (this->proportion_female[i].initial_value_m < 0.0 || this->proportion_female[i].initial_value_m > 1.0) { diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp index 6f7da9ae5..b5ce39d7f 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp @@ -321,6 +321,9 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { recruitment->id = this->id; recruitment->process_id = this->process_id.get(); // set logit_steep + ss.str(""); + ss << "Recruitment." << this->id << ".logit_steep"; + recruitment->logit_steep.set_variable_name(ss.str()); recruitment->logit_steep.resize(this->logit_steep.size()); for (size_t i = 0; i < this->logit_steep.size(); i++) { recruitment->logit_steep[i] = this->logit_steep[i].initial_value_m; @@ -343,6 +346,9 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { info->variable_map[this->logit_steep.id_m] = &(recruitment)->logit_steep; // set log_rzero + ss.str(""); + ss << "Recruitment." << this->id << ".log_rzero"; + recruitment->log_rzero.set_variable_name(ss.str()); recruitment->log_rzero.resize(this->log_rzero.size()); for (size_t i = 0; i < this->log_rzero.size(); i++) { recruitment->log_rzero[i] = this->log_rzero[i].initial_value_m; @@ -364,6 +370,9 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { } info->variable_map[this->log_rzero.id_m] = &(recruitment)->log_rzero; // set log_recruit_devs + ss.str(""); + ss << "Recruitment." << this->id << ".log_devs"; + recruitment->log_recruit_devs.set_variable_name(ss.str()); recruitment->log_recruit_devs.resize(this->log_devs.size()); for (size_t i = 0; i < this->log_devs.size(); i++) { recruitment->log_recruit_devs[i] = this->log_devs[i].initial_value_m; @@ -387,6 +396,9 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { info->variable_map[this->log_devs.id_m] = &(recruitment)->log_recruit_devs; // set log_r + ss.str(""); + ss << "Recruitment." << this->id << ".log_r"; + recruitment->log_r.set_variable_name(ss.str()); recruitment->log_r.resize(this->log_r.size()); for (size_t i = 0; i < log_r.size(); i++) { recruitment->log_r[i] = this->log_r[i].initial_value_m; @@ -407,6 +419,9 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { info->variable_map[this->log_r.id_m] = &(recruitment)->log_r; // set log_expected_recruitment + ss.str(""); + ss << "Recruitment." << this->id << ".log_expected_recruitment"; + recruitment->log_expected_recruitment.set_variable_name(ss.str()); recruitment->log_expected_recruitment.resize(this->n_years.get() - 1); for (size_t i = 0; i < static_cast(this->n_years.get() - 1); i++) { recruitment->log_expected_recruitment[i] = 0; diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp index f9cd8d64e..8240f9122 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp @@ -228,6 +228,9 @@ class LogisticSelectivityInterface : public SelectivityInterfaceBase { std::stringstream ss; // set relative info selectivity->id = this->id; + ss.str(""); + ss << "Selectivity." << this->id << ".inflection_point"; + selectivity->inflection_point.set_variable_name(ss.str()); selectivity->inflection_point.resize(this->inflection_point.size()); for (size_t i = 0; i < this->inflection_point.size(); i++) { selectivity->inflection_point[i] = @@ -252,6 +255,9 @@ class LogisticSelectivityInterface : public SelectivityInterfaceBase { info->variable_map[this->inflection_point.id_m] = &(selectivity)->inflection_point; + ss.str(""); + ss << "Selectivity." << this->id << ".slope"; + selectivity->slope.set_variable_name(ss.str()); selectivity->slope.resize(this->slope.size()); for (size_t i = 0; i < this->slope.size(); i++) { selectivity->slope[i] = this->slope[i].initial_value_m; @@ -487,6 +493,9 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { std::stringstream ss; // set relative info selectivity->id = this->id; + ss.str(""); + ss << "Selectivity." << this->id << ".inflection_point_asc"; + selectivity->inflection_point_asc.set_variable_name(ss.str()); selectivity->inflection_point_asc.resize(this->inflection_point_asc.size()); for (size_t i = 0; i < this->inflection_point_asc.size(); i++) { selectivity->inflection_point_asc[i] = @@ -511,6 +520,9 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { info->variable_map[this->inflection_point_asc.id_m] = &(selectivity)->inflection_point_asc; + ss.str(""); + ss << "Selectivity." << this->id << ".slope_asc"; + selectivity->slope_asc.set_variable_name(ss.str()); selectivity->slope_asc.resize(this->slope_asc.size()); for (size_t i = 0; i < this->slope_asc.size(); i++) { selectivity->slope_asc[i] = this->slope_asc[i].initial_value_m; @@ -532,6 +544,9 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { } info->variable_map[this->slope_asc.id_m] = &(selectivity)->slope_asc; + ss.str(""); + ss << "Selectivity." << this->id << ".inflection_point_desc"; + selectivity->inflection_point_desc.set_variable_name(ss.str()); selectivity->inflection_point_desc.resize( this->inflection_point_desc.size()); for (size_t i = 0; i < this->inflection_point_desc.size(); i++) { @@ -558,6 +573,9 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { info->variable_map[this->inflection_point_desc.id_m] = &(selectivity)->inflection_point_desc; + ss.str(""); + ss << "Selectivity." << this->id << ".slope_desc"; + selectivity->slope_desc.set_variable_name(ss.str()); selectivity->slope_desc.resize(this->slope_desc.size()); for (size_t i = 0; i < this->slope_desc.size(); i++) { selectivity->slope_desc[i] = this->slope_desc[i].initial_value_m; diff --git a/tests/gtest/CMakeLists.txt b/tests/gtest/CMakeLists.txt index 59a1326c0..40bf11f90 100644 --- a/tests/gtest/CMakeLists.txt +++ b/tests/gtest/CMakeLists.txt @@ -91,6 +91,17 @@ target_link_libraries(fimsObject_MemoryTracker ) gtest_discover_tests(fimsObject_MemoryTracker) +# test_fimsVector_Vector_operatorBrackets.cpp +add_executable(fimsVector_Vector_operatorBrackets + test_fimsVector_Vector_operatorBrackets.cpp +) +add_as_invoker_manifest(fimsVector_Vector_operatorBrackets) +target_link_libraries(fimsVector_Vector_operatorBrackets + gtest_main + fims_test +) +gtest_discover_tests(fimsVector_Vector_operatorBrackets) + # test_growth_EWAAGrowth_evaluate.cpp add_executable(growth_EWAAGrowth_evaluate test_growth_EWAAGrowth_evaluate.cpp diff --git a/tests/gtest/test_fimsVector_Vector_operatorBrackets.cpp b/tests/gtest/test_fimsVector_Vector_operatorBrackets.cpp new file mode 100644 index 000000000..b225ed5a2 --- /dev/null +++ b/tests/gtest/test_fimsVector_Vector_operatorBrackets.cpp @@ -0,0 +1,39 @@ +#include "gtest/gtest.h" +#include "fims_vector.hpp" + +namespace { + +TEST(fimsVector_Vector_operatorBrackets, ReportsDetailedMutableBoundsError) { + fims::Vector values(2, 0.0); + values.set_variable_name("Recruitment.1.log_devs"); + + try { + (void)values[2]; + FAIL() << "Expected std::invalid_argument"; + } catch (const std::invalid_argument &error) { + const std::string message = error.what(); + EXPECT_NE(message.find("fims::Vector out of bounds"), std::string::npos); + EXPECT_NE(message.find("Recruitment.1.log_devs"), std::string::npos); + EXPECT_NE(message.find("index 3"), std::string::npos); + EXPECT_NE(message.find("size 2"), std::string::npos); + } +} + +TEST(fimsVector_Vector_operatorBrackets, ReportsDetailedConstBoundsError) { + fims::Vector mutable_values(4, 0.0); + mutable_values.set_variable_name("Selectivity.9.slope"); + const fims::Vector &values = mutable_values; + + try { + (void)values[4]; + FAIL() << "Expected std::invalid_argument"; + } catch (const std::invalid_argument &error) { + const std::string message = error.what(); + EXPECT_NE(message.find("fims::Vector out of bounds"), std::string::npos); + EXPECT_NE(message.find("Selectivity.9.slope"), std::string::npos); + EXPECT_NE(message.find("index 5"), std::string::npos); + EXPECT_NE(message.find("size 4"), std::string::npos); + } +} + +} // namespace From 95243eafc29d006a4262a89cc9f6a097cf7c1913 Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Fri, 7 Aug 2026 19:41:55 +0000 Subject: [PATCH 3/5] - set names for data vectors - set names for variable vectors - clean up code for naming parameters and derived quantities --- .../interface/rcpp/rcpp_objects/rcpp_data.hpp | 8 +++ .../rcpp/rcpp_objects/rcpp_distribution.hpp | 26 +++++-- .../rcpp/rcpp_objects/rcpp_fleet.hpp | 17 +---- .../rcpp/rcpp_objects/rcpp_maturity.hpp | 12 +--- .../rcpp/rcpp_objects/rcpp_models.hpp | 14 ++-- .../rcpp/rcpp_objects/rcpp_population.hpp | 70 +++++++------------ .../rcpp/rcpp_objects/rcpp_recruitment.hpp | 30 ++------ .../rcpp/rcpp_objects/rcpp_selectivity.hpp | 43 ++---------- 8 files changed, 77 insertions(+), 143 deletions(-) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_data.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_data.hpp index 5a5caec43..81985b86b 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_data.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_data.hpp @@ -181,6 +181,8 @@ class AgeCompDataInterface : public DataInterfaceBase { this->amax); age_comp_data->id = this->id; + std::string data_name = "age_comp_data"; + age_comp_data->data.set_variable_name(data_name); for (int y = 0; y < ymax; y++) { for (int a = 0; a < amax; a++) { int i_age_year = y * amax + a; @@ -312,6 +314,8 @@ class LengthCompDataInterface : public DataInterfaceBase { std::make_shared>(this->ymax, this->lmax); length_comp_data->id = this->id; + std::string data_name = "length_comp_data"; + length_comp_data->data.set_variable_name(data_name); for (int y = 0; y < ymax; y++) { for (int l = 0; l < lmax; l++) { int i_length_year = y * lmax + l; @@ -433,6 +437,8 @@ class IndexDataInterface : public DataInterfaceBase { std::make_shared>(this->ymax); data->id = this->id; + std::string data_name = "index_data"; + data->data.set_variable_name(data_name); for (int y = 0; y < ymax; y++) { data->at(y) = this->index_data[y]; @@ -554,6 +560,8 @@ class LandingsDataInterface : public DataInterfaceBase { std::make_shared>(this->ymax); data->id = this->id; + std::string data_name = "landings_data"; + data->data.set_variable_name(data_name); for (int y = 0; y < ymax; y++) { data->at(y) = this->landings_data[y]; diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp index 766b5098c..7a1280332 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp @@ -460,17 +460,25 @@ class DnormDistributionsInterface : public DistributionsInterfaceBase { distribution->key[i] = this->key_m->at(i); } distribution->id = this->id_m; + ss.str(""); + ss << "dnorm." << this->id_m << ".observed_values"; + distribution->observed_values.set_variable_name(ss.str()); distribution->observed_values.resize(this->observed_values.size()); for (size_t i = 0; i < this->observed_values.size(); i++) { distribution->observed_values[i] = this->observed_values[i].initial_value_m; } + // set relative info + ss.str(""); + ss << "dnorm." << this->id_m << ".expected_values"; + distribution->expected_values.set_variable_name(ss.str()); distribution->expected_values.resize(this->expected_values.size()); for (size_t i = 0; i < this->expected_values.size(); i++) { distribution->expected_values[i] = this->expected_values[i].initial_value_m; } + ss.str(""); ss << "dnorm." << this->id_m << ".log_sd"; distribution->log_sd.set_variable_name(ss.str()); @@ -497,9 +505,7 @@ class DnormDistributionsInterface : public DistributionsInterfaceBase { for (size_t i = 0; i < this->expected_mean.size(); i++) { distribution->expected_mean[i] = this->expected_mean[i].initial_value_m; if (this->expected_mean[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "dnorm." << this->id_m << ".expected_mean." - << this->expected_mean[i].id_m; + ss << "." << this->expected_mean[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(distribution->expected_mean[i]); } @@ -805,12 +811,18 @@ class DlnormDistributionsInterface : public DistributionsInterfaceBase { for (size_t i = 0; i < this->key_m->size(); i++) { distribution->key[i] = this->key_m->at(i); } + ss.str("") + ss << "dlnorm." << this->id_m << ".observed_values"; + distribution->observed_values.set_variable_name(ss.str()); distribution->observed_values.resize(this->observed_values.size()); for (size_t i = 0; i < this->observed_values.size(); i++) { distribution->observed_values[i] = this->observed_values[i].initial_value_m; } // set relative info + ss.str("") + ss << "dlnorm." << this->id_m << ".expected_values"; + distribution->expected_values.set_variable_name(ss.str()); distribution->expected_values.resize(this->expected_values.size()); for (size_t i = 0; i < this->expected_values.size(); i++) { distribution->expected_values[i] = @@ -1104,7 +1116,7 @@ class DmultinomDistributionsInterface : public DistributionsInterfaceBase { std::shared_ptr> distribution = std::make_shared>(); - + std::stringstream ss; distribution->id = this->id_m; distribution->observed_data_id_m = interface_observed_data_id_m; distribution->input_type = this->input_type_m; @@ -1112,12 +1124,18 @@ class DmultinomDistributionsInterface : public DistributionsInterfaceBase { for (size_t i = 0; i < this->key_m->size(); i++) { distribution->key[i] = this->key_m->at(i); } + ss.str("") + ss << "dmultinom." << this->id_m << ".observed_values"; + distribution->observed_values.set_variable_name(ss.str()); distribution->observed_values.resize(this->observed_values.size()); for (size_t i = 0; i < this->observed_values.size(); i++) { distribution->observed_values[i] = this->observed_values[i].initial_value_m; } // set relative info + ss.str("") + ss << "dmultinom." << this->id_m << ".expected_values"; + distribution->expected_values.set_variable_name(ss.str()); distribution->expected_values.resize(this->expected_values.size()); for (size_t i = 0; i < this->expected_values.size(); i++) { distribution->expected_values[i] = diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp index 32f5bb097..4ee364772 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_fleet.hpp @@ -460,16 +460,12 @@ class FleetInterface : public FleetInterfaceBase { fleet->log_q.resize(this->log_q.size()); for (size_t i = 0; i < this->log_q.size(); i++) { fleet->log_q[i] = this->log_q[i].initial_value_m; - + ss << "." << this->log_q[i].id_m; if (this->log_q[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Fleet." << this->id << ".log_q." << this->log_q[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(fleet->log_q[i]); } if (this->log_q[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Fleet." << this->id << ".log_q." << this->log_q[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(fleet->log_q[i]); } @@ -492,16 +488,12 @@ class FleetInterface : public FleetInterfaceBase { fleet->log_Fmort.set_variable_name(ss.str()); for (size_t i = 0; i < log_Fmort.size(); i++) { fleet->log_Fmort[i] = this->log_Fmort[i].initial_value_m; - + ss << "." << this->log_Fmort[i].id_m; if (this->log_Fmort[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Fleet." << this->id << ".log_Fmort." << this->log_Fmort[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(fleet->log_Fmort[i]); } if (this->log_Fmort[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Fleet." << this->id << ".log_Fmort." << this->log_Fmort[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(fleet->log_Fmort[i]); } @@ -527,12 +519,9 @@ class FleetInterface : public FleetInterfaceBase { for (size_t i = 0; i < fleet->age_to_length_conversion.size(); i++) { fleet->age_to_length_conversion[i] = this->age_to_length_conversion[i].initial_value_m; - + ss << "." << this->age_to_length_conversion[i].id_m; if (this->age_to_length_conversion[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Fleet." << this->id << ".age_to_length_conversion." - << this->age_to_length_conversion[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(fleet->age_to_length_conversion[i]); } diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp index e06d55aad..06ab90997 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_maturity.hpp @@ -232,19 +232,14 @@ class LogisticMaturityInterface : public MaturityInterfaceBase { maturity->inflection_point.resize(this->inflection_point.size()); for (size_t i = 0; i < this->inflection_point.size(); i++) { maturity->inflection_point[i] = this->inflection_point[i].initial_value_m; + ss << "." << this->inflection_point[i].id_m; if (this->inflection_point[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Maturity." << this->id << ".inflection_point." - << this->inflection_point[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(maturity->inflection_point[i]); } if (this->inflection_point[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Maturity." << this->id << ".inflection_point." - << this->inflection_point[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(maturity->inflection_point[i]); } @@ -256,15 +251,12 @@ class LogisticMaturityInterface : public MaturityInterfaceBase { maturity->slope.resize(this->slope.size()); for (size_t i = 0; i < this->slope.size(); i++) { maturity->slope[i] = this->slope[i].initial_value_m; + ss << "." << this->slope[i].id_m; if (this->slope[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Maturity." << this->id << ".slope." << this->slope[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(maturity->slope[i]); } if (this->slope[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Maturity." << this->id << ".slope." << this->slope[i].id_m; info->RegisterRandomEffect(maturity->slope[i]); info->RegisterRandomEffectName(ss.str()); } diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp index 158a6088b..0d69d2f85 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_models.hpp @@ -998,11 +998,9 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { &derived_quantities_dim_info = model->GetPopulationDimensionInfo(population_interface->id); - std::stringstream ss; - auto set_population_dq_name = [&](const std::string &name) { - ss.str(""); - ss << "Population." << population_interface->id << "." << name; - derived_quantities[name].set_variable_name(ss.str()); + auto set_population_dq_name = [&](const std::string &name) { + derived_quantities[name].set_variable_name( + "Population." + fims::to_string(population_interface->id) + "." + name); }; derived_quantities["total_landings_weight"] = @@ -1201,11 +1199,9 @@ class CatchAtAgeInterface : public FisheryModelInterfaceBase { std::map &derived_quantities_dim_info = model->GetFleetDimensionInfo(fleet_interface->id); - std::stringstream ss; auto set_fleet_dq_name = [&](const std::string &name) { - ss.str(""); - ss << "Fleet." << fleet_interface->id << "." << name; - derived_quantities[name].set_variable_name(ss.str()); + derived_quantities[name].set_variable_name( + "Fleet." + fims::to_string(fleet_interface->id) + "." + name); }; // initialize derive quantities diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp index 5e1a4541b..5d083dfbc 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_population.hpp @@ -431,8 +431,10 @@ 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(); - population->log_M.resize(this->log_M.size()); + ss.str(""); + ss << "Population." << this->id << ".log_f_multiplier"; + population->log_f_multiplier.set_variable_name(ss.str()); if (this->log_f_multiplier.size() == static_cast(this->n_years.get())) { population->log_f_multiplier.resize(this->log_f_multiplier.size()); @@ -447,6 +449,24 @@ class PopulationInterface : public PopulationInterfaceBase { } population->log_f_multiplier.resize(this->log_f_multiplier.size()); } + for (size_t i = 0; i < log_f_multiplier.size(); i++) { + population->log_f_multiplier[i] = + this->log_f_multiplier[i].initial_value_m; + ss << "." << this->log_f_multiplier[i].id_m; + if (this->log_f_multiplier[i].estimation_type_m.get() == + "fixed_effects") { + info->RegisterParameterName(ss.str()); + info->RegisterParameter(population->log_f_multiplier[i]); + } + if (this->log_f_multiplier[i].estimation_type_m.get() == + "random_effects") { + info->RegisterRandomEffectName(ss.str()); + info->RegisterRandomEffect(population->log_f_multiplier[i]); + } + } + info->variable_map[this->log_f_multiplier.id_m] = + &(population)->log_f_multiplier; + if (this->spawning_biomass_ratio.size() == static_cast(this->n_years.get() + 1)) { @@ -468,66 +488,35 @@ class PopulationInterface : public PopulationInterfaceBase { ss.str(""); ss << "Population." << this->id << ".log_M"; population->log_M.set_variable_name(ss.str()); - population->log_init_naa.resize(this->log_init_naa.size()); + population->log_M.resize(this->log_M.size()); for (size_t i = 0; i < log_M.size(); i++) { population->log_M[i] = this->log_M[i].initial_value_m; + ss << "." << this->log_M[i].id_m; if (this->log_M[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Population." << this->id << ".log_M." << this->log_M[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(population->log_M[i]); } if (this->log_M[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Population." << this->id << ".log_M." << this->log_M[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(population->log_M[i]); } } info->variable_map[this->log_M.id_m] = &(population)->log_M; - ss.str(""); - ss << "Population." << this->id << ".log_f_multiplier"; - population->log_f_multiplier.set_variable_name(ss.str()); - for (size_t i = 0; i < log_f_multiplier.size(); i++) { - population->log_f_multiplier[i] = - this->log_f_multiplier[i].initial_value_m; - if (this->log_f_multiplier[i].estimation_type_m.get() == - "fixed_effects") { - ss.str(""); - ss << "Population." << this->id << ".log_f_multiplier." - << this->log_f_multiplier[i].id_m; - info->RegisterParameterName(ss.str()); - info->RegisterParameter(population->log_f_multiplier[i]); - } - if (this->log_f_multiplier[i].estimation_type_m.get() == - "random_effects") { - ss.str(""); - ss << "Population." << this->id << ".log_f_multiplier." - << this->log_f_multiplier[i].id_m; - info->RegisterRandomEffectName(ss.str()); - info->RegisterRandomEffect(population->log_f_multiplier[i]); - } - } - info->variable_map[this->log_f_multiplier.id_m] = - &(population)->log_f_multiplier; + ss.str(""); ss << "Population." << this->id << ".log_init_naa"; population->log_init_naa.set_variable_name(ss.str()); + population->log_init_naa.resize(this->log_init_naa.size()); for (size_t i = 0; i < log_init_naa.size(); i++) { population->log_init_naa[i] = this->log_init_naa[i].initial_value_m; + ss << "." << this->log_init_naa[i].id_m; if (this->log_init_naa[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Population." << this->id << ".log_init_naa." - << this->log_init_naa[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(population->log_init_naa[i]); } if (this->log_init_naa[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Population." << this->id << ".log_init_naa." - << this->log_init_naa[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(population->log_init_naa[i]); } @@ -561,19 +550,14 @@ class PopulationInterface : public PopulationInterfaceBase { } population->proportion_female[i] = this->proportion_female[i].initial_value_m; + ss << "." << this->proportion_female[i].id_m; if (this->proportion_female[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Population." << this->id << ".proportion_female." - << this->proportion_female[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(population->proportion_female[i]); } if (this->proportion_female[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Population." << this->id << ".proportion_female." - << this->proportion_female[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(population->proportion_female[i]); } diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp index b5ce39d7f..605328a65 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_recruitment.hpp @@ -327,18 +327,12 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { recruitment->logit_steep.resize(this->logit_steep.size()); for (size_t i = 0; i < this->logit_steep.size(); i++) { recruitment->logit_steep[i] = this->logit_steep[i].initial_value_m; - + ss << "." << this->logit_steep[i].id_m; if (this->logit_steep[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".logit_steep." - << this->logit_steep[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(recruitment->logit_steep[i]); } if (this->logit_steep[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".logit_steep." - << this->logit_steep[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(recruitment->logit_steep[i]); } @@ -352,18 +346,12 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { recruitment->log_rzero.resize(this->log_rzero.size()); for (size_t i = 0; i < this->log_rzero.size(); i++) { recruitment->log_rzero[i] = this->log_rzero[i].initial_value_m; - + ss << "." << this->log_rzero[i].id_m; if (this->log_rzero[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".log_rzero." - << this->log_rzero[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(recruitment->log_rzero[i]); } if (this->log_rzero[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".log_rzero." - << this->log_rzero[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(recruitment->log_rzero[i]); } @@ -376,18 +364,12 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { recruitment->log_recruit_devs.resize(this->log_devs.size()); for (size_t i = 0; i < this->log_devs.size(); i++) { recruitment->log_recruit_devs[i] = this->log_devs[i].initial_value_m; - + ss << "." << this->log_devs[i].id_m; if (this->log_devs[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".log_devs." - << this->log_devs[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(recruitment->log_recruit_devs[i]); } if (this->log_devs[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".log_devs." - << this->log_devs[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(recruitment->log_recruit_devs[i]); } @@ -402,16 +384,12 @@ class BevertonHoltRecruitmentInterface : public RecruitmentInterfaceBase { recruitment->log_r.resize(this->log_r.size()); for (size_t i = 0; i < log_r.size(); i++) { recruitment->log_r[i] = this->log_r[i].initial_value_m; - + ss << "." << this->log_r[i].id_m; if (this->log_r[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".log_r." << this->log_r[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(recruitment->log_r[i]); } if (this->log_r[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Recruitment." << this->id << ".log_r." << this->log_r[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(recruitment->log_r[i]); } diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp index 8240f9122..abf36a960 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_selectivity.hpp @@ -235,19 +235,14 @@ class LogisticSelectivityInterface : public SelectivityInterfaceBase { for (size_t i = 0; i < this->inflection_point.size(); i++) { selectivity->inflection_point[i] = this->inflection_point[i].initial_value_m; + ss << "." << this->inflection_point[i].id_m; if (this->inflection_point[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".inflection_point." - << this->inflection_point[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(selectivity->inflection_point[i]); } if (this->inflection_point[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".inflection_point." - << this->inflection_point[i].id_m; info->RegisterRandomEffect(selectivity->inflection_point[i]); info->RegisterRandomEffectName(ss.str()); } @@ -261,15 +256,12 @@ class LogisticSelectivityInterface : public SelectivityInterfaceBase { selectivity->slope.resize(this->slope.size()); for (size_t i = 0; i < this->slope.size(); i++) { selectivity->slope[i] = this->slope[i].initial_value_m; + ss << "." << this->slope[i].id_m; if (this->slope[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".slope." << this->slope[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(selectivity->slope[i]); } if (this->slope[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".slope." << this->slope[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(selectivity->slope[i]); } @@ -500,19 +492,14 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { for (size_t i = 0; i < this->inflection_point_asc.size(); i++) { selectivity->inflection_point_asc[i] = this->inflection_point_asc[i].initial_value_m; + ss << "." << this->inflection_point_asc[i].id_m; if (this->inflection_point_asc[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".inflection_point_asc." - << this->inflection_point_asc[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(selectivity->inflection_point_asc[i]); } if (this->inflection_point_asc[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".inflection_point_asc." - << this->inflection_point_asc[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(selectivity->inflection_point_asc[i]); } @@ -526,18 +513,12 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { selectivity->slope_asc.resize(this->slope_asc.size()); for (size_t i = 0; i < this->slope_asc.size(); i++) { selectivity->slope_asc[i] = this->slope_asc[i].initial_value_m; - + ss << "." << this->slope_asc[i].id_m; if (this->slope_asc[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".slope_asc." - << this->slope_asc[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(selectivity->slope_asc[i]); } if (this->slope_asc[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".slope_asc." - << this->slope_asc[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(selectivity->slope_asc[i]); } @@ -552,20 +533,14 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { for (size_t i = 0; i < this->inflection_point_desc.size(); i++) { selectivity->inflection_point_desc[i] = this->inflection_point_desc[i].initial_value_m; - + ss << "." << this->inflection_point_desc[i].id_m; if (this->inflection_point_desc[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".inflection_point_desc." - << this->inflection_point_desc[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(selectivity->inflection_point_desc[i]); } if (this->inflection_point_desc[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".inflection_point_desc." - << this->inflection_point_desc[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(selectivity->inflection_point_desc[i]); } @@ -579,18 +554,12 @@ class DoubleLogisticSelectivityInterface : public SelectivityInterfaceBase { selectivity->slope_desc.resize(this->slope_desc.size()); for (size_t i = 0; i < this->slope_desc.size(); i++) { selectivity->slope_desc[i] = this->slope_desc[i].initial_value_m; - + ss << "." << this->slope_desc[i].id_m; if (this->slope_desc[i].estimation_type_m.get() == "fixed_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".slope_desc." - << this->slope_desc[i].id_m; info->RegisterParameterName(ss.str()); info->RegisterParameter(selectivity->slope_desc[i]); } if (this->slope_desc[i].estimation_type_m.get() == "random_effects") { - ss.str(""); - ss << "Selectivity." << this->id << ".slope_desc." - << this->slope_desc[i].id_m; info->RegisterRandomEffectName(ss.str()); info->RegisterRandomEffect(selectivity->slope_desc[i]); } From f3c1fa4ba61552e45df76183ccb6992fba9fa782 Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Fri, 7 Aug 2026 21:34:05 +0000 Subject: [PATCH 4/5] fix errors, fix C++ test, add R test --- .../rcpp/rcpp_objects/rcpp_distribution.hpp | 8 ++-- tests/gtest/CMakeLists.txt | 12 +++--- ...ckets.cpp => test_fimsVector_Operator.cpp} | 4 +- tests/testthat/test-fimsfit.R | 38 +++++++++++++++++++ 4 files changed, 50 insertions(+), 12 deletions(-) rename tests/gtest/{test_fimsVector_Vector_operatorBrackets.cpp => test_fimsVector_Operator.cpp} (88%) diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp index 7a1280332..5cb5c639f 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_distribution.hpp @@ -811,7 +811,7 @@ class DlnormDistributionsInterface : public DistributionsInterfaceBase { for (size_t i = 0; i < this->key_m->size(); i++) { distribution->key[i] = this->key_m->at(i); } - ss.str("") + ss.str(""); ss << "dlnorm." << this->id_m << ".observed_values"; distribution->observed_values.set_variable_name(ss.str()); distribution->observed_values.resize(this->observed_values.size()); @@ -820,7 +820,7 @@ class DlnormDistributionsInterface : public DistributionsInterfaceBase { this->observed_values[i].initial_value_m; } // set relative info - ss.str("") + ss.str(""); ss << "dlnorm." << this->id_m << ".expected_values"; distribution->expected_values.set_variable_name(ss.str()); distribution->expected_values.resize(this->expected_values.size()); @@ -1124,7 +1124,7 @@ class DmultinomDistributionsInterface : public DistributionsInterfaceBase { for (size_t i = 0; i < this->key_m->size(); i++) { distribution->key[i] = this->key_m->at(i); } - ss.str("") + ss.str(""); ss << "dmultinom." << this->id_m << ".observed_values"; distribution->observed_values.set_variable_name(ss.str()); distribution->observed_values.resize(this->observed_values.size()); @@ -1133,7 +1133,7 @@ class DmultinomDistributionsInterface : public DistributionsInterfaceBase { this->observed_values[i].initial_value_m; } // set relative info - ss.str("") + ss.str(""); ss << "dmultinom." << this->id_m << ".expected_values"; distribution->expected_values.set_variable_name(ss.str()); distribution->expected_values.resize(this->expected_values.size()); diff --git a/tests/gtest/CMakeLists.txt b/tests/gtest/CMakeLists.txt index 40bf11f90..b0260ec7f 100644 --- a/tests/gtest/CMakeLists.txt +++ b/tests/gtest/CMakeLists.txt @@ -91,16 +91,16 @@ target_link_libraries(fimsObject_MemoryTracker ) gtest_discover_tests(fimsObject_MemoryTracker) -# test_fimsVector_Vector_operatorBrackets.cpp -add_executable(fimsVector_Vector_operatorBrackets - test_fimsVector_Vector_operatorBrackets.cpp +# test_fimsVector_Operator.cpp +add_executable(fimsVector_Operator + test_fimsVector_Operator.cpp ) -add_as_invoker_manifest(fimsVector_Vector_operatorBrackets) -target_link_libraries(fimsVector_Vector_operatorBrackets +add_as_invoker_manifest(fimsVector_Operator) +target_link_libraries(fimsVector_Operator gtest_main fims_test ) -gtest_discover_tests(fimsVector_Vector_operatorBrackets) +gtest_discover_tests(fimsVector_Operator) # test_growth_EWAAGrowth_evaluate.cpp add_executable(growth_EWAAGrowth_evaluate diff --git a/tests/gtest/test_fimsVector_Vector_operatorBrackets.cpp b/tests/gtest/test_fimsVector_Operator.cpp similarity index 88% rename from tests/gtest/test_fimsVector_Vector_operatorBrackets.cpp rename to tests/gtest/test_fimsVector_Operator.cpp index b225ed5a2..afad41fe0 100644 --- a/tests/gtest/test_fimsVector_Vector_operatorBrackets.cpp +++ b/tests/gtest/test_fimsVector_Operator.cpp @@ -3,7 +3,7 @@ namespace { -TEST(fimsVector_Vector_operatorBrackets, ReportsDetailedMutableBoundsError) { +TEST(fimsVector_Operator, ReportsDetailedBoundsError) { fims::Vector values(2, 0.0); values.set_variable_name("Recruitment.1.log_devs"); @@ -19,7 +19,7 @@ TEST(fimsVector_Vector_operatorBrackets, ReportsDetailedMutableBoundsError) { } } -TEST(fimsVector_Vector_operatorBrackets, ReportsDetailedConstBoundsError) { +TEST(fimsVector_Operator, ReportsDetailedConstBoundsError) { fims::Vector mutable_values(4, 0.0); mutable_values.set_variable_name("Selectivity.9.slope"); const fims::Vector &values = mutable_values; diff --git a/tests/testthat/test-fimsfit.R b/tests/testthat/test-fimsfit.R index 8a573ed86..d4fd94a04 100644 --- a/tests/testthat/test-fimsfit.R +++ b/tests/testthat/test-fimsfit.R @@ -252,3 +252,41 @@ test_that("fit_fims() errors when optimization fails to converge", { clear() }) + +test_that("fit_fims() produces informative error when throwing a fims::Vector bounding error", { + + #' @description Test that fit_fims() throws an informative error when variable bounds are not set up correctly + data("data_big", package = "FIMS") + data_4_model <- FIMSFrame(data_big) + # Create parameters + initialized_model <- create_default_configurations(data_4_model) |> + create_default_parameters(data = data_4_model) |> + tidyr::unnest(cols = data) |> + dplyr::rows_delete( + tibble::tibble( + module_name = "Population", + label = c("log_init_naa"), + age = 1 + ), + by = c("module_name", "label", "age") + ) |> + initialize_fims(data = data_4_model) + expect_error( + fit_fims(initialized_model, optimize = TRUE) + )data("data_big", package = "FIMS") + data_4_model <- FIMSFrame(data_big) + # Create parameters + initial_parameters <- create_default_configurations(data_4_model) |> + create_default_parameters(data = data_4_model) |> + initialize_fims(data = data_4_model) + # Remove one log_init_naa + length_n_parm <- initial_parameters$parameters$p |> length() + initial_parameters$parameters$p <- initial_parameters$parameters$p[-length_n_parm] + + expect_error( + object = fit_fims(initial_parameters, optimize = TRUE), + regexp = "fims::Vector out of bounds for Population.1.log_init_naa: index 12 >= size 11" + ) + clear() + +}) \ No newline at end of file From ec55fc2e60b08a75cb633a40ca7f8057dab3e301 Mon Sep 17 00:00:00 2001 From: Andrea-Havron-NOAA <85530309+Andrea-Havron-NOAA@users.noreply.github.com> Date: Fri, 7 Aug 2026 21:40:13 +0000 Subject: [PATCH 5/5] fix bug --- tests/testthat/test-fimsfit.R | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/tests/testthat/test-fimsfit.R b/tests/testthat/test-fimsfit.R index d4fd94a04..4cea87a4c 100644 --- a/tests/testthat/test-fimsfit.R +++ b/tests/testthat/test-fimsfit.R @@ -271,20 +271,9 @@ test_that("fit_fims() produces informative error when throwing a fims::Vector bo by = c("module_name", "label", "age") ) |> initialize_fims(data = data_4_model) - expect_error( - fit_fims(initialized_model, optimize = TRUE) - )data("data_big", package = "FIMS") - data_4_model <- FIMSFrame(data_big) - # Create parameters - initial_parameters <- create_default_configurations(data_4_model) |> - create_default_parameters(data = data_4_model) |> - initialize_fims(data = data_4_model) - # Remove one log_init_naa - length_n_parm <- initial_parameters$parameters$p |> length() - initial_parameters$parameters$p <- initial_parameters$parameters$p[-length_n_parm] expect_error( - object = fit_fims(initial_parameters, optimize = TRUE), + object = fit_fims(initialized_model, optimize = TRUE), regexp = "fims::Vector out of bounds for Population.1.log_init_naa: index 12 >= size 11" ) clear()