diff --git a/.gitignore b/.gitignore index 63e30d621fc768df0bf871b42d9e3a3d66afe428..a76188c7c4afe73a614577fa1fe5531d3a334b74 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,8 @@ cmake-build-debug autodocs test-data removed +<<<<<<< HEAD build-windows +======= +cmake-build-debug-system/ +>>>>>>> origin/butterflies diff --git a/CMakeLists.txt b/CMakeLists.txt index d671d9600b4b9e67e7b3e49bd2be5456cb94f548..05993bf744095920ea251a6b776c119aaf8a45da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ if ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUA -DHAVE_INLINE -DGSL_C99_INLINE -Wno-unused-function) -endif() +endif () if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") add_compile_options(-Wno-stringop-truncation -Wno-stringop-overflow) @@ -27,20 +27,18 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") add_compile_options("/std:c17") link_libraries(getopt) -else() +else () MESSAGE("Unknown compiler: ${CMAKE_C_COMPILER_ID}") -endif() +endif () set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) +set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) if (WIN32 AND NOT MSVC) add_compile_options(-D__USE_MINGW_ANSI_STDIO=1) endif () - - - execute_process( WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ERROR_QUIET @@ -51,18 +49,18 @@ execute_process( ) if ("${CATS_GIT_VERSION}" STREQUAL "") -execute_process( - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE CATS_GIT_VERSION - COMMAND git log -1 --format=%h -) + execute_process( + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CATS_GIT_VERSION + COMMAND git log -1 --format=%h + ) -endif() +endif () if ("${CATS_GIT_VERSION}" STREQUAL "") set(CATS_GIT_VERSION UNKNOWN) -endif() +endif () add_definitions("-DCATS_GIT_VERSION=${CATS_GIT_VERSION}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 334dac1cf7d5a79819550fbce2f0561c27f82fe5..2bf1b6ad8c63fa00570e3adfeff8cf18fcb32a36 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory(cats_strings) add_subdirectory(cats_ini) add_subdirectory(cats_csv) add_subdirectory(modules/cats_test_module) +add_subdirectory(modules/butterflies) \ No newline at end of file diff --git a/src/cats/CMakeLists.txt b/src/cats/CMakeLists.txt index a2c84bc7ae9ebf8a4bec0c5e1bc5476fe282d7f8..00239ae79b9860e9a72cbc61f3233041b377a531 100644 --- a/src/cats/CMakeLists.txt +++ b/src/cats/CMakeLists.txt @@ -39,7 +39,7 @@ include_directories(${GDAL_INCLUDE_DIR}) include_directories(${GSL_INCLUDE_DIR}) include_directories(${MPI_C_INCLUDE_PATH}) -add_library(libcats STATIC "" stats/statistics.c stats/statistics.h data/cats_global.c ../cats_windows.h test/test_ini.c test/test_ini.h debug/debug_vital_rates.c debug/debug_vital_rates.h) +add_library(libcats STATIC "" stats/statistics.c stats/statistics.h data/cats_global.c ../cats_windows.h test/test_ini.c test/test_ini.h debug/debug_vital_rates.c debug/debug_vital_rates.h overlays/overlay_resources.c overlays/overlay_resources.h paths/path_patterns.c paths/path_patterns.h) target_include_directories(libcats PUBLIC ".") target_sources(libcats PRIVATE diff --git a/src/cats/actions/action_overlay_update.c b/src/cats/actions/action_overlay_update.c index da4fd0a475523ad4b4000c6bd15c81c01e766739..a6ad6e2aaeca67cafc8df38dee230299f124cf19 100644 --- a/src/cats/actions/action_overlay_update.c +++ b/src/cats/actions/action_overlay_update.c @@ -30,6 +30,7 @@ #include "grids/cats_grid.h" #include "overlays/overlay_exclusion.h" #include "actions/cats_actions.h" +#include "paths/path_patterns.h" bool overlay_update_needed(struct cats_configuration *conf, int32_t time, enum overlay_type type); @@ -44,10 +45,12 @@ enum action_status action_overlay_update(struct cats_grid *grid, struct cats_con const int32_t year = conf->time.year_current; + + for (enum overlay_type type = OL_NONE; type < OL_MAX; type++) { if (type == OL_NONE) continue; - if (conf->overlays.enabled[type]) { + if (conf->overlays.overlay[type].enabled) { bool updated = update_overlay_if_needed(conf, year, type); if (type == OL_EXCLUSION && updated == true) { @@ -62,8 +65,8 @@ enum action_status action_overlay_update(struct cats_grid *grid, struct cats_con bool overlay_update_needed(struct cats_configuration *conf, int32_t time, enum overlay_type type) { - if (!conf->overlays.enabled[type]) return false; - int reload_interval = conf->overlays.reload_interval[type]; + if (!conf->overlays.overlay[type].enabled) return false; + int reload_interval = conf->overlays.overlay[type].reload_interval; switch (type) { case OL_EXCLUSION: @@ -72,6 +75,9 @@ bool overlay_update_needed(struct cats_configuration *conf, int32_t time, enum o case OL_HABITAT_TYPE_CC: if (conf->time.phase != PHASE_SIMULATION && conf->overlays.habitat_cc == NULL) return true; break; + case OL_RESOURCE: + if (conf->time.phase != PHASE_SIMULATION && conf->overlays.resources == NULL) return true; + break; case OL_MAX: case OL_NONE: return false; @@ -104,14 +110,13 @@ bool overlay_update_needed(struct cats_configuration *conf, int32_t time, enum o char *get_overlay_filename(struct cats_configuration *conf, enum overlay_type type, int32_t time) { assert(conf != NULL); - char *pattern = conf->overlays.filename[type]; - if (conf->overlays.is_static[type]) { + char *pattern = conf->overlays.overlay[type].filename; + if (conf->overlays.overlay[type].is_static) { return strdup(pattern); } - char *time_string = formatted_year(&conf->time, time); - char *result = replace_substring(pattern, "%Y", time_string); - free(time_string); + char *result = filename_pattern_substitution(pattern, conf, time); + return result; } @@ -120,17 +125,17 @@ char *get_overlay_filename(struct cats_configuration *conf, enum overlay_type ty bool update_overlay_if_needed(struct cats_configuration *conf, int32_t time, enum overlay_type type) { if (overlay_update_needed(conf, time, type) == false) { - log_message(LOG_INFO, "OVRL: reusing old overlay for overlay '%s'", conf->overlays.name[type]); + log_message(LOG_INFO, "OVRL: reusing old overlay for overlay '%s'", conf->overlays.overlay[type].name); return false; } char *overlay_filename = get_overlay_filename(conf, type, time); - log_message(LOG_INFO, "OVRL: overlay '%s': loading from file '%s'", conf->overlays.name[type], + log_message(LOG_INFO, "OVRL: overlay '%s': loading from file '%s'", conf->overlays.overlay[type].name, overlay_filename); load_overlay_from_file(conf, type, overlay_filename); - log_message(LOG_INFO, "OVRL: overlay '%s': loaded from file '%s'", conf->overlays.name[type], overlay_filename); + log_message(LOG_INFO, "OVRL: overlay '%s': loaded from file '%s'", conf->overlays.overlay[type].name, overlay_filename); free(overlay_filename); diff --git a/src/cats/actions/action_population_grid_output.c b/src/cats/actions/action_population_grid_output.c index 790ec0faa376d8e41c7aa66ce7075c6f85473129..323793b62790da15249d93c4dd50b2e586939074 100644 --- a/src/cats/actions/action_population_grid_output.c +++ b/src/cats/actions/action_population_grid_output.c @@ -53,7 +53,7 @@ enum action_status action_save_adults(struct cats_grid *grid, struct cats_config save_population_to_gdal(grid, conf); - if (conf->write_all) { + if (conf->output.write_all) { save_juveniles_to_gdal(grid, conf); save_seeds_to_gdal(grid, conf); } diff --git a/src/cats/actions/process_inter_period_survival.c b/src/cats/actions/process_inter_period_survival.c index cf10c16cc8c17c8c9f4e08ee3ac5949469121e54..479385a710b9537fb919d1156d0de5ce54e15fdb 100644 --- a/src/cats/actions/process_inter_period_survival.c +++ b/src/cats/actions/process_inter_period_survival.c @@ -88,14 +88,14 @@ enum action_status inter_period_survival_action(struct cats_grid *grid, struct c if (conf->command_line_options.lambda_test) { - write_stats(grid, conf, conf->lambda_stat_file, "pre-trans"); + write_stats(grid, conf, conf->output.lambda_stat_file, "pre-trans"); } threaded_action(&area_inter_period_survival, grid, conf, TS_DEFAULT); if (conf->command_line_options.lambda_test) { - write_stats(grid, conf, conf->lambda_stat_file, "post-trans"); + write_stats(grid, conf, conf->output.lambda_stat_file, "post-trans"); } diff --git a/src/cats/actions/process_seed_dispersal.c b/src/cats/actions/process_seed_dispersal.c index 6f106be8780ec9eba814ee461f936b68c1f263d6..8157c95b09fcf74f17d9d72cbca5e0bdac6cf45c 100644 --- a/src/cats/actions/process_seed_dispersal.c +++ b/src/cats/actions/process_seed_dispersal.c @@ -131,7 +131,7 @@ enum action_status process_disperse_seeds(struct cats_grid *grid, struct cats_co } #endif - if (conf->write_all && is_output_year(&conf->time)) { + if (conf->output.write_all && is_output_year(&conf->time)) { save_dispersed_seeds_to_gdal(grid, conf); } diff --git a/src/cats/actions/setup.c b/src/cats/actions/setup.c index 9b01c39d39bcf54f83d243c44c1ae89e5f8f6aff..d88079d3b9f4f5fe416fee6c3f9d6d6b7c425dc1 100644 --- a/src/cats/actions/setup.c +++ b/src/cats/actions/setup.c @@ -68,8 +68,8 @@ void setup_lambda_test_simulation(struct cats_configuration *conf, struct cats_g } conf->lambda_burn_in_time = conf->time.simulation_length / 5; - conf->lambda_stat_file = create_and_initialize_lambda_stat_file(conf, NULL, grid); - write_stats(grid, conf, conf->lambda_stat_file, "initial"); + conf->output.lambda_stat_file = create_and_initialize_lambda_stat_file(conf, NULL, grid); + write_stats(grid, conf, conf->output.lambda_stat_file, "initial"); } @@ -123,8 +123,8 @@ void setup_lambda_gradient_simulation(struct cats_configuration *conf, struct ca } conf->lambda_burn_in_time = conf->time.simulation_length / 5; - conf->lambda_stat_file = create_and_initialize_lambda_stat_file(conf, "gradient", grid); - write_stats(grid, conf, conf->lambda_stat_file, "initial"); + conf->output.lambda_stat_file = create_and_initialize_lambda_stat_file(conf, "gradient", grid); + write_stats(grid, conf, conf->output.lambda_stat_file, "initial"); } diff --git a/src/cats/actions/setup_actions.c b/src/cats/actions/setup_actions.c index 1c03f487fcc98b0dbbaf28132177801f26e7aa3c..6c2dcd63284dd14c92b6c041a2ec9756f8b61532 100644 --- a/src/cats/actions/setup_actions.c +++ b/src/cats/actions/setup_actions.c @@ -160,7 +160,9 @@ append_action(struct cats_configuration *conf, action_function action, enum cats void list_actions_full(struct cats_configuration *conf) { - log_message(LOG_INFO, "Action functions: %d", conf->action_functions.count); + + log_message(LOG_INFO, "Number of registered action functions: %d", conf->action_functions.count); + for (int32_t i = 0; i < conf->action_functions.count; i++) { log_message(LOG_INFO, "%d: %s %p ", i, conf->action_functions.names[i], conf->action_functions.functions[i]); diff --git a/src/cats/cats.c b/src/cats/cats.c index 9cbc75a925df860a35ab8e5cf8adb37a3d5a9bb0..202540723965191f9a49fdfe6c225c098d9925a9 100644 --- a/src/cats/cats.c +++ b/src/cats/cats.c @@ -131,8 +131,10 @@ void load_modules(struct cats_configuration *conf) { for (int32_t i = 0; i < conf->modules.found_count; i++) { int32_t id = load_module(conf, &conf->modules.module[i]); - log_message(LOG_RAW, "%s: %d/%d-> %d (%d)\n", conf->modules.module[i].name, i, conf->modules.count, id, - conf->modules.module[i].flags); + + log_message(LOG_IMPORTANT, "Loaded module %d of %d: '%s' with id %d and flags %d", + i + 1, conf->modules.count, conf->modules.module[i].name, id, conf->modules.module[i].flags); + } for (int32_t i = 0; i < conf->param_count; i++) { @@ -163,8 +165,8 @@ void load_modules(struct cats_configuration *conf) } + log_message(LOG_INFO, "%s: Loading vital rate functions for module id %d", __func__, j); - printf("%s: %d\n", __func__, j); if (p->module_data[j].load_species_param_function) { p->module_data[j].load_species_param_function(conf, conf->ini, p->species_config_section, p); } @@ -188,8 +190,9 @@ struct cats_configuration *load_main_configuration(const struct program_options load_modules(conf); + setup_module_directories(conf); + - print_modules(conf); setup_simulation_actions(conf); // which values of the configuration file did we ignore? return conf; diff --git a/src/cats/cats_global.h b/src/cats/cats_global.h index 1795b433c170f28a84e05a22e432899d6dabe49f..cc7a794edd752308d9cc0cce1078686bcdc1b6a4 100644 --- a/src/cats/cats_global.h +++ b/src/cats/cats_global.h @@ -98,6 +98,9 @@ struct cats_debug_options { FILE *misc_debug_file; /// @brief global variable: file handle for a debug file documenting a single cells }; - +static void abort_implemented(const char *name) { + fprintf(stderr, "function '%s' is not yet implemented\n", name); + exit_cats(EXIT_FAILURE); +} #endif //CATS_GLOBAL_H_ diff --git a/src/cats/command_line/command_line_options.c b/src/cats/command_line/command_line_options.c index 460bc247f936ec5db3ac05ce8745b9d0c4c0920f..71199d3de5f53854188f210cf2d3902a2bb9bef7 100644 --- a/src/cats/command_line/command_line_options.c +++ b/src/cats/command_line/command_line_options.c @@ -40,8 +40,11 @@ static struct option longopts[] = { {OPTION_SCALE_LAMBDA_ONLY_NAME, no_argument, NULL, OPTION_SCALE_LAMBDA_ONLY}, {OPTION_SCALE_LAMBDA_TEST_NAME, no_argument, NULL, OPTION_SCALE_LAMBDA_TEST}, {OPTION_SCALE_GRADIENT_NAME, no_argument, NULL, OPTION_SCALE_GRADIENT}, + {OPTION_LOG_FILE_NAME, required_argument, NULL, OPTION_LOG_FILE}, {OPTION_SUMMARY_FILE_NAME, required_argument, NULL, OPTION_SUMMARY_FILE}, + + {OPTION_JSON_NAME, no_argument, NULL, OPTION_JSON}, {OPTION_DEBUG_MPI_NAME, no_argument, NULL, OPTION_DEBUG_MPI}, {OPTION_SCALE_LAMBDA_CC_NAME, required_argument, NULL, OPTION_SCALE_LAMBDA_CC}, @@ -51,9 +54,11 @@ static struct option longopts[] = { {OPTION_DEBUG_CONFIG_UNSPECIFIED_NAME, no_argument, NULL, OPTION_DEBUG_CONFIG_UNSPECIFIED}, {OPTION_DEBUG_LAMBDA_NAME, no_argument, NULL, OPTION_DEBUG_LAMBDA}, {OPTION_DEBUG_VR_NAME, no_argument, NULL, OPTION_DEBUG_VR}, + {OPTION_DEBUG_VR_SCALE_NAME, required_argument, NULL, OPTION_DEBUG_VR_SCALE}, {OPTION_DEBUG_VR_SUBDIVISIONS_NAME, required_argument, NULL, OPTION_DEBUG_VR_SUBDIVISIONS}, {OPTION_DEBUG_VR_OT_NAME, required_argument, NULL, OPTION_DEBUG_VR_OT}, + {OPTION_POISSON_DAMPENING_NAME, required_argument, NULL, OPTION_POISSON_DAMPENING}, {OPTION_POISSON_DAMPENING_MIN_NAME, required_argument, NULL, OPTION_POISSON_DAMPENING_MIN}, {OPTION_POISSON_MAXIMUM_NAME, required_argument, NULL, OPTION_POISSON_MAXIMUM}, diff --git a/src/cats/command_line/command_line_options.h b/src/cats/command_line/command_line_options.h index 11634153c000cacef16e2452607fde5a2d8646c9..a5ed7b70bdd3fad7aea8ebba22209e90a4c7ae51 100644 --- a/src/cats/command_line/command_line_options.h +++ b/src/cats/command_line/command_line_options.h @@ -30,6 +30,7 @@ #include "logging/logging.h" #include "cats_global.h" + #define OPTION_LOG_FILE 1111 #define OPTION_LOG_FILE_NAME "log-file" diff --git a/src/cats/configuration/check_configuration.c b/src/cats/configuration/check_configuration.c index f3dae83058dc9d89cef60e62ec83ab09c21a0656..4eaae98ecaf30719d2db216b7da409c5473269d9 100644 --- a/src/cats/configuration/check_configuration.c +++ b/src/cats/configuration/check_configuration.c @@ -64,7 +64,8 @@ int rate_failed(cats_dt_rates value, char *name) int range_float_check_at_least(cats_dt_rates value, cats_dt_rates larger_than, char *name) { if (value < larger_than) { - log_message(LOG_ERROR, "%s out of range: is %f, has to be >= %f", name, (double) value, (double) larger_than); + + log_message(LOG_ERROR, "%s out of range: is %Lf, has to be >= %Lf", name, value, larger_than); return 1; } diff --git a/src/cats/configuration/configuration.c b/src/cats/configuration/configuration.c index 35ed78474e11b09b46374bb3b60325d8b597c3e9..6d6fb2af6824235e861c56bdd6f93df2d5b257e6 100644 --- a/src/cats/configuration/configuration.c +++ b/src/cats/configuration/configuration.c @@ -64,7 +64,7 @@ void init_cats_configuration(struct cats_configuration *conf) log_message(LOG_ERROR, "conf is NULL in %s", __func__); exit(EXIT_FAILURE); } - + init_stats_registry(&conf->stats_registry); init_module_registry(&conf->modules); reset_overlay(&conf->overlays); zero_statistics_stats(&conf->global_stats); @@ -76,13 +76,21 @@ void init_cats_configuration(struct cats_configuration *conf) conf->cycle.action_counter = 0; conf->environment.count = 0; - conf->output_directory = strdup(DEFAULT_OUTPUT_DIRECTORY); + conf->output.output_directory = strdup(DEFAULT_OUTPUT_DIRECTORY); conf->stats_populated_threshold = DEFAULT_POPULATED_THRESHOLD; - conf->param_max_threads = -1; - conf->performance_file = NULL; + conf->param_max_threads = 1; + conf->output.performance_file = NULL; + conf->grid_count = 1; + conf->overlays.custom_overlay_count = 0; + conf->overlays.custom_overlays = NULL; + conf->overlays.registered_custom_overlay_names = new_string_array(); + conf->output.needed_output_directories = new_string_array(); + conf->output.needed_module_output_directories = new_string_array(); + conf->output.have_glm = false; + conf->output.write_all = false; #ifdef USEMPI conf->mpi.world_size = 1; @@ -171,13 +179,13 @@ void post_process_configuration(struct cats_configuration *conf) char *filename = malloc_or_die(count); snprintf(filename, count, "performance/%s_%03d.csv", conf->run_name, conf->simulation.replicate); - conf->performance_file = fopen(filename, "w"); - if (!conf->performance_file) { + conf->output.performance_file = fopen(filename, "w"); + if (!conf->output.performance_file) { log_message(LOG_ERROR, "failed to open %s: %s", filename, strerror(errno)); exit_cats(EXIT_FAILURE); } - fprintf(conf->performance_file, "%s\n", "year,stat,cpu_time,wall_time"); + fprintf(conf->output.performance_file, "%s\n", "year,stat,cpu_time,wall_time"); } if (conf->param_max_threads <= 0) { @@ -222,14 +230,14 @@ void cleanup_configuration(struct cats_configuration **conf_orig) struct cats_configuration *conf = *conf_orig; // THIS HAS TO BE THE FIRST ENTRY cleanup_module_registry(&conf->modules); - if (conf->summary_file) { - fflush(conf->summary_file); - fclose(conf->summary_file); - free(conf->summary_file_name); + + if (conf->output.summary_file) { + fclose(conf->output.summary_file); + free(conf->output.summary_file_name); } - if (conf->statsfile_global) fclose(conf->statsfile_global); - free(conf->file_content); + if (conf->output.statsfile_global) fclose(conf->output.statsfile_global); + free(conf->output.file_content); /* keep sorted!*/ free(conf->cycle.actions); @@ -253,7 +261,7 @@ void cleanup_configuration(struct cats_configuration **conf_orig) free(conf->dispersal); - free(conf->output_directory); + free(conf->output.output_directory); for (int i = 0; i < conf->param_count; i++) { free(conf->param[i].preset_name); @@ -279,8 +287,8 @@ void cleanup_configuration(struct cats_configuration **conf_orig) //free(conf->predictors); cleanup_overlays(&conf->overlays); cleanup_environment_registry(&conf->environment_registry); - if (conf->performance_file) fclose(conf->performance_file); - conf->performance_file = NULL; + if (conf->output.performance_file) fclose(conf->output.performance_file); + conf->output.performance_file = NULL; free_ini(conf->ini); #ifdef USEMPI @@ -291,7 +299,8 @@ void cleanup_configuration(struct cats_configuration **conf_orig) free(conf->command_line_options.output_directory); free(conf->global_stats.populated_by_classes); - + free_string_array(&conf->output.needed_output_directories); + free_string_array(&conf->output.needed_module_output_directories); free(conf->param); conf->param = NULL; free(conf->run_name); diff --git a/src/cats/configuration/configuration.h b/src/cats/configuration/configuration.h index 15542b32603c58d418b5d51d210939d9ec800738..ca57b76b3f31e187ff92afa84b2f60f19713f332 100644 --- a/src/cats/configuration/configuration.h +++ b/src/cats/configuration/configuration.h @@ -122,19 +122,35 @@ struct cats_environment_collection { }; +struct cats_output { + struct string_array *needed_output_directories; + struct string_array *needed_module_output_directories; + bool have_glm; + bool write_all; + char *output_directory; + char *summary_file_name; + FILE *summary_file; + char *log_file_name; + FILE *lambda_stat_file; + bool param_compression; + FILE *statsfile_global; + FILE *performance_file; + char *file_content; + +}; + /// @brief Contains the configuration and state of the simulation struct cats_configuration { - char *output_directory; - char *summary_file_name; - FILE *summary_file; - char *log_file_name; + bool quiet; struct cats_global *global; struct program_options command_line_options; bool debug_enabled; struct cats_ini *ini; + struct cats_stats_registry stats_registry; + // cats_stats cats_dt_population stats_populated_threshold; @@ -149,7 +165,6 @@ struct cats_configuration { int link_count; - bool write_all; bool all_species_same; @@ -177,7 +192,7 @@ struct cats_configuration { struct cats_overlays overlays; - bool param_compression; + int32_t grid_count; @@ -195,7 +210,6 @@ struct cats_configuration { char *run_name; // actions - FILE *lambda_stat_file; //enum simulation_mode mode; @@ -205,8 +219,7 @@ struct cats_configuration { int64_t rng_seed; - FILE *statsfile_global; - FILE *performance_file; + bool timing_info; struct cats_cycle cycle; @@ -216,8 +229,11 @@ struct cats_configuration { cats_dt_rates direct_scale_factor; cats_dt_rates lambda_at_OT; // FIXME, per parameter set - char *file_content; + enum cats_debug_flags debug; + + + struct cats_output output; }; void cleanup_configuration(struct cats_configuration **conf); diff --git a/src/cats/configuration/load_configuration.c b/src/cats/configuration/load_configuration.c index 9f6bfeca849d5c38a3eb179f969412245873c148..8fa69006c97af31901e61a2ae52c88625f6caf04 100644 --- a/src/cats/configuration/load_configuration.c +++ b/src/cats/configuration/load_configuration.c @@ -67,14 +67,16 @@ load_configuration_from_file(const char *filename, const struct program_options // create empty configuration struct cats_configuration *conf = new_configuration(); conf->quiet = command_line_options->quiet; - conf->log_file_name = command_line_options->log_file; - conf->summary_file = NULL; - conf->summary_file_name = command_line_options->summary_file; - if (conf->summary_file_name) { - conf->summary_file = fopen(conf->summary_file_name, "w"); - ENSURE_FILE_OPENED(conf->summary_file, conf->summary_file_name) - fprintf(conf->summary_file, "run.name,replicate,year,file.type,file.name\n"); - fflush(conf->summary_file); + + conf->output.log_file_name = command_line_options->log_file; + conf->output.summary_file = NULL; + conf->output.summary_file_name = command_line_options->summary_file; + if (conf->output.summary_file_name) { + conf->output.summary_file = fopen(conf->output.summary_file_name, "w"); + ENSURE_FILE_OPENED(conf->output.summary_file, conf->output.summary_file_name) + fprintf(conf->output.summary_file, "type,path,year\n"); + if (conf->output.log_file_name) fprintf(conf->output.summary_file, "log,%s,\n",conf->output.log_file_name); + } #ifdef USEMPI mpi_setup(conf); @@ -120,7 +122,7 @@ load_configuration_from_file(const char *filename, const struct program_options conf->ini = ini; // we keep a copy of the configuration file contents in memory if (conf->command_line_options.need_conf) { - conf->file_content = slurp_file(filename); + conf->output.file_content = slurp_file(filename); } // load data from loaded ini file into the configuration @@ -135,10 +137,6 @@ load_configuration_from_file(const char *filename, const struct program_options // clean up and return free(count.dispersals); - if (conf->log_file_name && conf->summary_file) { - fprintf(conf->summary_file, "%s,%d,NA,log,%s\n", conf->run_name, conf->simulation.replicate, conf->log_file_name); - fflush(conf->summary_file); - } return conf; } diff --git a/src/cats/configuration/load_configuration_general.c b/src/cats/configuration/load_configuration_general.c index 2c83421d0bd167f779421fec9e5b815e56fcd147..5b044dc92bb1eed89b518427e11d08244f3d0360 100644 --- a/src/cats/configuration/load_configuration_general.c +++ b/src/cats/configuration/load_configuration_general.c @@ -63,14 +63,14 @@ void load_general_info(struct cats_configuration *conf, struct cats_ini *ini) char *od; bool have_od = load_conf_value(false, ini, "general", "output directory", &od); if (have_od) { - free(conf->output_directory); - conf->output_directory = od; + free(conf->output.output_directory); + conf->output.output_directory = od; } load_conf_value(false, ini, "general", "debug", &conf->debug_enabled); - load_conf_value(false, ini, "general", "compression", &conf->param_compression); + load_conf_value(false, ini, "general", "compression", &conf->output.param_compression); - load_conf_value(false, ini, "general", "write all", &conf->write_all); + load_conf_value(false, ini, "general", "write all", &conf->output.write_all); load_conf_value(false, ini, "general", "file format", &conf->geometry.file_format); load_conf_value(false, ini, "general", "ignore raster projection", &conf->geometry.ignore_raster_projection); load_conf_value(false, ini, "general", "threads", &conf->param_max_threads); diff --git a/src/cats/configuration/load_configuration_glm.c b/src/cats/configuration/load_configuration_glm.c index e3b80973512cb3f9d6a6f03c5fe493bca8b88442..366749270f35673dabefdf79b0faee9a217bf2ac 100644 --- a/src/cats/configuration/load_configuration_glm.c +++ b/src/cats/configuration/load_configuration_glm.c @@ -167,5 +167,7 @@ void add_glm_from_conf(struct cats_configuration *conf, struct cats_ini *ini, ch predictors->string[i]); add_to_environment(set, env); } + + conf->output.have_glm = true; } diff --git a/src/cats/configuration/load_configuration_overlays.c b/src/cats/configuration/load_configuration_overlays.c index 3db90fc434579cc79fee970f8f78a512a2043d3b..bbacc6f7e1b1828624bd3efdbb463d9cb2e68028 100644 --- a/src/cats/configuration/load_configuration_overlays.c +++ b/src/cats/configuration/load_configuration_overlays.c @@ -52,25 +52,34 @@ void load_overlay_configuration(struct cats_configuration *conf, struct cats_ini char *type_name = NULL; load_conf_value(true, ini, overlay_section, "type", &type_name); - enum overlay_type type = get_overlay_type_from_name(type_name); - free(type_name); + enum overlay_type type = get_overlay_type_from_name(type_name, conf->overlays.registered_custom_overlay_names); + + struct cats_overlay *overlay = NULL; + + if (type < OL_CUSTOM && type > OL_NONE) { + overlay = &conf->overlays.overlay[type]; + } else if (type == OL_CUSTOM) { + overlay = add_overlay(conf, type_name); + } - conf->overlays.enabled[type] = true; - load_conf_value(true, ini, overlay_section, "filename pattern", &conf->overlays.filename[type]); - load_conf_value(false, ini, overlay_section, "reload interval", &conf->overlays.reload_interval[type]); + overlay->enabled = true; - if (conf->overlays.reload_interval[type] > 0) { - conf->overlays.is_static[type] = false; + + load_conf_value(true, ini, overlay_section, "filename pattern", &overlay->filename); + load_conf_value(false, ini, overlay_section, "reload interval", &overlay->reload_interval); + + if (overlay->reload_interval > 0) { + overlay->is_static = false; } else { - conf->overlays.is_static[type] = true; + overlay->is_static = true; } conf->overlays.have_overlays = true; long double default_value = NAN; - conf->overlays.name[type] = strdup(overlay_name); + overlay->name = strdup(overlay_name); switch (type) { case OL_EXCLUSION: @@ -78,18 +87,21 @@ void load_overlay_configuration(struct cats_configuration *conf, struct cats_ini case OL_HABITAT_TYPE_CC: load_conf_value(true, ini, overlay_section, "table filename", - &conf->overlays.filename_aux[type]); + &overlay->filename_aux); load_conf_value(true, ini, overlay_section, "default value", &default_value); - conf->overlays.aux_data[OL_HABITAT_TYPE_CC] = load_habitat_layer_cc_aux( - conf->overlays.filename_aux[type], + overlay->aux_data = load_habitat_layer_cc_aux( + overlay->filename_aux, (double) default_value); break; + case OL_RESOURCE: + break; case OL_NONE: case OL_MAX: - default: + case OL_CUSTOM: log_message(LOG_ERROR, "unknown or unhandled overlay type '%s'", overlay_name); exit(EXIT_FAILURE); } + free(type_name); } free_string_array(&overlay_names); diff --git a/src/cats/configuration/load_configuration_species_params.c b/src/cats/configuration/load_configuration_species_params.c index 8bd5ec5c752c5216b1a210d143e78ada9fefc810..5ac0ad001137a7377982d5aca0fa531f7aff834b 100644 --- a/src/cats/configuration/load_configuration_species_params.c +++ b/src/cats/configuration/load_configuration_species_params.c @@ -103,7 +103,8 @@ void load_conf_vital_rate(struct cats_vital_rate *vr, struct cats_configuration bool req = !vr->preset.have_maximum; - // printf("%s: %s required %d\n", __func__, vr->name, req); + log_message(LOG_INFO, "%s: Loading vital rate '%s' (required: %d)", __func__, vr->name, req); + if (vr == &p->carrying_capacity) req = true; bool have_max = load_conf_value(req, ini, species_section, vital_rate_maximum, &vr->max_rate); diff --git a/src/cats/configuration/load_vital_rates.c b/src/cats/configuration/load_vital_rates.c index 8788a52a48f211031b9c39a48f92f51d0ba8259c..326f244dbc045215eb43f6b798baf7f50c60b330 100644 --- a/src/cats/configuration/load_vital_rates.c +++ b/src/cats/configuration/load_vital_rates.c @@ -253,7 +253,9 @@ void conf_load_vital_rate_extended(struct cats_vital_rate *vr, struct cats_confi conf_load_vital_rates_density(vr, conf, ini, species_section, param); char *vital_rate_minimum = compound_string(vr_name, "minimum", sep); - load_conf_value(false, ini, species_section, vital_rate_minimum, &vr->min_rate); + cats_dt_rates min_rate; + load_conf_value(false, ini, species_section, vital_rate_minimum, &min_rate); + set_vital_rate_minimum(vr, min_rate); free(vital_rate_minimum); switch (param->parametrization) { diff --git a/src/cats/configuration/print_configuration.c b/src/cats/configuration/print_configuration.c index 62655e43a5c964e21406cf4c7e14700d83a7c7b0..8265fe845b4b8d30efb164dc770ade3b11a814a0 100644 --- a/src/cats/configuration/print_configuration.c +++ b/src/cats/configuration/print_configuration.c @@ -32,6 +32,7 @@ #include "preset.h" #include "inline_vital_rates.h" #include "dispersal/dispersal.h" +#include "modules/load_module.h" const char *get_dispersal_name(enum dispersal_type type); @@ -45,7 +46,7 @@ void print_overlay_summary(struct cats_configuration *conf) for (enum overlay_type ol = OL_NONE + 1; ol < OL_MAX; ol++) { print_category(1, "overlay type", get_overlay_name(ol)); - print_string(2, "enabled", bool_to_string(conf->overlays.enabled[ol])); + print_string(2, "enabled", bool_to_string(conf->overlays.overlay[ol].enabled)); } } @@ -203,7 +204,11 @@ void print_config_summary(struct cats_configuration *conf) print_environments(conf, 0); log_message(LOG_RAW, "%s\n", TEXT_DIVIDER); print_environment_sets(conf); + + log_message(LOG_RAW, "%s\n", TEXT_DIVIDER); + print_modules(conf); log_message(LOG_RAW, "%s\n", TEXT_DIVIDER); + } diff --git a/src/cats/data/cats_datatypes.c b/src/cats/data/cats_datatypes.c index c0817acfd282833ac0edf5e288bfebbf48259e67..46afd2732690e3820888896b2665732cd6488e33 100644 --- a/src/cats/data/cats_datatypes.c +++ b/src/cats/data/cats_datatypes.c @@ -30,3 +30,126 @@ #include "cats_datatypes.h" #include "logging.h" + + +const char *true_values[] = {"1", "y", "t"}; +const char *false_values[] = {"0", "n", "f"}; +const int true_counts = (int) (sizeof(true_values) / sizeof(char *)); +const int false_counts = (int) (sizeof(false_values) / sizeof(char *)); + + +bool string_to_double(char *string, double *value) +{ + if (string == NULL || strlen(string) == 0) return false; + + char *end_pointer = NULL; + errno = 0; + + double converted = strtod(string, &end_pointer); + + if (strlen(end_pointer) != 0) { + log_message(LOG_WARNING, "%s: invalid or unused characters when converting '%s' to 'double': '%s'", + __func__, string, + end_pointer); + + } + + if (errno == 0) { + *value = converted; + return true; + } + return false; +} + + +bool string_to_float(char *string, float *value) +{ + if (string == NULL || strlen(string) == 0) return false; + + char *end_pointer = NULL; + errno = 0; + + float converted = strtof(string, &end_pointer); + + if (strlen(end_pointer) != 0) { + log_message(LOG_WARNING, "%s: invalid or unused characters when converting '%s' to 'float': '%s'", + __func__, string, + end_pointer); + return false; + } + + if (errno == 0) { + *value = converted; + return true; + } + return false; +} + + +bool string_to_long_double(char *string, long double *value) +{ + if (string == NULL || strlen(string) == 0) return false; + + char *end_pointer = NULL; + errno = 0; + + long double converted = strtold(string, &end_pointer); + + if (strlen(end_pointer) != 0) { + log_message(LOG_WARNING, "%s: invalid or unused characters when converting '%s' to 'long double': '%s'", + __func__, string, end_pointer); + return false; + } + + if (errno == 0) { + *value = (cats_dt_rates) converted; + return true; + } + return false; +} + + +bool string_to_bool(char *string, bool *value) +{ + if (string == NULL) return false; + + for (int i = 0; i < true_counts; i++) { + if (!strncmp(string, true_values[i], 1)) { + *value = true; + return true; + } + } + + for (int i = 0; i < false_counts; i++) { + if (!strncmp(string, false_values[i], 1)) { + *value = false; + return true; + } + } + + return false; +} + + +bool string_to_integer(char *string, int32_t *value) +{ + if (string == NULL || strlen(string) == 0) return false; + + errno = 0; + char *end_pointer = NULL; + long converted = strtol(string, &end_pointer, 10); + + if (strlen(end_pointer) != 0) { + log_message(LOG_WARNING, "%s: invalid or unused characters when converting '%s' to integer %ld: '%s'", + __func__, string, converted, end_pointer); + return false; + } + + if (errno == 0) { + *value = (int32_t) converted; + return true; + } + return false; +} + + diff --git a/src/cats/debug/debug_vital_rates.c b/src/cats/debug/debug_vital_rates.c index 420e03e57ecc613d894d030344842d9bf68859f3..ec6f3625f7bbd5623949155b7a2ecf6e19a1f456 100644 --- a/src/cats/debug/debug_vital_rates.c +++ b/src/cats/debug/debug_vital_rates.c @@ -150,7 +150,8 @@ static inline void debug_vr_write_line(FILE *f, cats_dt_rates result, cats_dt_environment suit, cats_dt_rates N, cats_dt_rates K_suit, cats_dt_rates K_max, cats_dt_rates density, int32_t dens_idx, const struct cats_grid *grid) { - fprintf(f, "%0.6Lf,%0.6Lf,%0.6f,%0.6Lf,%0.6Lf,%0.6Lf,%0.6Lf,%0.6Lf,%0.6Lf,%d,%0.6Lf\n", + fprintf(f, + "%0.6Lf,%0.6Lf,%0.6f,%0.6Lf,%0.6Lf,%0.6Lf,%0.6Lf,%0.6Lf,%0.6Lf,%d,%0.6Lf\n", result, grid->param.scale_factor, suit, diff --git a/src/cats/debug/debug_vital_rates.h b/src/cats/debug/debug_vital_rates.h index ee6cddfcfaa2c5171f056db59667dd53dd24b7bc..442e19712f0d92a2fa61655e3ed38fd17f78df87 100644 --- a/src/cats/debug/debug_vital_rates.h +++ b/src/cats/debug/debug_vital_rates.h @@ -26,8 +26,6 @@ #define CATS_DEBUG_VITAL_RATES_H #include "configuration/configuration.h" -struct cats_grid *minimal_grid(struct cats_configuration *conf, struct cats_environment *env); -struct cats_environment *minimal_suitability_environment(void); void debug_vital_rates(struct cats_configuration *conf, const struct program_options *command_line_options); diff --git a/src/cats/grids/cats_grid.c b/src/cats/grids/cats_grid.c index 61ba30239c7e478f7371e571a03c7affec6dd205..48daf0cb70b21dfaf6c9d29407ea73eaf5737876 100644 --- a/src/cats/grids/cats_grid.c +++ b/src/cats/grids/cats_grid.c @@ -42,10 +42,9 @@ #include "plants/seeds.h" #include "stats/grid_stats.h" -#include "logging.h" #include "gdal_load.h" #include "grid_setup.h" -#include "../../memory/cats_memory.h" +#include "memory/cats_memory.h" #include "misc/cats_random.h" #ifdef USEMPI @@ -187,7 +186,7 @@ void initialize_grid(struct cats_grid *grid, struct cats_configuration *conf, st // FIXME: SCALE FACTOR make this unnecessary and decouple scale factor calculations from **grids // Attention: we need to call finalize_grid() later if only partially initialised - init_statistics(&grid->stats); + init_statistics(&grid->stats, &conf->stats_registry); setup_grid_population_structures(grid); @@ -251,29 +250,25 @@ void cleanup_grid_seeds_and_juveniles(struct cats_grid *grid) free_grid(&grid->seeds_produced, grid->dimension.rows); free_grid(&grid->dispersed_seeds, grid->dimension.rows); - if (grid->seed_bank) { for (cats_dt_coord row = 0; row < grid->dimension.rows; row++) { for (cats_dt_coord col = 0; col < grid->dimension.cols; col++) { destroy_seed_structure(grid, row, col); } - - free(grid->seed_bank[row]); } } + if (grid->juveniles) { for (cats_dt_coord row = 0; row < grid->dimension.rows; row++) { for (cats_dt_coord col = 0; col < grid->dimension.cols; col++) { destroy_juveniles(grid, row, col); } - free(grid->juveniles[row]); - grid->juveniles[row] = NULL; + free(grid->seed_bank[row]); } } - free(grid->seed_bank); free(grid->juveniles); grid->juveniles = NULL; @@ -347,7 +342,7 @@ void do_all_grids(struct cats_grid **grids, struct cats_configuration *conf, gri void initialize_grid_stats(struct cats_grid *grid, struct cats_configuration *conf) { assert(grid != NULL); - init_statistics(&grid->stats); + init_statistics(&grid->stats, &conf->stats_registry); grid->stats.has_been_populated = (char **) new_raw_2d_array_from_dimension(grid->dimension, sizeof(char)); char *filename = get_grid_stat_filename(grid, conf); @@ -356,10 +351,9 @@ void initialize_grid_stats(struct cats_grid *grid, struct cats_configuration *co remove(filename); grid->stats.file = fopen(filename, "a+"); ENSURE_FILE_OPENED(grid->stats.file, filename) - if (conf->summary_file) { - fprintf(conf->summary_file, "%s,%d,NA,grid-stats,%s\n", conf->run_name, conf->simulation.replicate, filename); - fflush(conf->summary_file); - } + + if (conf->output.summary_file) fprintf(conf->output.summary_file, "grid-stats,%s,\n",filename); + free(filename); write_grid_stats(conf, grid, true); fflush(grid->stats.file); diff --git a/src/cats/grids/gdal_helper.c b/src/cats/grids/gdal_helper.c index ab8ef1fe46d07abfcaef2904a5bc60c7b917ac50..4a924ab9d2c4b2eae4771f1a7c038418f992aab3 100644 --- a/src/cats/grids/gdal_helper.c +++ b/src/cats/grids/gdal_helper.c @@ -166,18 +166,18 @@ void verify_raster_geometry(GDALDatasetH dataset, struct simulation_geometry *ge char *projection = (char *) GDALGetProjectionRef(dataset); if (geometry->projection_string == NULL || strlen(geometry->projection_string) == 0) { - log_message(LOG_WARNING, "GEOM: global projection string not set, ignoring projection '%s", projection); + log_message(LOG_INFO, "GEOM: global projection string not set, ignoring projection '%s'", projection); } else { if (strcmp(projection, geometry->projection_string) != 0) { enum cats_log_level ll; if (geometry->ignore_raster_projection) { - ll = LOG_WARNING; + ll = LOG_INFO; } else { ll = LOG_ERROR; errors += 1; } - log_message(ll, "GEOM: global projection string '%s' not equal to projection '%s", - geometry->projection_string, projection); + // log_message(ll, "GEOM: global projection string '%s' not equal to projection '%s'", geometry->projection_string, projection); + log_message(ll, "GEOM: projection mismatch with reference projection"); } @@ -234,7 +234,7 @@ create_gdal_dataset(const struct simulation_geometry *geometry, const char *file } GDALSetMetadataItem(dataset, "TIFFTAG_SOFTWARE", cats_version(), NULL); - GDALSetMetadataItem(dataset, "TIFFTAG_IMAGEDESCRIPTION", conf->file_content, NULL); + GDALSetMetadataItem(dataset, "TIFFTAG_IMAGEDESCRIPTION", conf->output.file_content, NULL); CSLDestroy(options); return dataset; diff --git a/src/cats/grids/gdal_load.c b/src/cats/grids/gdal_load.c index d184c729e4f80cdabdfdeb7f4f4b618c9a949125..89efc014e9d04db5c38bc9e478074c12b57028b9 100644 --- a/src/cats/grids/gdal_load.c +++ b/src/cats/grids/gdal_load.c @@ -26,7 +26,6 @@ #include <cpl_string.h> #include "data/cats_grid.h" #include "configuration/configuration.h" -#include "logging.h" #include "paths/paths.h" #include "gdal_load.h" #include "overlays/overlays.h" @@ -141,7 +140,6 @@ void load_gdal_data(const struct raster_load_wrapper *data) double nodata = GDALGetRasterNoDataValue(band, &have_nodata); if (have_nodata) { log_message(LOG_INFO, "\t'%s': found nodata value %f ", data->filename, nodata); - } double orig = nodata; @@ -189,7 +187,7 @@ void load_gdal_data(const struct raster_load_wrapper *data) val = NAN; } - data->target_environment[r][c] = val; + data->target_environment[r][c] = (cats_dt_environment) val; } break; diff --git a/src/cats/grids/gdal_save.c b/src/cats/grids/gdal_save.c index 74ac287e851a17fad69011723ffe61d2802bea15..b6d25342bc7332ef4447fbbb0aa26d920a439d3b 100644 --- a/src/cats/grids/gdal_save.c +++ b/src/cats/grids/gdal_save.c @@ -70,10 +70,9 @@ void *save_population_to_gdal(struct cats_grid *grid, struct cats_configuration (cats_dt_population) get_vital_rate_maximum(&grid->param.carrying_capacity)); fflush(stderr); } - if (conf->summary_file){ - fprintf(conf->summary_file, "%s,%d,%d,population,%s\n",conf->run_name, conf->simulation.replicate, conf->time.year_current, filename); - fflush(conf->summary_file); - } + + if (conf->output.summary_file) fprintf(conf->output.summary_file, "population,%s,%d\n",filename, conf->time.year_current); + free(filename); return 0; @@ -279,7 +278,7 @@ save_grid_to_gdal(struct grid_wrapper *gridwrapper, GDALDataType out_datatype, c set_gdal_dataset_geo_info(dataset, &conf->geometry, &gridwrapper->offset); set_gdal_band_nodata(band, nodata_value); set_gdal_band_metadata(band, GCI_GrayIndex, description); - set_gdal_dataset_metadata(dataset, "configuration", conf->file_content); + set_gdal_dataset_metadata(dataset, "configuration", conf->output.file_content); band = GDALGetRasterBand(dataset, 1); diff --git a/src/cats/hybrid/scalefactor.c b/src/cats/hybrid/scalefactor.c index 63ded6208db510824ba8c799a9712c53cd07280a..003a61bd0b8d6e7b101e58184a188cab6ba7d41f 100644 --- a/src/cats/hybrid/scalefactor.c +++ b/src/cats/hybrid/scalefactor.c @@ -42,34 +42,47 @@ #include "lambda/leslie_matrix.h" #include "lambda/lambda.h" +struct cats_module *get_demographic_module_for_matrix(struct cats_configuration *conf, int32_t species_id) +{ + struct cats_module *module = NULL; + struct cats_species_param *param = &conf->param[species_id]; + if (param->demographic_module == NULL) return module; + + int32_t module_id = param->demographic_module->id; + + if (conf->modules.module[module_id].flags & MODULE_ALTERNATE_DEMOGRAPHIC) return &conf->modules.module[module_id]; + + return NULL; +} + + +bool have_leslie_matrix_function_for_module(struct cats_module *module) +{ + if (module == NULL) return true; + cats_leslie_matrix_func func = module->create_leslie_matrix; + return func != NULL; +} cats_dt_rates calculate_scale_factor_from_lambda(struct cats_configuration *conf, int32_t species_id) { assert(species_id < conf->param_count); struct cats_species_param *param = &conf->param[species_id]; - bool ok = false; - cats_leslie_matrix_func func = NULL; - - - // FIXME LOOKS LIKE A MESS - if (param->custom_vital_rates == false || param->custom_vital_ages == false) ok = true; - if (param->custom_vital_rates || param->custom_vital_ages) { - if (param->demographic_module != NULL) { - int32_t id = param->demographic_module->id; - if (conf->modules.module[id].flags & MODULE_ALTERNATE_DEMOGRAPHIC) { - func = conf->modules.module[id].create_leslie_matrix; - if (func != NULL) { - ok = true; - } - } - } - } - if (!ok) { + + struct cats_module *module = get_demographic_module_for_matrix(conf, species_id); + bool have_func = have_leslie_matrix_function_for_module(module); + log_message(LOG_IMPORTANT, "Demographic module %p", module); + + if (module != NULL && have_func == false) { + log_message(LOG_WARNING, "unable to calculate scale factor for %s: custom vital rates or vital ages", param->species_name); log_message(LOG_WARNING, "Using scale factor 0.5 for %s", param->species_name); return 0.5; + + } else if (module != NULL) { + log_message(LOG_IMPORTANT, "Grid %d uses module %s for demography: %p", species_id, + module->canonical_name, module->create_leslie_matrix); } @@ -104,9 +117,11 @@ cats_dt_rates calculate_scale_factor_from_lambda(struct cats_configuration *conf conf->param[species_id].scale_factor = s; conf->direct_scale_factor = s; cats_dt_rates old_s = s; - lambda = calculate_lambda(conf, &l_param, true, func); - log_message(LOG_INFO, "iteration %d: scale factor %0.25f resulted in lambda %0.25f", iterations, - (double) s, (double) lambda); + + lambda = calculate_lambda(conf, &l_param, true, module); + log_message(LOG_INFO, "iteration %d: scale factor %0.25Lf resulted in lambda %0.25Lf", iterations, s, + lambda); + if (fabsl(lambda - 1.0) < threshold) { conf->lambda_at_OT = lambda; @@ -168,7 +183,7 @@ cats_dt_rates calculate_scale_factor_from_lambda(struct cats_configuration *conf iterations); conf->param[species_id].scale_factor = s; conf->direct_scale_factor = s; - calculate_lambda(conf, &l_param, false, func); + calculate_lambda(conf, &l_param, false, module); return s; @@ -177,7 +192,9 @@ cats_dt_rates calculate_scale_factor_from_lambda(struct cats_configuration *conf void set_scale_factor_all_species(struct cats_configuration *conf, cats_dt_rates scale_factor) { + log_message(LOG_INFO, "setting scale factor for all grids directly to %f", (double) scale_factor); + for (int id = 0; id < conf->grid_count; id++) { conf->param[id].scale_factor = scale_factor; } @@ -207,7 +224,10 @@ void setup_scale_factor(struct cats_configuration *conf, struct program_options if (options->direct_scale_factor > 0 && options->direct_scale_factor < 1) { conf->direct_scale_factor = options->direct_scale_factor; + log_message(LOG_IMPORTANT, "using pre-calculated scale factor %f", (double) conf->direct_scale_factor); + + set_scale_factor_all_species(conf, conf->direct_scale_factor); return; } diff --git a/src/cats/inline_overlays.h b/src/cats/inline_overlays.h index eb519de82e3ac2def8e549fd9bcc679c79b076c2..519689e39cf39c53b519c1de0e5529f6948fc49e 100644 --- a/src/cats/inline_overlays.h +++ b/src/cats/inline_overlays.h @@ -34,7 +34,7 @@ static inline bool cell_excluded_by_habitat(const struct cats_configuration *config, cats_dt_coord row, cats_dt_coord col) { if (!config->overlays.have_overlays) return false; - if (!config->overlays.enabled[OL_HABITAT_TYPE_CC]) return false; + if (!config->overlays.overlay[OL_HABITAT_TYPE_CC].enabled) return false; return (config->overlays.habitat_cc->data[row][col] == 0.0); } @@ -45,7 +45,7 @@ cell_excluded_by_overlay(const struct cats_configuration *config, cats_dt_coord { assert(config != NULL); - if (config->overlays.have_overlays == false || config->overlays.enabled[OL_EXCLUSION] == false) return false; + if (config->overlays.have_overlays == false || config->overlays.overlay[OL_EXCLUSION].enabled == false) return false; assert(config->overlays.exclusion->data != NULL); diff --git a/src/cats/lambda/lambda-grid.c b/src/cats/lambda/lambda-grid.c index 4573989fc3e8725345a1fc8960419c4e2bf8cb52..501cc0f6298d62c54685e63c1265fda18c7de0f1 100644 --- a/src/cats/lambda/lambda-grid.c +++ b/src/cats/lambda/lambda-grid.c @@ -78,13 +78,15 @@ lookup_lambda(struct cats_configuration *conf, struct cats_grid *grid, cats_dt_p l_param.suitability = NAN; break; } + /* cats_leslie_matrix_func func = NULL; if (grid->param.demographic_module && grid->param.demographic_module->create_leslie_matrix) { func = grid->param.demographic_module->create_leslie_matrix; } + */ // we have density dependence if (N >= 0 || grid->param.parametrization != PARAM_HYBRID) { - return calculate_lambda(conf, &l_param, true, func); + return calculate_lambda(conf, &l_param, true, NULL); } assert(grid->param.parametrization == PARAM_HYBRID); @@ -100,7 +102,7 @@ lookup_lambda(struct cats_configuration *conf, struct cats_grid *grid, cats_dt_p return param->lambda_cache[index]; } else { l_param.N = 0; - cats_dt_rates lambda = calculate_lambda(conf, &l_param, true, func); + cats_dt_rates lambda = calculate_lambda(conf, &l_param, true, NULL); param->lambda_cache[index] = lambda; return lambda; } diff --git a/src/cats/lambda/lambda.c b/src/cats/lambda/lambda.c index 4df3836972545bda35aa98736f08fe1198b0ce5f..cbd08c651b552de9fba6dea1b87fbe379c1ffb82 100644 --- a/src/cats/lambda/lambda.c +++ b/src/cats/lambda/lambda.c @@ -39,18 +39,29 @@ void debug_lambda_problem(struct cats_configuration *conf, gsl_complex largest_e cats_dt_rates calculate_lambda(struct cats_configuration *conf, struct lambda_parameters *l_param, bool silent, - cats_leslie_matrix_func leslie_matrix_func) + struct cats_module *module) { - int32_t N = 0; - bool debug = conf->command_line_options.debug_flags & DEBUG_LAMBDA; - const int32_t species_id = l_param->species_id; + double *matrix = NULL; - if (leslie_matrix_func == NULL) { + int32_t N = 0; + + if (module == NULL) { matrix = create_leslie_matrix(conf, l_param, silent, &N); + } else if (module->create_leslie_matrix != NULL) { + matrix = module->create_leslie_matrix(conf, l_param, silent, &N); + } else { - matrix = leslie_matrix_func(conf, l_param, silent, &N); + log_message(LOG_ERROR, "no function to create leslie matrix specified"); + exit_cats(EXIT_FAILURE); } + + + bool debug = conf->command_line_options.debug_flags & DEBUG_LAMBDA; + const int32_t species_id = l_param->species_id; + + + // double *matrix_copy = NULL; @@ -76,8 +87,12 @@ cats_dt_rates calculate_lambda(struct cats_configuration *conf, struct lambda_pa print_matrix(matrix_copy, N, N); log_message(LOG_RAW, "Schur form T:\n"); print_matrix(matrix, N, N); - debug_lambda_1_found(conf, species_id, largest_eigen_value, matrix_copy, N, l_param); - log_message(LOG_RAW, "============================\n"); + + if (module == NULL) { + debug_lambda_1_found(conf, species_id, largest_eigen_value, matrix_copy, N, l_param); + log_message(LOG_RAW, "============================\n"); + } + } if (silent == false && l_param->calculate_scale == true) { @@ -85,7 +100,9 @@ cats_dt_rates calculate_lambda(struct cats_configuration *conf, struct lambda_pa if (fabs(GSL_REAL(largest_eigen_value) - 1.0) < LAMBDA_EPS) { log_message(LOG_RAW, "Final matrix\n"); print_matrix(matrix_copy, N, N); - debug_lambda_1_found(conf, species_id, largest_eigen_value, matrix_copy, N, l_param); + if (module == NULL) { + debug_lambda_1_found(conf, species_id, largest_eigen_value, matrix_copy, N, l_param); + } } } diff --git a/src/cats/lambda/lambda.h b/src/cats/lambda/lambda.h index a2e0f2dbcef671a2f82443097bdf865f7edcf013..96ec98d68a0b23d34b2f4289f5aac94c960c4248 100644 --- a/src/cats/lambda/lambda.h +++ b/src/cats/lambda/lambda.h @@ -28,6 +28,6 @@ #include "leslie_matrix.h" cats_dt_rates calculate_lambda(struct cats_configuration *conf, struct lambda_parameters *l_param, bool silent, - cats_leslie_matrix_func leslie_matrix_func); + struct cats_module *module); #endif //CATS_LAMBDA_H diff --git a/src/cats/lambda/leslie_matrix.c b/src/cats/lambda/leslie_matrix.c index 413a266d4fe75e46dff700823f5c9f8a259b0735..7562e3d1304334ce934f62116d30a0eda1ea88ca 100644 --- a/src/cats/lambda/leslie_matrix.c +++ b/src/cats/lambda/leslie_matrix.c @@ -36,6 +36,43 @@ #include "inline_vital_rates.h" +cats_dt_rates +calculate_rate_for_matrix(struct cats_vital_rate *rate, + const struct lambda_parameters *l_param, + bool print) +{ + assert(l_param != NULL); + assert(l_param->param != NULL); + + const struct cats_species_param *param = l_param->param; //&conf->param[grid_id]; + + const cats_dt_rates N = l_param->N; + const cats_dt_rates K = l_param->K; + struct link_override_parameters override = {-1}; + + if (param->parametrization == PARAM_HYBRID) { + override.override_suitability = l_param->suitability; + } + + override.override_population = N; + override.override_carrying_capacity = l_param->K; + + cats_dt_rates value = calculate_rate(rate, N, param, l_param->grid, l_param->row, l_param->col, &override); + + if (print) { + if (param->parametrization == PARAM_HYBRID) { + log_message(LOG_INFO, "%s: %Lf (maximum %Lf) for population %Lf/%Lf at suitability %Lf", + rate->name, value, rate->max_rate, N, K, l_param->suitability); + } else { + log_message(LOG_INFO, "%s: %Lf (maximum %Lf) for population %Lf/%Lf", + rate->name, value, rate->max_rate, N, K); + } + + } + + return value; +} + cats_dt_rates get_rate_for_matrix(enum cats_vital_rate_id rate_type, const struct lambda_parameters *l_param, diff --git a/src/cats/lambda/leslie_matrix.h b/src/cats/lambda/leslie_matrix.h index 25604c6c9887790293b1212d8b639865a289adca..66cb8590d7b262653ded9737e3df8019585dde36 100644 --- a/src/cats/lambda/leslie_matrix.h +++ b/src/cats/lambda/leslie_matrix.h @@ -43,6 +43,10 @@ cats_dt_rates get_rate_for_matrix(enum cats_vital_rate_id rate_type, const struct lambda_parameters *l_param, bool print); +cats_dt_rates +calculate_rate_for_matrix(struct cats_vital_rate *rate, + const struct lambda_parameters *l_param, + bool print); double * create_leslie_matrix(struct cats_configuration *conf, struct lambda_parameters *l_param, bool silent, int32_t *N_out); diff --git a/src/cats/misc/cats_random.c b/src/cats/misc/cats_random.c index 878dbe7a33334c5d6513943e927fa65f5071a63e..238e437a7bd55186f3e424843e6029545753998a 100644 --- a/src/cats/misc/cats_random.c +++ b/src/cats/misc/cats_random.c @@ -64,6 +64,39 @@ unsigned int get_random_seed(bool show) } +uint64_t get_random_seed64(bool show) +{ + uint64_t seed; + struct timeval tv; +#ifdef CATS_ON_WINDOWS + get_time(&tv); + seed = tv.tv_sec + tv.tv_usec; +#else + const char *device = "/dev/urandom"; + FILE *random = fopen(device, "r"); + + if (random == NULL) { + get_time(&tv); + seed = tv.tv_sec + tv.tv_usec; + } else { + size_t read = fread(&seed, sizeof(seed), 1, random); + if (read <= 0) { + log_message(LOG_ERROR, "could not read from %s, using time instead", device); + get_time(&tv); + seed = tv.tv_sec + tv.tv_usec; + } + + fclose(random); + + if (show) log_message(LOG_IMPORTANT, "using seed from %s: %"PRId64, device, seed); + } +#endif + if (show) log_message(LOG_INFO, "random seed value: %"PRId64, seed); + + return seed; +} + + gsl_rng *allocate_rng(void) { gsl_rng *r = gsl_rng_alloc(gsl_rng_mt19937); @@ -103,6 +136,7 @@ int32_t poisson_undampened(const gsl_rng *rng, cats_dt_rates value) if (result > CATS_MAX_POPULATION) { log_message(LOG_WARNING, "poisson called on %f resulted in %f > %d, returning %d", (double) value, (double) result, CATS_MAX_POPULATION, CATS_MAX_POPULATION); + return CATS_MAX_POPULATION; } int32_t final = (int32_t) result; diff --git a/src/cats/misc/cats_random.h b/src/cats/misc/cats_random.h index 2c9da9103ff5a82375505323eecf280b3e88f893..7a2b7827247fa7cd2f897474db13355ba337e0e3 100644 --- a/src/cats/misc/cats_random.h +++ b/src/cats/misc/cats_random.h @@ -43,4 +43,6 @@ cats_dt_seeds poisson_seeds_undampened(const gsl_rng *rng, cats_dt_rates value); cats_dt_seeds poisson_seeds_capped(const gsl_rng *rng, cats_dt_rates value, cats_dt_seeds cap); + +uint64_t get_random_seed64(bool show); #endif //CATS_CATS_RANDOM_H diff --git a/src/cats/modules/load_module.c b/src/cats/modules/load_module.c index ad1f8c20dd602e18cb9dead2bf6f0e59a9994e77..4089dbf6a9c6ec1fd5a6af5fd279e3d6fb56ad55 100644 --- a/src/cats/modules/load_module.c +++ b/src/cats/modules/load_module.c @@ -107,7 +107,7 @@ int32_t register_module(struct cats_configuration *conf, const char *module_name int32_t module_id = conf->modules.count; CATS_MODULE_ID_INTERNAL = module_id; CATS_MODULE_DATA_INTERNAL = module_data; - logging_initialize(conf->command_line_options.default_log_level, &conf->global->time_info, conf->log_file_name, conf->quiet); + logging_initialize(conf->command_line_options.default_log_level, &conf->global->time_info, conf->output.log_file_name, conf->quiet); logging_set_module_name(module_name); conf->modules.module[module_id].flags = module_flags; @@ -148,8 +148,8 @@ void register_load_species_param_config_func(struct cats_configuration *conf, ca void register_create_leslie_matrix_func(struct cats_configuration *conf, cats_leslie_matrix_func func) { - conf->param[0].module_data[CATS_MODULE_ID].create_leslie_matrix_function = func; conf->modules.module[CATS_MODULE_ID].create_leslie_matrix = func; + log_message(LOG_INFO, "Using leslie matrix function at address %p", conf->modules.module[CATS_MODULE_ID].create_leslie_matrix); } @@ -167,6 +167,12 @@ void register_module_vital_rate(struct cats_configuration *conf, struct cats_vit vr->is_carrying_capacity = false; strncpy(vr->name, name, MAX_VITAL_NAME_LEN + 1); + vr->default_rate_id = m->vital_rate_count; m->vital_rate_count++; +} + +void add_module_output_directory(struct cats_configuration *conf, const char *name) +{ + string_array_add(conf->output.needed_module_output_directories, name); } \ No newline at end of file diff --git a/src/cats/modules/load_module.h b/src/cats/modules/load_module.h index 38dd81e811b7c6f8ff546c4e89c1e70beda751d6..bd87c6049a1eda9d523de4d79598388e476f55c2 100644 --- a/src/cats/modules/load_module.h +++ b/src/cats/modules/load_module.h @@ -50,4 +50,6 @@ void register_load_species_param_config_func(struct cats_configuration *conf, ca void print_modules(struct cats_configuration *conf); +void add_module_output_directory(struct cats_configuration *conf, const char *name); + #endif //CATS_LOAD_MODULE_H diff --git a/src/cats/modules/modules.c b/src/cats/modules/modules.c index 68dc7f18cf7dbd64d42f19be4a0f416fa3e9cd55..a1949e5c9dfb37eae8bbabc03d76f2b0a4c72300 100644 --- a/src/cats/modules/modules.c +++ b/src/cats/modules/modules.c @@ -28,7 +28,6 @@ #include "modules.h" #include "cats_strings/cats_strings.h" - void init_cats_module(struct cats_module *module, int32_t id) { module->name = NULL; @@ -38,6 +37,8 @@ void init_cats_module(struct cats_module *module, int32_t id) module->id = id; module->create_leslie_matrix = NULL; module->stats_header = NULL; + + } @@ -59,7 +60,6 @@ void init_module_species_data(struct module_species_data *m) m->grid_init_function = NULL; m->grid_cleanup_function = NULL; m->load_species_param_function = NULL; - m->create_leslie_matrix_function = NULL; m->grid_stat_function = NULL; m->module = NULL; m->vital_rate_count = 0; diff --git a/src/cats/modules/modules.h b/src/cats/modules/modules.h index d24d85b36bc14f8282674d73bc8f3a40ddf53b7b..cdae0e9b520950adb93a952318f84d37a1cc632a 100644 --- a/src/cats/modules/modules.h +++ b/src/cats/modules/modules.h @@ -23,12 +23,11 @@ #ifndef CATS_MODULES_H #define CATS_MODULES_H - #include <stdint.h> #include <stdbool.h> #include "defaults.h" #include "data/cats_datatypes.h" - +#include <gsl/gsl_eigen.h> struct cats_ini; struct cats_configuration; struct lambda_parameters; @@ -53,13 +52,14 @@ typedef struct string_array *(*cats_grid_stat_function)(struct cats_configuratio typedef void (*cats_cell_function)(struct cats_configuration *conf, struct cats_grid *grid, cats_dt_coord row, cats_dt_coord col, cats_dt_population K); +typedef void (*eigen_system_print_func)(gsl_complex eigen_value, gsl_vector_complex_view *eigen_vector, int32_t N, struct cats_species_param *param); + struct module_species_data { struct cats_module *module; cats_grid_function grid_init_function; cats_grid_function grid_cleanup_function; cats_load_species_param_function load_species_param_function; - cats_leslie_matrix_func create_leslie_matrix_function; cats_grid_stat_function grid_stat_function; cats_cell_function cell_destroyed_action; cats_cell_function cell_carrying_capacity_action; @@ -68,7 +68,6 @@ struct module_species_data { char **vital_rate_name; bool *vital_rate_required; int32_t vital_rate_count; - void *module_data; }; @@ -79,13 +78,16 @@ enum cats_module_flags { }; + + struct cats_module { char *name; char *canonical_name; char *filename; void *data; enum cats_module_flags flags; - void *create_leslie_matrix; + cats_leslie_matrix_func create_leslie_matrix; + int32_t id; struct string_array *stats_header; }; diff --git a/src/cats/overlays/overlay_exclusion.c b/src/cats/overlays/overlay_exclusion.c index 559b40ee97be7b3e21cd46e07574421e304b6007..f68b8691b9e9215d4b0b9552f13962cffe458b2b 100644 --- a/src/cats/overlays/overlay_exclusion.c +++ b/src/cats/overlays/overlay_exclusion.c @@ -39,8 +39,8 @@ int64_t get_non_excluded_cell_count(struct cats_configuration *conf, struct cats_grid *grid) { int64_t all_count = raster_cell_count(grid->dimension); - if (conf->overlays.have_overlays && conf->overlays.enabled[OL_EXCLUSION]) { - all_count -= conf->overlays.excluded_cells; + if (conf->overlays.have_overlays && conf->overlays.overlay[OL_EXCLUSION].enabled) { + all_count -= conf->overlays.overlay[OL_EXCLUSION].excluded_cells; } return all_count; @@ -58,24 +58,30 @@ void destroy_excluded_cells(const struct cats_configuration *conf, struct cats_g for (cats_dt_coord col = 0; col < cols; col++) { if (cell_excluded_by_overlay(conf, row, col) == false) continue; - destroyed_cell_count += 1; + cats_dt_population old_population = get_adult_population(grid, row, col); if (grid->param.default_demographics) { + set_population_ignore_cc(grid, row, col, 0); // destroy_excluded_cells destroy_plant_cell(grid, row, col); + if (old_population) destroyed_cell_count += 1; } else { + bool destroyed = false; for (int32_t i = 0; i < conf->modules.count; i++) { cats_cell_function destroy = grid->param.module_data[i].cell_destroyed_action; if (destroy != NULL) { destroy(grid->conf, grid, row, col, 0); + destroyed = true; } } + if (old_population && destroyed) destroyed_cell_count += 1; } } } - log_message(LOG_INFO, "grid %d (%s): %"PRIi64" cells pruned", grid->id, grid->param.species_name, + + log_message(LOG_INFO, "Removed excluded cell for grid %d (%s): %ld cells removed", grid->id, grid->param.species_name, destroyed_cell_count); } @@ -84,8 +90,6 @@ void destroy_excluded_cells_all_grids(const struct cats_configuration *conf, str { if (grid->id != 0) return; - log_message(LOG_INFO, "Purging all excluded cells"); - const int32_t grid_count = conf->grid_count; struct cats_grid **parent = grid->parent; diff --git a/src/cats/overlays/overlay_habitat_type_cc.c b/src/cats/overlays/overlay_habitat_type_cc.c index a349ef002bad33555acbf02a026d516ae165d72b..e98c258f98d2059292ecac0f7667e78c9d4697c2 100644 --- a/src/cats/overlays/overlay_habitat_type_cc.c +++ b/src/cats/overlays/overlay_habitat_type_cc.c @@ -107,8 +107,9 @@ double get_habitat_cc_multiplier(const struct habitat_layer_cc_aux *aux, int32_t aux->max_code); exit(EXIT_FAILURE); } - - return aux->habitat_multipliers[habitat_type]; + double result = aux->habitat_multipliers[habitat_type]; + assert(result >= 0.0); + return result; } diff --git a/src/cats/overlays/overlay_resources.c b/src/cats/overlays/overlay_resources.c new file mode 100644 index 0000000000000000000000000000000000000000..64a171f856e369a527a9a5d3a804722574f8f4d9 --- /dev/null +++ b/src/cats/overlays/overlay_resources.c @@ -0,0 +1,30 @@ +#include <assert.h> +#include <math.h> +#include "overlay_resources.h" +#include "memory/arrays.h" + + +struct cats_2d_array_double *translate_resources(const struct cats_2d_array_double *data) +{ + struct cats_2d_array_double *result = new_2d_array_double(data->dimension); + + const cats_dt_coord rows = data->dimension.rows; + const cats_dt_coord cols = data->dimension.cols; + + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { + + double value = data->data[row][col]; + + if (isnan(value)) { + result->data[row][col] = 0.0; + } else { + + if (value <0 ) value = 0; + result->data[row][col] = value; + } + } + } + + return result; +} diff --git a/src/cats/overlays/overlay_resources.h b/src/cats/overlays/overlay_resources.h new file mode 100644 index 0000000000000000000000000000000000000000..012b5acdd11a3310cad339e5a391010d71be8a93 --- /dev/null +++ b/src/cats/overlays/overlay_resources.h @@ -0,0 +1,8 @@ +// +// Created by andreas on 08/02/23. +// + +#ifndef CATS_OVERLAY_RESOURCES_H +#define CATS_OVERLAY_RESOURCES_H +struct cats_2d_array_double *translate_resources(const struct cats_2d_array_double *data); +#endif //CATS_OVERLAY_RESOURCES_H diff --git a/src/cats/overlays/overlays.c b/src/cats/overlays/overlays.c index c20c097519449721d90e2716eabe0de5bbc77fef..3d7c17b7e334992a800d3687f23df0d7d7518fe7 100644 --- a/src/cats/overlays/overlays.c +++ b/src/cats/overlays/overlays.c @@ -33,7 +33,8 @@ #include "grids/cats_grid.h" #include "overlay_habitat_type_cc.h" #include "overlay_exclusion.h" - +#include "overlay_resources.h" +#include "paths/path_patterns.h" const char *get_overlay_name(enum overlay_type type) { @@ -42,10 +43,12 @@ const char *get_overlay_name(enum overlay_type type) return "exclusion"; case OL_HABITAT_TYPE_CC: return "habitat_type_cc"; - + case OL_RESOURCE: + return "resources"; + break; case OL_NONE: case OL_MAX: - default: + case OL_CUSTOM: break; } @@ -57,30 +60,47 @@ void cleanup_overlays(struct cats_overlays *overlays) { if (overlays->exclusion) free_cats_grid(&overlays->exclusion); if (overlays->habitat_cc) free_cats_grid(&overlays->habitat_cc); - if (overlays->aux_data[OL_HABITAT_TYPE_CC]) { - cleanup_habitat_layer_cc_aux(&overlays->aux_data[OL_HABITAT_TYPE_CC]); + if (overlays->overlay[OL_HABITAT_TYPE_CC].aux_data) { + cleanup_habitat_layer_cc_aux(&overlays->overlay[OL_HABITAT_TYPE_CC].aux_data); } for (int32_t i = 0; i < OL_MAX; i++) { - free(overlays->filename[i]); - overlays->filename[i] = NULL; - free(overlays->filename_aux[i]); - overlays->filename_aux[i] = NULL; - free(overlays->filename_loaded[i]); - overlays->filename_loaded[i] = NULL; - free(overlays->name[i]); - overlays->name[i] = NULL; + free(overlays->overlay[i].filename); + overlays->overlay[i].filename = NULL; + free(overlays->overlay[i].filename_aux); + overlays->overlay[i].filename_aux = NULL; + free(overlays->overlay[i].filename_loaded); + overlays->overlay[i].filename_loaded = NULL; + free(overlays->overlay[i].name); + overlays->overlay[i].name = NULL; } } +struct cats_overlay *add_overlay(struct cats_configuration *conf, const char *name) +{ + struct cats_overlay *overlay = NULL; + abort_implemented(__func__); + return overlay; + +} + -enum overlay_type get_overlay_type_from_name(const char *name) +enum overlay_type get_overlay_type_from_name(const char *name, const struct string_array *registered_names) { if (!name) return OL_NONE; if (!strcmp(name, "exclusion")) return OL_EXCLUSION; if (!strcmp(name, "habitat carrying capacity")) return OL_HABITAT_TYPE_CC; + if (!strcmp(name, "resources")) return OL_RESOURCE; + + if (registered_names != NULL) { + for (int32_t i = 0; i < registered_names->count; i++) { + if (!strcmp(name, registered_names->string[i])) { + return OL_CUSTOM; + } + } + } log_message(LOG_ERROR, "%s: unknown overlay type '%s'", __func__, name); exit(EXIT_FAILURE); @@ -94,17 +114,19 @@ void reset_overlay(struct cats_overlays *overlay) overlay->exclusion = NULL; overlay->cols = 0; overlay->rows = 0; - overlay->excluded_cells = 0; + for (int32_t i = 0; i < OL_MAX; i++) { - overlay->enabled[i] = false; - overlay->is_static[i] = true; - overlay->reload_interval[i] = 0; - overlay->filename[i] = NULL; - overlay->filename_aux[i] = NULL; - overlay->filename_loaded[i] = NULL; - overlay->aux_data[i] = NULL; - overlay->name[i] = NULL; + overlay->overlay[i].enabled = false; + overlay->overlay[i].excluded_cells = 0; + + overlay->overlay[i].is_static = true; + overlay->overlay[i].reload_interval = 0; + overlay->overlay[i].filename = NULL; + overlay->overlay[i].filename_aux = NULL; + overlay->overlay[i].filename_loaded = NULL; + overlay->overlay[i].aux_data = NULL; + overlay->overlay[i].name = NULL; } } @@ -117,27 +139,37 @@ void load_overlay_from_file(struct cats_configuration *conf, enum overlay_type t log_message(LOG_ERROR, "overlay type %d out of range", type); } log_message(LOG_INFO, "gdal: loading grid mask %s", filename); - struct cats_2d_array_double *raw_values = get_double_values_from_gdal(&conf->geometry, filename, false, false); + char *filename_substituted = filename_pattern_substitution(filename, conf, conf->time.year_current); + + struct cats_2d_array_double *raw_values = get_double_values_from_gdal(&conf->geometry, filename_substituted, false, false); check_raster_dimensions(raw_values->dimension, conf->geometry.dimension); - if (conf->overlays.filename_loaded[type]) free(conf->overlays.filename_loaded[type]); - conf->overlays.filename_loaded[type] = strdup(filename); + if (conf->overlays.overlay[type].filename_loaded) free(conf->overlays.overlay[type].filename_loaded); + conf->overlays.overlay[type].filename_loaded = strdup(filename); switch (type) { case OL_EXCLUSION: if (conf->overlays.exclusion) free_cats_grid(&conf->overlays.exclusion); - conf->overlays.exclusion = translate_exclusion(raw_values, &conf->overlays.excluded_cells); + conf->overlays.exclusion = translate_exclusion(raw_values, &conf->overlays.overlay[type].excluded_cells); break; case OL_HABITAT_TYPE_CC: if (conf->overlays.habitat_cc) free_cats_grid(&conf->overlays.habitat_cc); - conf->overlays.habitat_cc = translate_habitat(raw_values, conf->overlays.aux_data[type]); + conf->overlays.habitat_cc = translate_habitat(raw_values, conf->overlays.overlay[type].aux_data); + break; + case OL_CUSTOM: + log_message(LOG_ERROR, "OL_CUSTOM not implemented yet"); + exit(EXIT_FAILURE); + case OL_RESOURCE: + if (conf->overlays.resources) free_cats_grid(&conf->overlays.resources); + conf->overlays.resources = translate_resources(raw_values); break; case OL_NONE: case OL_MAX: break; - } + } + free(filename_substituted); free_cats_grid(&raw_values); } diff --git a/src/cats/overlays/overlays.h b/src/cats/overlays/overlays.h index 9b504e6f451c29de08d7a4be08444ecd74260bf0..cc1f4a8fffa366c36dde9395bb9a2cbf94da9ed9 100644 --- a/src/cats/overlays/overlays.h +++ b/src/cats/overlays/overlays.h @@ -36,6 +36,8 @@ enum overlay_type { OL_NONE = 0, OL_EXCLUSION = 1, OL_HABITAT_TYPE_CC = 2, + OL_RESOURCE = 3, + OL_CUSTOM = 4, OL_MAX }; @@ -46,6 +48,24 @@ enum ol_exclusion_type { OL_EXCLUSION_NAN = 2, }; +struct cats_overlay { + bool enabled; ///< which of the overlay types are enabled? + bool is_static; ///< do the overlays remain constant over time? + int32_t reload_interval; ///< how often do overlays change? + int64_t excluded_cells; + char *name; ///< overlay names (as specified in the configuration file) + char *filename; ///< overlay filenames or filename patterns + char *filename_aux; + char *filename_loaded; + void *aux_data; + + struct cats_2d_array_char *data_char; + struct cats_2d_array_double *data_double; + + cats_dt_coord cols; ///< number of rows (same as grid, for convenience only) + cats_dt_coord rows; ///< number of columns (same as grid, for convenience only) + +}; /** @brief Contains information for all overlays @@ -54,19 +74,16 @@ enum ol_exclusion_type { struct cats_overlays { bool have_overlays; ///< do we actually have overlays? True if at least one overlay is enabled. - bool enabled[OL_MAX]; ///< which of the overlay types are enabled? - bool is_static[OL_MAX]; ///< do the overlays remain constant over time? - int32_t reload_interval[OL_MAX]; ///< how often do overlays change? - int64_t excluded_cells; - char *name[OL_MAX]; ///< overlay names (as specified in the configuration file) - char *filename[OL_MAX]; ///< overlay filenames or filename patterns - char *filename_aux[OL_MAX]; - char *filename_loaded[OL_MAX]; - void *aux_data[OL_MAX]; - + struct cats_overlay overlay[OL_MAX]; struct cats_2d_array_char *exclusion; struct cats_2d_array_double *habitat_cc; + struct cats_2d_array_double *resources; + + struct cast_overlay *custom_overlays; + int32_t custom_overlay_count; + + struct string_array *registered_custom_overlay_names; cats_dt_coord cols; ///< number of rows (same as grid, for convenience only) cats_dt_coord rows; ///< number of columns (same as grid, for convenience only) @@ -77,7 +94,7 @@ void cleanup_overlays(struct cats_overlays *overlays); const char *get_overlay_name(enum overlay_type type); -enum overlay_type get_overlay_type_from_name(const char *name); +enum overlay_type get_overlay_type_from_name(const char *name, const struct string_array *registered_names); bool @@ -86,5 +103,5 @@ update_overlay_if_needed(struct cats_configuration *conf, int32_t time, enum ove void reset_overlay(struct cats_overlays *overlay); void load_overlay_from_file(struct cats_configuration *conf, enum overlay_type type, char *filename); - +struct cats_overlay *add_overlay(struct cats_configuration *conf, const char *name); #endif \ No newline at end of file diff --git a/src/cats/paths/directory_helper.c b/src/cats/paths/directory_helper.c index 327518fa867c1ed86dc7f87a4125df41e125321f..11494f0d642625f0ccdaef0bb6580953fd6f5b95 100644 --- a/src/cats/paths/directory_helper.c +++ b/src/cats/paths/directory_helper.c @@ -59,8 +59,14 @@ struct string_array *get_needed_directories(const struct cats_configuration *con struct string_array *array = new_string_array(); string_array_add(array, "adults"); string_array_add(array, "stats"); - string_array_add(array, "environments"); - if (conf->write_all) { + + + if (conf->output.write_all && conf->output.have_glm) { + string_array_add(array, "environments"); + } + + + if (conf->output.write_all) { string_array_add(array, "seeds"); string_array_add(array, "juveniles"); } @@ -87,29 +93,36 @@ void ensure_needed_directories_exist(const struct cats_configuration *conf, cons log_message(LOG_INFO, "%s: Checking output directories", __func__); - - - - struct string_array *output = new_string_array_init(conf->output_directory); + struct string_array *output = new_string_array_init(conf->output.output_directory); check_and_create_directory_if_needed(output); free_string_array(&output); for (int32_t i = 0; i < output_dirs->count; i++) { struct string_array *path = get_output_directory(conf, output_dirs->string[i]); check_and_create_directory_if_needed(path); free_string_array(&path); + + } + + free_string_array(&output); } +void setup_module_directories(struct cats_configuration *conf) +{ + struct string_array *dirs_to_check = conf->output.needed_module_output_directories; + ensure_needed_directories_exist(conf, dirs_to_check); +} -void setup_directories(struct cats_configuration *conf) -{ if (conf->command_line_options.output_directory) { - log_message(LOG_IMPORTANT, "overriding output directory from '%s' to '%s'", conf->output_directory, +void setup_directories(struct cats_configuration *conf) +{ + if (conf->command_line_options.output_directory) { + log_message(LOG_IMPORTANT, "overriding output directory from '%s' to '%s'", conf->output.output_directory, conf->command_line_options.output_directory); - free(conf->output_directory); - conf->output_directory = strdup(conf->command_line_options.output_directory); + free(conf->output.output_directory); + conf->output.output_directory = strdup(conf->command_line_options.output_directory); } else { - log_message(LOG_IMPORTANT, "output directory is '%s'", conf->output_directory); + log_message(LOG_IMPORTANT, "output directory is '%s'", conf->output.output_directory); } struct string_array *dirs_to_check = get_needed_directories(conf); diff --git a/src/cats/paths/directory_helper.h b/src/cats/paths/directory_helper.h index 1593ce568803787e080b9b04cf0e917ad6f5493a..6632bc0ea8f99db0c0523be2b475587c6c57d0bf 100644 --- a/src/cats/paths/directory_helper.h +++ b/src/cats/paths/directory_helper.h @@ -35,5 +35,6 @@ struct string_array *get_needed_directories(const struct cats_configuration *con bool check_and_create_directory_if_needed(const struct string_array *path); +void setup_module_directories(struct cats_configuration *conf); #endif //CATS_DIRECTORY_HELPER_H diff --git a/src/cats/paths/output_paths.c b/src/cats/paths/output_paths.c index 0725cfece66698296ec98c52e31d6efd412d3a51..0bf139b941e1faa696768168a8dffd70a84c8490 100644 --- a/src/cats/paths/output_paths.c +++ b/src/cats/paths/output_paths.c @@ -36,8 +36,8 @@ const char *get_base_output_directory(const struct cats_configuration *conf) { - if (conf->output_directory && strlen(conf->output_directory)) { - return conf->output_directory; + if (conf->output.output_directory && strlen(conf->output.output_directory)) { + return conf->output.output_directory; } return DEFAULT_OUTPUT_DIRECTORY; diff --git a/src/cats/paths/path_patterns.c b/src/cats/paths/path_patterns.c new file mode 100644 index 0000000000000000000000000000000000000000..954ac61c22389c9783d0b63030c77a522945c306 --- /dev/null +++ b/src/cats/paths/path_patterns.c @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * + * action_overlay_update.c + * + * Copyright (C) 2011-2022, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "path_patterns.h" +#include "temporal/timeformat.h" + + +char *filename_pattern_substitution(const char *pattern, const struct cats_configuration *conf, int32_t year) +{ + char *time_string = formatted_year(&conf->time, year); + char *year_pattern = replace_substring(pattern, "%Y", time_string); + char *replicate_string = NULL; + int rc = asprintf(&replicate_string, "%d", conf->simulation.replicate); + asprintf_check(rc); + char *replicate_pattern = replace_substring(year_pattern, "%n", replicate_string); + free(year_pattern); + free(time_string); + free(replicate_string); + return replicate_pattern; +} \ No newline at end of file diff --git a/src/cats/paths/path_patterns.h b/src/cats/paths/path_patterns.h new file mode 100644 index 0000000000000000000000000000000000000000..1c81313ac67d0738a2901461329f380686ec59b0 --- /dev/null +++ b/src/cats/paths/path_patterns.h @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * + * action_overlay_update.c + * + * Copyright (C) 2011-2022, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CATS_PATH_PATTERNS_H +#define CATS_PATH_PATTERNS_H +#include "configuration/configuration.h" + +char *filename_pattern_substitution(const char *pattern, const struct cats_configuration *conf, int32_t year); + +#endif //CATS_PATH_PATTERNS_H diff --git a/src/cats/paths/paths_suitability.c b/src/cats/paths/paths_suitability.c index d76cb3a901d3ff6f9baa8140e291b5f165bb92c3..628823af2c8a55ddb3b6c27488163ca267cec7ed 100644 --- a/src/cats/paths/paths_suitability.c +++ b/src/cats/paths/paths_suitability.c @@ -27,6 +27,7 @@ #include "environment/environment.h" #include "temporal/timeformat.h" #include "data/species_parameters.h" +#include "path_patterns.h" char * @@ -38,11 +39,7 @@ get_environment_filename(const struct cats_configuration *conf, const struct cat struct string_array *name = new_string_array(); - char *pattern = NULL; - char *time_string = formatted_year(&conf->time, year); - pattern = replace_substring(environment->pattern, "%Y", time_string); - - free(time_string); + char *pattern = filename_pattern_substitution(environment->pattern, conf, year); string_array_add(name, pattern); free(pattern); diff --git a/src/cats/performance/performance_stats.c b/src/cats/performance/performance_stats.c index 98155d10bb7e07194f3273a5bb771d7a800d25c7..9d9355a6b712466e5e4b1fb4c64d3aab0994e577 100644 --- a/src/cats/performance/performance_stats.c +++ b/src/cats/performance/performance_stats.c @@ -27,6 +27,6 @@ void add_performance_info(const struct cats_configuration *conf, int year, const char *name, double time_cpu, double time_wall) { - if (!conf->performance_file) return; - fprintf(conf->performance_file, "%d,%s,%f,%f\n", year, name, time_cpu, time_wall); + if (!conf->output.performance_file) return; + fprintf(conf->output.performance_file, "%d,%s,%f,%f\n", year, name, time_cpu, time_wall); } \ No newline at end of file diff --git a/src/cats/populations/carrying_capacity.c b/src/cats/populations/carrying_capacity.c index f7980ed84abe6516b34a9b9da67b50e80f186f4e..a285ee516d61783de0b9c63054980ef7692b0f29 100644 --- a/src/cats/populations/carrying_capacity.c +++ b/src/cats/populations/carrying_capacity.c @@ -59,7 +59,7 @@ get_carrying_capacity(const struct cats_grid *grid, cats_dt_coord row, cats_dt_c double multiplier = 1.0; - if (conf->overlays.enabled[OL_HABITAT_TYPE_CC]) { + if (conf->overlays.overlay[OL_HABITAT_TYPE_CC].enabled) { multiplier *= conf->overlays.habitat_cc->data[row][col]; if (multiplier <= 0.0) { return 0; } } diff --git a/src/cats/stats/global_stats.c b/src/cats/stats/global_stats.c index 2a813746a4bffeab9455e238022f12d8aa2c31d7..8c818d68ec895e0289478b7e0ab9b9adb330da4f 100644 --- a/src/cats/stats/global_stats.c +++ b/src/cats/stats/global_stats.c @@ -47,17 +47,15 @@ void initialize_global_stats(struct cats_configuration *conf) char *filename = get_global_stat_filename(conf); remove(filename); - conf->statsfile_global = fopen(filename, "a+"); - ENSURE_FILE_OPENED(conf->statsfile_global, filename) - if (conf->summary_file) { - fprintf(conf->summary_file, "%s,%d,NA,global-stats,%s\n",conf->run_name, conf->simulation.replicate,filename); - fflush(conf->summary_file); - } + + conf->output.statsfile_global = fopen(filename, "a+"); + ENSURE_FILE_OPENED(conf->output.statsfile_global, filename) + if (conf->output.summary_file) fprintf(conf->output.summary_file, "global-stats,%s,\n",filename); write_global_stats(conf, NULL, true); - fflush(conf->statsfile_global); + fflush(conf->output.statsfile_global); free(filename); } @@ -112,8 +110,8 @@ void write_global_stats(struct cats_configuration *conf, struct cats_grid *grid, } char *string = string_array_paste(x, ","); - fprintf(conf->statsfile_global, "%s", string); - fflush(conf->statsfile_global); + fprintf(conf->output.statsfile_global, "%s", string); + fflush(conf->output.statsfile_global); } diff --git a/src/cats/stats/grid_stats.c b/src/cats/stats/grid_stats.c index f2a290012172a6b4c5c5cdddbf0857654e174773..d018b5e83c5a32c18e97a1d43909e281c0da6dac 100644 --- a/src/cats/stats/grid_stats.c +++ b/src/cats/stats/grid_stats.c @@ -53,13 +53,13 @@ struct string_array *add_stats_header_overlay(struct string_array *x, struct cat for (enum overlay_type type = OL_NONE; type < OL_MAX; type++) { - if (conf->overlays.enabled[type]) { + if (conf->overlays.overlay[type].enabled) { if (header) { - char *overlay_name = compound_string("overlay", conf->overlays.name[type], "::"); + char *overlay_name = compound_string("overlay", conf->overlays.overlay[type].name, "::"); string_array_add(x, overlay_name); free(overlay_name); } else { - string_array_add(x, conf->overlays.filename_loaded[type]); + string_array_add(x, conf->overlays.overlay[type].filename_loaded); } } } @@ -143,6 +143,12 @@ void consolidate_thread_stats(struct cats_grid *grid, struct cats_configuration for (int32_t stat_idx = STAT_MIN; stat_idx < STAT_MAX; stat_idx++) { g->stats.stats[stat_idx] += threads[i].stats[class].stats[stat_idx]; } + + if (g->stats.custom_stats) { + for (int64_t j = 0; j < threads[i].stats->custom_stat_count; j++){ + g->stats.custom_stats[j] += threads[i].stats[class].custom_stats[j]; + } + } } } } diff --git a/src/cats/stats/statistics.c b/src/cats/stats/statistics.c index e0c63deafe896b2fd7a5af4446a7629654b2e2dd..d853989677488f82f2422a2950db187cf60d9b28 100644 --- a/src/cats/stats/statistics.c +++ b/src/cats/stats/statistics.c @@ -23,8 +23,11 @@ #include <assert.h> +#include <stdlib.h> #include "statistics.h" #include "cats_strings/cats_strings.h" +#include "memory/cats_memory.h" +#include "configuration/configuration.h" void zero_statistics_stats(struct statistics *stats) @@ -36,28 +39,74 @@ void zero_statistics_stats(struct statistics *stats) char **has_been_populated_ts = stats->has_been_populated_ts; int32_t *N = stats->populated_by_classes; struct string_array *header = stats->stats_header; - + int64_t *custom_stats = stats->custom_stats; + int64_t custom_stat_count = stats->custom_stat_count; // reset everything struct statistics empty = {0}; *stats = empty; + // restore some values stats->file = file; stats->has_been_populated = has_been_populated; stats->has_been_populated_ts = has_been_populated_ts; stats->populated_by_classes = N; stats->stats_header = header; + stats->custom_stats = custom_stats; + stats->custom_stat_count = custom_stat_count; + + for (int32_t i = 0; i < STAT_MAX; i++) { + stats->stats[i] = 0; + } + + if (stats->custom_stats != NULL) { + for (int64_t i = 0; i < stats->custom_stat_count; i++) { + stats->custom_stats[i] = 0; + } + } + } -void init_statistics(struct statistics *stats) +void init_statistics(struct statistics *stats, struct cats_stats_registry *registry) { + if (stats->initialized) { + log_message(LOG_WARNING, "statistics are already initialised"); + } struct statistics empty = {0}; *stats = empty; + stats->custom_stat_count = registry->count; + stats->custom_stats = calloc_or_die(stats->custom_stat_count, sizeof(int64_t)); + stats->initialized = true; } void cleanup_statistics(struct statistics *stats) { if (stats->stats_header) free_string_array(&stats->stats_header); -} \ No newline at end of file + free(stats->custom_stats); + stats->custom_stats = NULL; +} + + +int64_t add_custom_stat(struct cats_stats_registry *registry, const char *name) +{ + int64_t count = registry->count; + string_array_add(registry->names, name); + registry->count += 1; + return count; +} + + +void init_stats_registry(struct cats_stats_registry *registry) +{ + registry->count = 0; + registry->names = new_string_array(); +} + +void cleanup_stats_registry(struct cats_stats_registry *registry) +{ + registry->count = 0; + free_string_array(®istry->names); + registry->names = NULL; +} diff --git a/src/cats/stats/statistics.h b/src/cats/stats/statistics.h index df0208d8300382553d6de9d67a803014e917963c..f5f9f5b3b08f6078ff1830487c01ad45e20021f9 100644 --- a/src/cats/stats/statistics.h +++ b/src/cats/stats/statistics.h @@ -23,10 +23,16 @@ #ifndef CATS_STATISTICS_H #define CATS_STATISTICS_H - +#include <stdbool.h> #include <stdio.h> #include <stdint.h> +struct cats_stats_registry { + int64_t count; + struct string_array *names; +}; + + // STAT_MIN and STAT_MAX are used for enumeration purposes // CS (CELL STATS) count the number of cells where the value of the specified stat is larger than zero // GS (GRID SUM) is the sum of the specified stat over the whole simulation area @@ -69,8 +75,10 @@ enum cats_stats { /// @brief data structure for collected statistics, both on a per-grid and global basis struct statistics { - + bool initialized; int64_t stats[STAT_MAX]; ///< individual cell stats, on for each \sa enum cats_stats + int64_t *custom_stats; + int64_t custom_stat_count; FILE *file; ///< file handle for output file int32_t *populated_by_classes; ///< only used for global stats: how many cells are populated by 1, 2, 3, ... N classes char **has_been_populated; ///< which cells have already been populated at least once? @@ -80,8 +88,9 @@ struct statistics { void zero_statistics_stats(struct statistics *stats); -void init_statistics(struct statistics *stats); +void init_statistics(struct statistics *stats, struct cats_stats_registry *registry); void cleanup_statistics(struct statistics *stats); - +void init_stats_registry(struct cats_stats_registry *registry); +int64_t add_custom_stat(struct cats_stats_registry *registry, const char *name); #endif //CATS_STATISTICS_H diff --git a/src/cats/stats/write_stats.c b/src/cats/stats/write_stats.c index b4e440dad9041af9531419b56420ff0d82a75fd9..eb80e40d7368f28f39532422f8d5a19fb3b9dac6 100644 --- a/src/cats/stats/write_stats.c +++ b/src/cats/stats/write_stats.c @@ -35,6 +35,8 @@ const char *get_stat_name(enum cats_stats which) { + + switch (which) { case CS_POPULATED: return "populated"; @@ -155,7 +157,7 @@ void gui_output_grid_stats(const struct string_array *header, const struct strin void write_grid_stats(struct cats_configuration *conf, struct cats_grid *grid, bool header) { - if (conf->performance_file && grid->id == 0) fflush(conf->performance_file); + if (conf->output.performance_file && grid->id == 0) fflush(conf->output.performance_file); if (grid->stats.file == NULL) { log_message(LOG_ERROR, diff --git a/src/cats/threading/threading-helpers.c b/src/cats/threading/threading-helpers.c index 653006e2696072790bbde6ff99c920023d8ca0ae..0160f610d0f5a991342862f8bacdf688a7e2555f 100644 --- a/src/cats/threading/threading-helpers.c +++ b/src/cats/threading/threading-helpers.c @@ -30,6 +30,13 @@ #include "misc/cats_random.h" #include "stats/grid_stats.h" #include "data/error.h" +#include "memory/cats_memory.h" + +void create_custom_stats_for_thread(struct statistics *thread_stats, struct statistics *grid_stats) +{ + thread_stats->custom_stat_count = grid_stats->custom_stat_count; + thread_stats->custom_stats = calloc_or_die(grid_stats->custom_stat_count, sizeof(int64_t)); +} void initialize_thread(struct cats_thread_info *thread, struct cats_grid *grid, struct cats_configuration *conf, int32_t id) @@ -37,14 +44,27 @@ initialize_thread(struct cats_thread_info *thread, struct cats_grid *grid, struc thread->rng = allocate_rng(); thread->rng_seed = conf->rng_seed; - for (int32_t i = 0; i < MAX_CLASSES; i++) { + for (int32_t i = 0; i < conf->grid_count; i++) { + + create_custom_stats_for_thread(&thread->stats[i], &grid->stats); zero_statistics_stats(&thread->stats[i]); } + thread->rw_debug_cells_with_adults = 0; + thread->rw_debug_random_walks = 0; + thread->rw_debug_deposits = 0; + thread->seed = get_random_seed(false); + for (int i = 0; i < 4; i++) { + thread->seed4[i] = get_random_seed64(false); + } thread->id = id; thread->grid = grid; thread->conf = conf; + thread->rng_buf_size = 32; + thread->rng_state_buffer = calloc_or_die(1, thread->rng_buf_size); + thread->rng_state = calloc_or_die(1, sizeof(struct random_data)); + initstate_r(get_random_seed(false), thread->rng_state_buffer, thread->rng_buf_size, thread->rng_state); } @@ -56,12 +76,22 @@ int32_t get_dispersal_radius(const struct cats_configuration *conf, int32_t id) void cleanup_threads(struct cats_thread_info *threads, int32_t num_threads) { + for (int32_t i = 0; i < num_threads; i++) { + gsl_rng_free(threads[i].rng); + free(threads[i].rng_state); + free(threads[i].rng_state_buffer); } - + for (int32_t i = 0; i < num_threads; i++) { + for (int32_t j = 0; j < threads[i].conf->grid_count; j++) { + free(threads[i].stats->custom_stats); + } + } free(threads); + + } diff --git a/src/cats/threading/threading.c b/src/cats/threading/threading.c index e593f2c302f4f403039f6595adefea93bad99d37..539fd2b938190e14846a3264f422c9fcae8cd052 100644 --- a/src/cats/threading/threading.c +++ b/src/cats/threading/threading.c @@ -94,6 +94,8 @@ setup_threads(struct cats_configuration *conf, struct cats_grid *grid, int32_t m } + + switch (t->strategy) { case TS_UNKNOWN: log_message(LOG_ERROR, "undefined threading strategy"); diff --git a/src/cats/threading/threading.h b/src/cats/threading/threading.h index 3a15267ab6692fa37bc64d428f560bc8dbc2609d..b34f2a2a9e462b4db9f15a0198218b4a8c2f1b3b 100644 --- a/src/cats/threading/threading.h +++ b/src/cats/threading/threading.h @@ -50,6 +50,14 @@ struct cats_thread_info { struct cats_configuration *conf; gsl_rng *rng; unsigned int rng_seed; + int64_t rw_debug_cells_with_adults; + int64_t rw_debug_random_walks; + int64_t rw_debug_deposits; + char *rng_state_buffer; + int32_t rng_buf_size; + struct random_data *rng_state; + unsigned int seed; + uint64_t seed4[4]; }; diff --git a/src/cats/vital_rates/setup_rates.c b/src/cats/vital_rates/setup_rates.c index 6e3279338b622a125d8cb4e3b89c0ac8e746277e..29d7585f62c448e6f83a4faa64fdd56830513f59 100644 --- a/src/cats/vital_rates/setup_rates.c +++ b/src/cats/vital_rates/setup_rates.c @@ -109,12 +109,32 @@ void set_vital_rate_maximum(struct cats_vital_rate *vital, cats_dt_rates max_rat } +void set_vital_rate_minimum(struct cats_vital_rate *vital, cats_dt_rates minimum) +{ + assert(vital != NULL); + if (minimum < 0) { + log_message(LOG_ERROR, "%s (%s): vital rate minimum %Lf must not be negative ", __func__, + vital->name, minimum); + exit_cats(EXIT_FAILURE); + } + + if (minimum > vital->max_rate) { + log_message(LOG_ERROR, "%s (%s): vital rate minimum %Lf must be less or equal to maximum %Lf " + , __func__, vital->name, minimum, vital->max_rate); + exit_cats(EXIT_FAILURE); + } + + vital->min_rate = minimum; + +} + void set_vital_density(struct cats_vital_rate *vital, enum cats_density_dependence density) { assert(vital != NULL); if (vital->is_carrying_capacity && density != NO_DENSITY_DEP) { - log_message(LOG_ERROR, "%s (%s): carrying capacity may never depend on population density", __func__, - vital->name); + + log_message(LOG_ERROR, "%s (%s): carrying capacity may never depend on population density", __func__, vital->name); + exit_cats(EXIT_FAILURE); } vital->density = density; diff --git a/src/cats/vital_rates/setup_rates.h b/src/cats/vital_rates/setup_rates.h index f700b88f70d11d30e091675888ada24763859c03..3ea94f64c815026bb9eb4874fd6ca8f2e971a71f 100644 --- a/src/cats/vital_rates/setup_rates.h +++ b/src/cats/vital_rates/setup_rates.h @@ -45,7 +45,7 @@ void set_vital_density(struct cats_vital_rate *vital, enum cats_density_dependen void load_vital_rates(struct cats_configuration *conf); -void set_vital_cutoff(struct cats_vital_rate *vital, const cats_dt_rates value, +void set_vital_cutoff(struct cats_vital_rate *vital, cats_dt_rates value, const struct cats_species_param *param); enum cats_rate_function_type get_function_type(const struct cats_configuration *conf, const char *short_name); @@ -60,4 +60,6 @@ void set_vital_rate_link_hybrid_function(struct cats_vital_rate *vr, struct cats void set_vital_density(struct cats_vital_rate *vital, enum cats_density_dependence density); +void set_vital_rate_minimum(struct cats_vital_rate *vital, cats_dt_rates minimum); + #endif //CATS_SETUP_RATES_H diff --git a/src/modules/butterflies/CMakeLists.txt b/src/modules/butterflies/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c20a250845e6cfd490a47a0312602fc2f7ded1c1 --- /dev/null +++ b/src/modules/butterflies/CMakeLists.txt @@ -0,0 +1,27 @@ +add_library(cats-butterflies SHARED "" + butterflies_actions.c butterflies_actions.h + butterflies_vital_rates.c butterflies_vital_rates.h + module.h + butterflies_dispersal.c butterflies_dispersal.h + butterflies_populations.c butterflies_populations.h + butterflies_inline.h + butterflies_generations.c butterflies_generations.h + butterflies_stats.c + butterflies_overlays.c butterflies_overlays.h + butterflies_paths.c butterflies_paths.h + butterflies_actions_setup.c + butterflies_actions_setup.h butterflies_initial_population.c butterflies_initial_population.h butterflies_scale.c butterflies_scale.h) + +target_include_directories(cats-butterflies PUBLIC ".") + +target_sources(cats-butterflies + PRIVATE + butterflies_main.c butterflies_main.h + + PUBLIC + + ) + + +set_property(TARGET cats-butterflies PROPERTY POSITION_INDEPENDENT_CODE ON) +target_link_libraries(cats-butterflies cats_logging libcats) diff --git a/src/modules/butterflies/butterflies_actions.c b/src/modules/butterflies/butterflies_actions.c new file mode 100644 index 0000000000000000000000000000000000000000..4ce07bee845a5f15064bc28020a6b691ba213199 --- /dev/null +++ b/src/modules/butterflies/butterflies_actions.c @@ -0,0 +1,360 @@ +#include "actions/cats_actions.h" +#include "modules/module_header.h" +#include "butterflies_actions.h" +#include "butterflies_main.h" +#include "inline_overlays.h" +#include "butterflies_populations.h" + +#include "butterflies_inline.h" +#include "grids/grid_wrapper.h" +#include "grids/gdal_save.h" +#include "paths/paths.h" +#include "populations/population.h" +#include "butterflies_generations.h" +#include "butterflies_dispersal.h" +#include "butterflies_overlays.h" +#include "butterflies_paths.h" +#include "inline.h" +#include "lambda/leslie_matrix.h" +#include "temporal/years.h" +#include "butterflies_initial_population.h" + + +enum action_status bf_action_stats_reset(struct cats_grid *grid, struct cats_configuration *conf) +{ + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + int module_id = CATS_MODULE_ID; + + struct grid_data_butterflies *module_data = grid->grid_modules[module_id].module_data; + if (conf->time.year_current == conf->time.year_start && + module_conf->generations_max == module_data->generation_current) { + log_message(LOG_INFO, "SUMMARY: Scale factor: %Lf", grid->param.scale_factor); + struct lambda_parameters l_param = {0}; + l_param.calculate_scale = true; + l_param.suitability = grid->param.OT; + l_param.N = 0; + l_param.K = (cats_dt_population) + (get_vital_rate_maximum(&conf->param[grid->id].carrying_capacity) * + conf->param->max_adult_cc_fraction); + l_param.grid = 0; + l_param.row = 0; + l_param.col = 0; + l_param.param = &conf->param[grid->id]; + l_param.species_id = grid->id; + bool print_rate = false; + cats_dt_rates female_fraction = module_conf->female_fraction; + cats_dt_rates stationary = module_conf->probability_to_stay; + cats_dt_rates mobile = 1.0 - stationary; + cats_dt_rates egg_fraction_source = module_conf->egg_fraction_source; + cats_dt_rates eggs_per_female = calculate_rate_for_matrix(&module_conf->eggs_per_female, &l_param, + print_rate); + cats_dt_rates reproduction_rate = calculate_rate_for_matrix(&module_conf->reproduction_rate, &l_param, + print_rate); + cats_dt_rates K = + calculate_rate_for_matrix(&conf->param[grid->id].carrying_capacity, &l_param, print_rate) * + conf->param->max_adult_cc_fraction; + cats_dt_rates local_eggs = (stationary + mobile * egg_fraction_source) * eggs_per_female; + // female -> female + // to achieve the target reproduction rate, the number of eggs per female laid in the cell + // that survive and become adult has to be the reproduction rate divided by the female fraction divided by the number of eggs + cats_dt_rates eggs_to_adults_rate = + bf_egg_to_adult_survival_rate(reproduction_rate, local_eggs) / module_conf->female_fraction; + cats_dt_rates result = local_eggs * eggs_to_adults_rate * female_fraction; + cats_dt_rates generations = calculate_rate_for_matrix(&module_conf->butterfly_generations, &l_param, + print_rate); + + + log_message(LOG_INFO, "SUMMARY: reproduction rate at OT: %Lf", reproduction_rate); + log_message(LOG_INFO, "SUMMARY: eggs per female at OT: %Lf", eggs_per_female); + log_message(LOG_INFO, "SUMMARY: local eggs at OT: %Lf", local_eggs); + log_message(LOG_INFO, "SUMMARY: stationary females at OT: %Lf", stationary); + log_message(LOG_INFO, "SUMMARY: eggs to adult rate at OT: %Lf", eggs_to_adults_rate); + log_message(LOG_INFO, "SUMMARY: egg fraction source (non-stationary females) at OT: %Lf", + egg_fraction_source); + log_message(LOG_INFO, "SUMMARY: carrying capacity at OT: %Lf", K); + log_message(LOG_INFO, "SUMMARY: generations at OT: %Lf", generations); + log_message(LOG_INFO, "SUMMARY: effective female to female rate at OT: %Lf\n", result); + + l_param.suitability = 1.0; + eggs_per_female = calculate_rate_for_matrix(&module_conf->eggs_per_female, &l_param, print_rate); + reproduction_rate = calculate_rate_for_matrix(&module_conf->reproduction_rate, &l_param, print_rate); + K = calculate_rate_for_matrix(&conf->param[grid->id].carrying_capacity, &l_param, print_rate) * + conf->param->max_adult_cc_fraction; + local_eggs = (stationary + mobile * egg_fraction_source) * eggs_per_female; + eggs_to_adults_rate = + bf_egg_to_adult_survival_rate(reproduction_rate, local_eggs) / module_conf->female_fraction; + result = local_eggs * eggs_to_adults_rate; + generations = calculate_rate_for_matrix(&module_conf->butterfly_generations, &l_param, print_rate); + + log_message(LOG_INFO, "SUMMARY: reproduction rate at suitability 1: %Lf", reproduction_rate); + log_message(LOG_INFO, "SUMMARY: eggs per female at suitability 1: %Lf", eggs_per_female); + log_message(LOG_INFO, "SUMMARY: local eggs at suitability 1: %Lf", local_eggs); + log_message(LOG_INFO, "SUMMARY: stationary females at suitability 1: %Lf", stationary); + log_message(LOG_INFO, "SUMMARY: eggs to adult rate at suitability 1: %Lf", eggs_to_adults_rate); + log_message(LOG_INFO, "SUMMARY: egg fraction source (non-stationary females) at suitability 1: %Lf", + egg_fraction_source); + log_message(LOG_INFO, "SUMMARY: carrying capacity at suitability 1: %Lf", K); + log_message(LOG_INFO, "SUMMARY: generations at suitability 1: %Lf", generations); + log_message(LOG_INFO, "SUMMARY: effective female to female rate at suitability 1: %Lf\n", result); + + } + + return action_grid_stats_reset(grid, conf); +} + + +void grid_butterflies_maturation(struct cats_grid *grid, struct cats_thread_info *ts) +{ + struct cats_configuration *conf = ts->conf; + const cats_dt_coord start_row = ts->area.start_row; + const cats_dt_coord end_row = ts->area.end_row; + + const cats_dt_coord start_col = ts->area.start_col; + const cats_dt_coord end_col = ts->area.end_col; + + ts->rw_debug_cells_with_adults = 0; + + for (cats_dt_coord row = start_row; row < end_row; row++) { + for (cats_dt_coord col = start_col; col < end_col; col++) { +#ifdef BF_DEBUG + if (get_adult_population(grid, row, col)) { + printf("BFDBG::%s::adults at maturation::%d,%d,%d\n", + __func__, row, col, get_adult_population(grid, row, col)); + } +#endif + if (cell_excluded_by_overlay(conf, row, col) + || bf_cell_excluded_by_generation(grid, row, col)) { +#ifdef BF_DEBUG + if (get_adult_population(grid, row, col)) { + printf("BFDBG::%s::adults at maturation::%d,%d,%d - excluded by generation\n", + __func__, row, col, get_adult_population(grid, row, col)); + } +#endif + + continue; + } + + bf_cell_maturation(grid, ts, row, col, false); + } + } + +} + + +void bf_initial_population_to_eggs(struct cats_grid *grid, struct cats_thread_info *ts) +{ + struct cats_configuration *conf = ts->conf; + const cats_dt_coord start_row = ts->area.start_row; + const cats_dt_coord end_row = ts->area.end_row; + + const cats_dt_coord start_col = ts->area.start_col; + const cats_dt_coord end_col = ts->area.end_col; + + + for (cats_dt_coord row = start_row; row < end_row; row++) { + for (cats_dt_coord col = start_col; col < end_col; col++) { + + if (cell_excluded_by_overlay(conf, row, col)) { + continue; + } + set_population(grid, row, col, get_adult_population(grid, row, col)); + butterflies_cell_dispersal(grid, ts, row, col, false, true); + } + } + +} + + +void butterflies_area_dispersal(struct cats_grid *grid, struct cats_thread_info *ts) +{ + struct cats_configuration *conf = ts->conf; + const cats_dt_coord start_row = ts->area.start_row; + const cats_dt_coord end_row = ts->area.end_row; + + const cats_dt_coord start_col = ts->area.start_col; + const cats_dt_coord end_col = ts->area.end_col; + + ts->rw_debug_cells_with_adults = 0; + ts->rw_debug_random_walks = 0; + ts->rw_debug_deposits = 0; + + + for (cats_dt_coord row = start_row; row < end_row; row++) { + for (cats_dt_coord col = start_col; col < end_col; col++) { + if (cell_excluded_by_overlay(conf, row, col) + || bf_cell_excluded_by_generation(grid, row, col)) { + continue; + } + + butterflies_cell_dispersal(grid, ts, row, col, false, false); + } + } + + /* + if (ts->rw_debug_cells_with_adults) { + log_message(LOG_INFO, "thread %d: %ld cells with adults, %ld random walks, %ld deposits", + ts->id, ts->rw_debug_cells_with_adults, ts->rw_debug_random_walks, ts->rw_debug_deposits); + } + */ + +} + + +enum action_status bf_action_maturation(struct cats_grid *grid, struct cats_configuration *conf) +{ + threaded_action(&grid_butterflies_maturation, grid, conf, TS_DEFAULT); + + return ACTION_RUN; +} + + +enum action_status bf_action_save_grid(struct cats_grid *grid, struct cats_configuration *conf) +{ + if (!is_output_year(&conf->time)) return ACTION_NOT_RUN; + int32_t id = grid->id; + char *filename = bf_population_filename(conf, grid); + struct grid_wrapper data = gridwrapper(grid->population, grid->dimension); + save_grid_to_gdal(&data, GDT_Int32, conf, filename, conf->param[id].species_name); + free(filename); + return ACTION_RUN; +} + + +enum action_status bf_action_save_overlay(struct cats_grid *grid, struct cats_configuration *conf) +{ + if (!is_output_year(&conf->time)) return ACTION_NOT_RUN; + int32_t id = grid->id; + + char *filename = bf_population_overlay_filename(conf, grid); + int module_id = CATS_MODULE_ID; + + struct grid_data_butterflies *module_data = grid->grid_modules[module_id].module_data; + + struct grid_wrapper data = gridwrapper(module_data->info_layer, grid->dimension); + save_grid_to_gdal(&data, GDT_Int32, conf, filename, conf->param[id].species_name); + free(filename); + return ACTION_RUN; +} + + +enum action_status bf_action_save_eggs_grid(struct cats_grid *grid, struct cats_configuration *conf) +{ + if (!is_output_year(&conf->time)) return ACTION_NOT_RUN; + int32_t id = grid->id; + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *module_data = grid->grid_modules[module_id].module_data; + char *filename = bf_population_eggs_filename(conf, grid); + struct grid_wrapper data = gridwrapper(module_data->eggs, grid->dimension); + save_grid_to_gdal(&data, GDT_Int32, conf, filename, conf->param[id].species_name); + free(filename); + return ACTION_RUN; +} + + +enum action_status bf_action_dispersal(struct cats_grid *grid, struct cats_configuration *conf) +{ + threaded_action(&butterflies_area_dispersal, grid, conf, TS_DISPERSAL); + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + + for (enum butterfly_stats which = BF_POPULATED_AT_DISPERSAL; which < BF_STAT_MAX; which++) { + int64_t stat_id = module_conf->stat_ids[which]; + log_message(LOG_INFO, "STAT %s: %ld", bf_get_stats_field_name(which), grid->stats.stats[stat_id]); + } + + + return ACTION_RUN; +} + + +enum action_status bf_action_stats_gather(struct cats_grid *grid, struct cats_configuration *conf) +{ + threaded_action(&bf_area_stats_gather, grid, conf, TS_DEFAULT); + return ACTION_RUN; +} + + +enum action_status bf_action_stats_write(__attribute__((unused)) struct cats_grid *grid, + __attribute__((unused)) struct cats_configuration *conf) +{ + bf_stats_write(conf, grid); + + return ACTION_RUN; +} + + +// only run at the start of the year +enum action_status bf_action_generation_update(struct cats_grid *grid, struct cats_configuration *conf) +{ + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + const int64_t egg_cells_id = module_conf->stat_ids[BF_CELLS_WITH_EGGS]; + const int64_t egg_cells_removed_id = module_conf->stat_ids[BF_CELLS_WITH_EGGS_REMOVED]; + grid->stats.custom_stats[egg_cells_id] = 0; + grid->stats.custom_stats[egg_cells_removed_id] = 0; + + data->generation_current = module_conf->generations_max; + log_message(LOG_IMPORTANT, "resetting generation to %d", module_conf->generations_max); + + if (grid->param.initial_population.adjusted == false) { + bf_initial_population_adjustment(conf, grid); + grid->param.initial_population.adjusted = true; + } + + threaded_action(&bf_area_generation_update, grid, conf, TS_DEFAULT); + + int64_t cells_with_eggs = grid->stats.custom_stats[egg_cells_id]; + int64_t cells_with_eggs_removed = grid->stats.custom_stats[egg_cells_removed_id]; + log_message(LOG_INFO, "%ld cells with eggs left, %ld cells with eggs removed", cells_with_eggs, + cells_with_eggs_removed); + grid->stats.custom_stats[egg_cells_id] = 0; + grid->stats.custom_stats[egg_cells_removed_id] = 0; + return ACTION_RUN; +} + + +enum action_status bf_action_overlay_update(struct cats_grid *grid, struct cats_configuration *conf) +{ + return bf_grid_overlay_update(conf, grid); +} + + +enum action_status bf_action_generation_finish(struct cats_grid *grid, struct cats_configuration *conf) +{ + const int module_id = CATS_MODULE_ID; + + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + threaded_action(&bf_area_kill_adults, grid, conf, TS_DEFAULT); + + + data->generation_current--; + assert(data->generation_current >= 0); + // struct conf_data_butterflies *data = CATS_MODULE_DATA; + return ACTION_RUN; +} + + +enum action_status +bf_action_generation_start(struct cats_grid *grid, __attribute__((unused)) struct cats_configuration *conf) +{ +#ifdef BF_DEBUG + cats_dt_population rows = grid->dimension.rows; + cats_dt_population cols = grid->dimension.cols; + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { + if (get_adult_population(grid, row, col)) { + printf("BFDBG::%s::adults at start of generation::%d,%d,%d\n", + __func__, row, col, get_adult_population(grid, row, col)); + + } + } + } +#endif + int module_id = CATS_MODULE_ID; + + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + log_message(LOG_INFO, "Starting generation %d", data->generation_current); + assert(data->generation_current >= 0); + return ACTION_RUN; +} diff --git a/src/modules/butterflies/butterflies_actions.h b/src/modules/butterflies/butterflies_actions.h new file mode 100644 index 0000000000000000000000000000000000000000..139efe614e8d1a1a7a9021f6172507ef1ea7e99a --- /dev/null +++ b/src/modules/butterflies/butterflies_actions.h @@ -0,0 +1,30 @@ +#ifndef CATS_BUTTERFLIES_ACTIONS_H +#define CATS_BUTTERFLIES_ACTIONS_H + +#include "configuration/configuration.h" + +enum action_status bf_action_maturation(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_save_grid(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_dispersal(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_stats_gather(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_stats_reset(__attribute__((unused)) struct cats_grid *grid, __attribute__((unused)) struct cats_configuration *conf); + +enum action_status bf_action_stats_write(__attribute__((unused)) struct cats_grid *grid, __attribute__((unused)) struct cats_configuration *conf); + +enum action_status bf_action_overlay_update(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_generation_finish(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_generation_start(struct cats_grid *grid, __attribute__((unused)) struct cats_configuration *conf); + +enum action_status bf_action_generation_update(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_save_eggs_grid(struct cats_grid *grid, struct cats_configuration *conf); + +enum action_status bf_action_save_overlay(struct cats_grid *grid, struct cats_configuration *conf); + +#endif //CATS_BUTTERFLIES_ACTIONS_H diff --git a/src/modules/butterflies/butterflies_actions_setup.c b/src/modules/butterflies/butterflies_actions_setup.c new file mode 100644 index 0000000000000000000000000000000000000000..9258f0ffaa45de1ff829a6faeab1329470d4db84 --- /dev/null +++ b/src/modules/butterflies/butterflies_actions_setup.c @@ -0,0 +1,78 @@ + +#include "butterflies_actions_setup.h" +#include "configuration/configuration.h" +#include "actions/setup_actions.h" +#include "module.h" +#include "modules/module_header.h" +#include "butterflies_actions.h" +#include "butterflies_main.h" + +void bf_add_generation_action(struct cats_configuration *conf, action_function function, const char *name, + int generation) +{ + char *result = NULL; + int rc = asprintf(&result, "%s (generation %d)", name, generation); + asprintf_check(rc); + append_action(conf, function, ALL_STAGES, result, module_name); + free(result); +} + +void bf_register_actions(struct cats_configuration *conf) +{ + + register_action_function(conf, bf_action_stats_reset, "butterfly_action_reset_stats", + "resetting butterfly statistics"); + register_action_function(conf, bf_action_generation_update, "butterfly_action_update_generations", + "update generations"); + + register_action_function(conf, bf_action_generation_finish, "bf_action_generation_finish", + "update generation"); + register_action_function(conf, bf_action_generation_start, "bf_action_generation_start", + "start generation"); + + + register_action_function(conf, bf_action_maturation, "butterfly_action_egg_to_adult", + "transition eggs to adults"); + + register_action_function(conf, bf_action_overlay_update, "butterfly_action_overlay_update", + "updating overlays"); + + register_action_function(conf, bf_action_dispersal, "butterfly_action_dispersal", "egg dispersal"); + register_action_function(conf, bf_action_save_grid, "bf_action_save_grid", "output"); + register_action_function(conf, bf_action_save_eggs_grid, "bf_action_save_egg_grid", "egg output"); + register_action_function(conf, bf_action_save_overlay, "bf_action_save_overlay_grid", "overlay output"); + register_action_function(conf, bf_action_stats_gather, "bf_action_stats_gather", "gather stats"); + register_action_function(conf, bf_action_stats_reset, "bf_action_stats_reset", "reset stats"); + register_action_function(conf, bf_action_stats_write, "bf_action_stats_write", "write stats"); +} + + +void bf_add_actions(struct cats_configuration *conf) +{ + struct conf_data_butterflies *data = CATS_MODULE_DATA; + + log_message(LOG_INFO, "Adding actions of %d generations", data->generations_max); + + + append_action(conf, bf_action_stats_reset, ALL_STAGES, "resetting butterfly statistics", module_name); + append_action_by_name(conf, "action_load_environments", ALL_STAGES, "environment update", module_name); + append_action_by_name(conf, "action_overlay_update", ALL_STAGES, "overlay update", module_name); + + append_action(conf, bf_action_overlay_update, ALL_STAGES, "updating resource layer", module_name); + append_action(conf, bf_action_generation_update, ALL_STAGES, "update generations", module_name); + append_action(conf, bf_action_save_overlay, ALL_STAGES, "save overlay", module_name); + + for (int32_t generation = data->generations_max; generation > 0; generation--) { + + bf_add_generation_action(conf, bf_action_stats_reset, "reset stats", generation); + bf_add_generation_action(conf, bf_action_generation_start, "start generation", generation); + bf_add_generation_action(conf, bf_action_maturation, "transition eggs to adults", + generation); + bf_add_generation_action(conf, bf_action_save_grid, "output adults", generation); + bf_add_generation_action(conf, bf_action_stats_gather, "gather stats", generation); + bf_add_generation_action(conf, bf_action_dispersal, "dispersal", generation); + bf_add_generation_action(conf, bf_action_save_eggs_grid, "output eggs", generation); + bf_add_generation_action(conf, bf_action_generation_finish, "finish generation", generation); + bf_add_generation_action(conf, bf_action_stats_write, "write stats", generation); + } +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_actions_setup.h b/src/modules/butterflies/butterflies_actions_setup.h new file mode 100644 index 0000000000000000000000000000000000000000..224068527270fadfbef4b2e9a312283468c5a7ab --- /dev/null +++ b/src/modules/butterflies/butterflies_actions_setup.h @@ -0,0 +1,9 @@ +#ifndef CATS_BUTTERFLIES_ACTIONS_SETUP_H +#define CATS_BUTTERFLIES_ACTIONS_SETUP_H + +#include "configuration/configuration.h" + +void bf_register_actions(struct cats_configuration *conf); +void bf_add_actions(struct cats_configuration *conf); + +#endif //CATS_BUTTERFLIES_ACTIONS_SETUP_H diff --git a/src/modules/butterflies/butterflies_dispersal.c b/src/modules/butterflies/butterflies_dispersal.c new file mode 100644 index 0000000000000000000000000000000000000000..b995e3dab695c13bf7998b6dba32c35602ee734f --- /dev/null +++ b/src/modules/butterflies/butterflies_dispersal.c @@ -0,0 +1,228 @@ +#include "inline_population.h" +#include "butterflies_inline.h" +#include "misc/cats_random.h" +#include "populations/carrying_capacity.h" +#include "inline_overlays.h" + +#include <math.h> +#include <gsl/gsl_randist.h> +#include "butterflies_main.h" +#include "butterflies_dispersal.h" +#include "configuration/configuration.h" +#include "modules/module_header.h" + +const int N_DIRECTIONS = 9; +/* + * directions + * 4 3 2 + * 5 0 1 + * 6 7 8 + */ + +const cats_dt_coord DIRECTION_OFFSETS[9][2] = { + {+0, +0}, + {+0, +1}, + {-1, +1}, + {-1, +0}, + {-1, -1}, + {+0, -1}, + {+1, -1}, + {+1, +0}, + {+1, +1} +}; + + +static void inline single_random_walk(struct cats_thread_info *ts, struct cats_grid *grid, cats_dt_coord source_row, + cats_dt_coord source_col, int32_t eggs, cats_dt_rates egg_fraction_step, + __attribute__((unused)) int32_t rw_num) +{ + + const int module_id = CATS_MODULE_ID; + const struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + const struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + const bool debug_rw = module_conf->debug_rw; + + + int32_t eggs_left = eggs; + const cats_dt_coord max_steps = module_conf->animal_dispersal_max_radius; + + + cats_dt_coord row = source_row; + cats_dt_coord col = source_col; + + const cats_dt_coord rows = grid->dimension.rows; + const cats_dt_coord cols = grid->dimension.cols; + + + for (cats_dt_coord step = 0; step < max_steps; step++) { + const unsigned long int direction = gsl_rng_uniform_int(ts->rng, N_DIRECTIONS); + + + const cats_dt_coord *offsets = DIRECTION_OFFSETS[direction]; + const cats_dt_coord row_offset = offsets[0]; + const cats_dt_coord col_offset = offsets[1]; + + assert(row_offset >= -1 && row_offset <= 1); + assert(col_offset >= -1 && col_offset <= 1); + + row += row_offset; + col += col_offset; + + if (row >= rows || row < 0 || col >= cols || col < 0) { + return; // we escaped the simulation extent and got lost + } + + + // is the cell a valid dispersal target location? + if (!(data->info_layer[row][col] & BF_CELL_VALID_DISPERSAL_TARGET)) { + if (debug_rw) { + fprintf(module_conf->debug_rw_file, "%d,%d,%d,%d,%d,%d,%d\n", rw_num, row, col, + step + 1, + module_conf->animal_dispersal_max_radius - step - 1, 0, eggs_left); + } + + continue; + } + + + int32_t eggs_to_deposit = (int32_t) ceilf((float) (eggs_left * egg_fraction_step)); + assert(eggs_to_deposit >= 0); + //printf("x %d %d -> %d %d: %d of %d / %Lf\n", source_row, source_col, row, col, eggs_to_deposit, eggs, egg_fraction_step); + if (eggs_to_deposit > eggs_left) { + eggs_to_deposit = eggs_left; + } + + eggs_left -= eggs_to_deposit; + data->eggs[row][col] += (float) eggs_to_deposit; + ts->rw_debug_deposits++; + if (debug_rw) { + fprintf(module_conf->debug_rw_file, "%d,%d,%d,%d,%d,%d,%d\n", rw_num, row, col, step + 1, + module_conf->animal_dispersal_max_radius - step - 1, eggs_to_deposit, eggs_left); + } + + + if (eggs_left == 0) break; + + } + + assert(eggs_left >= 0); +} + + + + + + +void +butterflies_cell_dispersal(struct cats_grid *grid, struct cats_thread_info *ts, cats_dt_coord row, cats_dt_coord col, + bool check_exclusion, bool local_only) +{ + const struct cats_configuration *conf = ts->conf; + if (check_exclusion + && (cell_excluded_by_overlay(conf, row, col) || bf_cell_excluded_by_generation(grid, row, col))) + return; + + + // total adults: the number of adults that became adult in this cell, possibly exceeding the carrying capacity (thanks to poisson processes) + const cats_dt_population total_adults = get_adult_population(grid, row, col); + + if (total_adults == 0) return; + ts->rw_debug_cells_with_adults++; + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + // total females: how many of the total adults are female, as drawn from a binomial distribution with p = 0.5 + // can be safely cast, because gsl_ran_binomial will return a number <= total_adults + const cats_dt_population total_females = (cats_dt_population) gsl_ran_binomial(ts->rng, + (double) module_conf->female_fraction, + total_adults); + assert(total_females >= 0 && total_females <= total_adults); + + // total males: the rest + const cats_dt_population total_males = total_adults - total_females; + + // we need at least one female and one male + if (total_females == 0 || total_males == 0) return; + + // here we calculate the number of eggs per female, so we can return early if the cell is unsuitable + + cats_dt_rates eggs_per_f = calculate_rate(&module_conf->eggs_per_female, total_adults, &grid->param, grid, row, + col, NULL); + int32_t eggs_per_female = (int32_t) ceill(eggs_per_f); + assert(eggs_per_female >= 0); + + if (eggs_per_female == 0) return; + + + // how many females will leave the cell + // wandering_females: the number of females that will leave the cell and do a random walk + cats_dt_rates probability_to_leave = (1.0 - module_conf->probability_to_stay); + cats_dt_population wandering_females = poisson_population_capped(ts->rng, + total_females * probability_to_leave, + total_females); + + // stationary_females: the number of females that will not leave the cell, and leave all their eggs here + cats_dt_population stationary_females = total_females - wandering_females; + assert(stationary_females >= 0); + assert(wandering_females >= 0); + + + const int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + const bool debug_rw = module_conf->debug_rw; + + int32_t eggs_to_disperse_per_female = (int32_t) ceill( + eggs_per_female * (1.0 - module_conf->egg_fraction_source)); + + // all the eggs of all the stationary females + the eggs the wandering females leave in the source cell + const cats_dt_rates source_cell_eggs = stationary_females * eggs_per_female + + wandering_females * (eggs_per_female - eggs_to_disperse_per_female); + + data->eggs[row][col] += (float) ceill(source_cell_eggs); + + + if (eggs_to_disperse_per_female == 0) { + log_message(LOG_ERROR, "%s: random walk with no eggs to distribute in cell %d %d", __func__, row, col); + return; + } + + const cats_dt_rates egg_fraction_step = module_conf->egg_fraction_step; + + + + if (local_only) { + return; + } + + if (debug_rw) { + fprintf(module_conf->debug_rw_file, "# total source cell eggs = %d\n", (int) ceill(source_cell_eggs)); + fprintf(module_conf->debug_rw_file, "# stationary females = %d\n", stationary_females); + fprintf(module_conf->debug_rw_file, "# wandering females = %d\n", wandering_females); + fprintf(module_conf->debug_rw_file, "# total females = %d\n", wandering_females + stationary_females); + fprintf(module_conf->debug_rw_file, "# wandering females source cell eggs = %d\n", + (int) (wandering_females * (eggs_per_female - eggs_to_disperse_per_female))); + fprintf(module_conf->debug_rw_file, "# stationary females source cell eggs = %d\n", + (int) (stationary_females * eggs_per_female)); + fprintf(module_conf->debug_rw_file, "id,row,col,step,steps left,eggs deposited,eggs left\n"); + + } + + for (int32_t rw_number = 0; rw_number < wandering_females; rw_number++) { + if (debug_rw) { + + fprintf(module_conf->debug_rw_file, "%d,%d,%d,0,%d,%d,%d\n", rw_number, row, col, + module_conf->animal_dispersal_max_radius, + (int) ceill(eggs_per_female * module_conf->egg_fraction_source), + (int) ceill(eggs_to_disperse_per_female)); + + } + + + single_random_walk(ts, grid, row, col, eggs_to_disperse_per_female, egg_fraction_step, rw_number); + ts->rw_debug_random_walks++; + } + if (debug_rw) { + fflush(module_conf->debug_rw_file); + fclose(module_conf->debug_rw_file); + log_message(LOG_IMPORTANT, "Ending simulation early - first cell with random walks complete"); + exit_cats(EXIT_SUCCESS); + } +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_dispersal.h b/src/modules/butterflies/butterflies_dispersal.h new file mode 100644 index 0000000000000000000000000000000000000000..d0fe418daccdaa8a03b71d48406dec781206f9ad --- /dev/null +++ b/src/modules/butterflies/butterflies_dispersal.h @@ -0,0 +1,14 @@ +#ifndef CATS_BUTTERFLIES_DISPERSAL_H +#define CATS_BUTTERFLIES_DISPERSAL_H + +#include "threading/threading.h" +#include "cats/configuration/configuration.h" + +void +butterflies_cell_dispersal(struct cats_grid *grid, struct cats_thread_info *ts, + cats_dt_coord row, cats_dt_coord col, + bool check_exclusion, + bool local_only); + +void bf_initial_population_to_eggs(struct cats_grid *grid, struct cats_thread_info *ts); +#endif //CATS_BUTTERFLIES_DISPERSAL_H diff --git a/src/modules/butterflies/butterflies_generations.c b/src/modules/butterflies/butterflies_generations.c new file mode 100644 index 0000000000000000000000000000000000000000..3e0ab6617f8f0d0d4efe25115d927ac16138080c --- /dev/null +++ b/src/modules/butterflies/butterflies_generations.c @@ -0,0 +1,73 @@ +#include "butterflies_populations.h" +#include "inline_overlays.h" +#include "butterflies_main.h" +#include "modules/module_header.h" +#include "actions/cats_actions.h" +#include "butterflies_generations.h" +#include "inline.h" + +// only run at the start of the year +void bf_area_generation_update(struct cats_grid *grid, struct cats_thread_info *ts) +{ + struct cats_configuration *conf = ts->conf; + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + struct cats_vital_rate *rate = &module_conf->butterfly_generations; + + const cats_dt_coord start_row = ts->area.start_row; + const cats_dt_coord end_row = ts->area.end_row; + const cats_dt_coord start_col = ts->area.start_col; + const cats_dt_coord end_col = ts->area.end_col; + + struct statistics *stats = &ts->stats[grid->id]; + const cats_dt_rates ot = grid->param.OT; + + const int64_t egg_cells_id = module_conf->stat_ids[BF_CELLS_WITH_EGGS]; + const int64_t egg_cells_removed_id = module_conf->stat_ids[BF_CELLS_WITH_EGGS]; + int64_t cells_with_eggs = 0; + int64_t cells_with_eggs_removed = 0; + + for (cats_dt_coord row = start_row; row < end_row; row++) { + for (cats_dt_coord col = start_col; col < end_col; col++) { + + if (get_suitability(grid, row, col) < ot) + { + if (data->eggs[row][col] > 0) cells_with_eggs_removed += 1; + data->eggs[row][col] = 0.0f; + data->generations[row][col] = 0.0f; + set_population_ignore_cc(grid, row, col, 0); + + continue; + } + + if (cell_excluded_by_overlay(conf, row, col)) { + if (data->eggs[row][col] > 0) cells_with_eggs_removed += 1; + data->eggs[row][col] = 0.0f; + data->generations[row][col] = 0.0f; + set_population_ignore_cc(grid, row, col, 0); + continue; + } + + if ( ! (data->info_layer[row][col] & BF_CELL_VALID_DISPERSAL_TARGET)){ + if (data->eggs[row][col] > 0) cells_with_eggs_removed += 1; + data->eggs[row][col] = 0.0f; + data->generations[row][col] = 0.0f; + set_population_ignore_cc(grid, row, col, 0); + continue; + } + + cats_dt_rates gen = calculate_rate(rate, 0, conf->param, grid, row, col, NULL); + + data->generations[row][col] = (float) gen; + if (data->eggs[row][col] > 0) cells_with_eggs += 1; + + } + } + + stats->custom_stats[egg_cells_id] += cells_with_eggs; + stats->custom_stats[egg_cells_removed_id] += cells_with_eggs_removed; + + +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_generations.h b/src/modules/butterflies/butterflies_generations.h new file mode 100644 index 0000000000000000000000000000000000000000..5df061be22cd4cf788a65ed00eab4d09ba65f98a --- /dev/null +++ b/src/modules/butterflies/butterflies_generations.h @@ -0,0 +1,9 @@ +#ifndef CATS_BUTTERFLIES_GENERATIONS_H +#define CATS_BUTTERFLIES_GENERATIONS_H + +#include "data/cats_grid.h" +#include "threading/threading.h" + +void bf_area_generation_update(struct cats_grid *grid, struct cats_thread_info *ts); + +#endif //CATS_BUTTERFLIES_GENERATIONS_H \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_initial_population.c b/src/modules/butterflies/butterflies_initial_population.c new file mode 100644 index 0000000000000000000000000000000000000000..f4fc811d292b12b08cf0ad73a594ee11e36c3319 --- /dev/null +++ b/src/modules/butterflies/butterflies_initial_population.c @@ -0,0 +1,112 @@ + +#include "lambda/leslie_matrix.h" +#include "inline.h" + +#include "butterflies_dispersal.h" +#include "butterflies_generations.h" +#include "populations/population.h" +#include "paths/paths.h" + +#include "butterflies_populations.h" + +#include "butterflies_main.h" +#include "butterflies_actions.h" +#include "modules/module_header.h" +#include "actions/cats_actions.h" +#include "butterflies_initial_population.h" + + +void bf_initial_population_adjustment(struct cats_configuration *conf, struct cats_grid *grid) +{ + int64_t init_populated_cells = count_populated_cells(grid); + int64_t invalid_resources = butterflies_prune_invalid_cells(grid); + log_message(LOG_IMPORTANT, "Loaded initial populations: %ld cells occupied before adjustment", + init_populated_cells); + if (grid->param.initial_population.set_to_cc == true) { + +//#define DEBUG_INITIAL_POPULATIONS 1 +#ifdef DEBUG_INITIAL_POPULATIONS + const int32_t max_i = 10; + struct cats_vital_rate *cc_rate = &grid->param.carrying_capacity; + cats_dt_rates OT = grid->param.OT; + for (int32_t i = 0; i < max_i; i++) { + + cats_dt_rates suit = OT + i * (1.0 - OT)/max_i; + cats_dt_rates cc = cc_rate->func->func(cc_rate, &grid->param, suit, 0, NAN); + log_message(LOG_INFO, "Carrying capacity for suitability %Lf: %Lf", suit, cc); + + } + const cats_dt_coord rows = grid->dimension.rows; + const cats_dt_coord cols = grid->dimension.cols; + int64_t start = 0; + int64_t multi_excluded = 0; + + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { + + if (get_adult_population(grid, row, col) == 0) continue; + start += 1; + cats_dt_rates multiplier = 1.0; + if (conf->overlays.overlay[OL_HABITAT_TYPE_CC].enabled) { + multiplier *= conf->overlays.habitat_cc->data[row][col]; + } + cats_dt_rates suit = get_suitability(grid, row, col); + cats_dt_rates cc_raw = cc_rate->func->func(cc_rate, &grid->param, suit, 0, NAN); + if (multiplier == 0) { + multi_excluded += 1; + } + cats_dt_population cc = get_carrying_capacity(grid, row, col); + + printf("DEBUG::row %d col %d, suit %Lf, cc multi %Lf, cc raw %Lf, cc %d\n", row, col, suit, multiplier, cc_raw, cc); + } + } + + + +#endif + + increase_initial_population_to_cc(grid, conf); + int64_t populated_cells_after_cc = count_populated_cells(grid); + log_message(LOG_IMPORTANT, "\t%ld cells occupied after setting population sizes to carrying capacity", + populated_cells_after_cc); + } + + + if (grid->param.initial_population.suitability_threshold > 0.0) { + prune_initial_population_under_threshold(conf, grid); + } + + int64_t after_populated_cells = count_populated_cells(grid); + + log_message(LOG_IMPORTANT, "Pruned initial populations from %ld cells to %ld, (%ld wrong habitat)", + init_populated_cells, after_populated_cells, invalid_resources); + + + cats_dt_population rows = grid->dimension.rows; + cats_dt_population cols = grid->dimension.cols; + + threaded_action(&bf_initial_population_to_eggs, grid, conf, TS_DISPERSAL); + +#ifdef BF_DEBUG + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; +#endif + + + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { +#ifdef BF_DEBUG + if (get_adult_population(grid, row, col) || data->eggs[row][col]) { + printf("" + "BFDBG::%s::initial adults -> eggs::%d,%d,%d,%f\n", + __func__, row, col, get_adult_population(grid, row, col), + data->eggs[row][col]); + } +#endif + set_population_ignore_cc(grid, row, col, 0); + } + } + + + grid->param.initial_population.adjusted = true; +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_initial_population.h b/src/modules/butterflies/butterflies_initial_population.h new file mode 100644 index 0000000000000000000000000000000000000000..718f3cf48fa1380bc16b757767ea4107ceb4b04f --- /dev/null +++ b/src/modules/butterflies/butterflies_initial_population.h @@ -0,0 +1,5 @@ +#ifndef CATS_BUTTERFLIES_INITIAL_POPULATION_H +#define CATS_BUTTERFLIES_INITIAL_POPULATION_H +#include "data/cats_grid.h" +void bf_initial_population_adjustment(struct cats_configuration *conf, struct cats_grid *grid); +#endif //CATS_BUTTERFLIES_INITIAL_POPULATION_H diff --git a/src/modules/butterflies/butterflies_inline.h b/src/modules/butterflies/butterflies_inline.h new file mode 100644 index 0000000000000000000000000000000000000000..2551ab7c3d6894228886493398ed61fdeb0c9482 --- /dev/null +++ b/src/modules/butterflies/butterflies_inline.h @@ -0,0 +1,19 @@ +#ifndef CATS_BUTTERFLIES_INLINE_H +#define CATS_BUTTERFLIES_INLINE_H + +#include <math.h> +#include <stdbool.h> + +#include "butterflies_main.h" +#include "modules/module_header.h" + + +static inline bool bf_cell_excluded_by_generation(const struct cats_grid *grid, cats_dt_coord row, cats_dt_coord col) +{ + const int module_id = CATS_MODULE_ID; + const struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + if (data->generation_current == 0.0 || data->generation_current > (int32_t) ceilf(data->generations[row][col])) return true; + return false; +} + +#endif //CATS_BUTTERFLIES_INLINE_H diff --git a/src/modules/butterflies/butterflies_main.c b/src/modules/butterflies/butterflies_main.c new file mode 100644 index 0000000000000000000000000000000000000000..bd1ee7b29daa0262dc949bb5361af95ea7417ae0 --- /dev/null +++ b/src/modules/butterflies/butterflies_main.c @@ -0,0 +1,194 @@ +#include <math.h> +#include "modules/module_header.h" +#include "butterflies_main.h" +#include "configuration/load_configuration_species_params.h" +#include "module.h" +#include "actions/cats_actions.h" +#include "butterflies_actions.h" +#include "butterflies_vital_rates.h" +#include "cats_ini/cats_ini.h" +#include "butterflies_actions_setup.h" +#include "paths/output_paths.h" +#include "paths/directory_helper.h" +#include "butterflies_populations.h" +#include "butterflies_paths.h" +struct cats_global global; +struct cats_debug_options cats_debug; + +#include "lambda/leslie_matrix.h" +#include "actions/setup_actions.h" +#include "butterflies_scale.h" + + +cats_dt_rates bf_expected_local_eggs_per_female(struct cats_configuration *conf, struct cats_grid *grid, cats_dt_coord row, cats_dt_coord col) +{ + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + cats_dt_rates stationary = module_conf->probability_to_stay; + cats_dt_rates mobile = 1.0 - stationary; + cats_dt_rates egg_fraction_source = module_conf->egg_fraction_source; + cats_dt_rates eggs_per_female = calculate_rate(&module_conf->eggs_per_female, NAN, &grid->param, grid, row, col, NULL); + cats_dt_rates local_eggs = (stationary + mobile * egg_fraction_source ) * eggs_per_female; + return local_eggs; +} + + + +void *butterfly_grid_init(__attribute__((unused)) struct cats_configuration *conf, struct cats_grid *grid, + __attribute__((unused)) void *ignored) +{ + log_message(LOG_INFO, "%s: %s: grid init of grid %d with %d rows and %d cols", + module_name, __func__, grid->id, grid->dimension.cols, grid->dimension.rows); + + + struct grid_data_butterflies *data = malloc_or_die(sizeof(struct grid_data_butterflies)); + log_message(LOG_INFO, "allocating data for generations"); + data->generations = new_raw_2d_array_from_dimension(grid->dimension, sizeof(float)); + data->info_layer = new_raw_2d_array_from_dimension(grid->dimension, sizeof(int32_t)); + log_message(LOG_INFO, "done allocating data for generations"); + data->generation_current = 0; + + //struct conf_data_butterflies *conf_data = CATS_MODULE_DATA; + data->eggs = new_raw_2d_array_from_dimension(grid->dimension, sizeof(float)); + + if (grid->param.parametrization != PARAM_HYBRID) { + log_message(LOG_ERROR, "%s only works with hybrid parametrisation mode", module_name); + exit_cats(EXIT_FAILURE); + + } + + return data; +} + + +void * +butterfly_grid_cleanup(__attribute__((unused)) struct cats_configuration *conf, struct cats_grid *grid, void *data) +{ + log_message(LOG_INFO, "%s: grid cleanup", module_name); + assert(grid != NULL); + struct grid_data_butterflies *grid_data = data; + free_grid(&grid_data->generations, grid->dimension.rows); + free(grid_data->generations); + grid_data->generations = NULL; + free_grid(&grid_data->info_layer, grid->dimension.rows); + free(grid_data->info_layer); + grid_data->info_layer = NULL; + grid_data->generation_current = -1; + return NULL; +} + + +void load_butterflies_species_params(struct cats_configuration *conf, struct cats_ini *ini, const char *section_name, + struct cats_species_param *param) +{ + struct conf_data_butterflies *data = CATS_MODULE_DATA; + + load_conf_vital_rate(&data->eggs_per_female, conf, ini, section_name, param); + load_conf_vital_rate(&data->butterfly_generations, conf, ini, section_name, param); + load_conf_vital_rate(&data->reproduction_rate, conf, ini, section_name, param); + + + //load_conf_vital_rate(&data->butterfly_egg_to_adult_survival, conf, ini, section_name, param); + + load_conf_value(true, ini, section_name, "butterflies random walk steps maximum", + &data->animal_dispersal_max_radius); + load_conf_value(true, ini, section_name, "butterflies egg fraction source", &data->egg_fraction_source); + bool l = load_conf_value(false, ini, section_name, "butterflies egg fraction step", &data->egg_fraction_step); + + if (!l) { + data->egg_fraction_step = 0.5; + log_message(LOG_INFO, "using default value for butterflies egg fraction step: %Lf\n", + data->egg_fraction_step); + } + + l = load_conf_value(false, ini, section_name, "butterflies female fraction", &data->female_fraction); + + if (!l) { + data->female_fraction = 0.5; + log_message(LOG_INFO, "using default value for butterflies female fraction: %Lf\n", + data->female_fraction); + } + if (data->female_fraction <= 0.0 || data->female_fraction >= 1.0) { + log_message(LOG_ERROR, "butterflies female fraction must be in (0, 1), is %Lf", data->female_fraction); + exit_cats(EXIT_FAILURE); + } + + + if (data->egg_fraction_step <= 0.0 || data->egg_fraction_step > 1.0) { + log_message(LOG_ERROR, "butterflies egg fraction step must be in (0, 1], is %Lf", + data->egg_fraction_step); + exit_cats(EXIT_FAILURE); + } + + if (data->animal_dispersal_max_radius <= 0) { + log_message(LOG_ERROR, "butterflies random walk steps maximum must be > 0, is %d", + data->animal_dispersal_max_radius); + exit_cats(EXIT_FAILURE); + } + + data->debug_rw = false; + data->debug_rw_file = NULL; + data->debug_rw_filename = NULL; + load_conf_value(true, ini, section_name, "butterflies probability to stay", &data->probability_to_stay); + load_conf_value(false, ini, section_name, "butterflies debug random walks", &data->debug_rw); + + if (data->debug_rw) { + struct string_array *path = get_output_directory(conf, "debug"); + check_and_create_directory_if_needed(path); + struct string_array *name = new_string_array_init("rw-debug"); + string_array_add(name, conf->run_name); + data->debug_rw_filename = assemble_filename(path, name, "_", "csv"); + data->debug_rw_file = fopen(data->debug_rw_filename, "w"); + ENSURE_FILE_OPENED(data->debug_rw_file, data->debug_rw_filename); + free_string_array(&path); + free_string_array(&name); + + log_message(LOG_IMPORTANT, "Random walk debugging is enabled, reducing number of threads to one"); + conf->param_max_threads = 1; + } + + if (data->probability_to_stay < 0.0 || data->probability_to_stay > 1.0) { + log_message(LOG_ERROR, "butterflies probability to stay has to be in range [0, 1]"); + exit_cats(EXIT_FAILURE); + } + + data->generations_max = (int32_t) ceill(data->butterfly_generations.max_rate); + data->generations_min = (int32_t) ceill(data->butterfly_generations.min_rate); + param->plant_dispersal_max_radius = data->animal_dispersal_max_radius; + param->max_adult_cc_fraction = 1.0; + + + if (!data->actions_added) { + bf_register_actions(conf); + list_actions_full(conf); + log_message(LOG_EMPTY, " "); + bf_add_actions(conf); + log_message(LOG_EMPTY, " "); + + data->actions_added = true; + + + } + +} + + +int cats_module_init(struct cats_configuration *conf) +{ + struct conf_data_butterflies *data = calloc_or_die(1, sizeof(struct conf_data_butterflies)); + enum cats_module_flags flags = MODULE_ALTERNATE_DEMOGRAPHIC | MODULE_OVERRIDE_ACTIONS; + int32_t id = register_module(conf, module_name, data, flags); + register_cats_grid_init_function(conf, butterfly_grid_init, butterfly_grid_cleanup); + register_load_species_param_config_func(conf, load_butterflies_species_params); + register_create_leslie_matrix_func(conf, bf_leslie_matrix); + bf_add_vital_rates(conf, data); + + log_message(LOG_INFO, "Hello from '%s' (id: %d)", module_name, id); + + + for (enum butterfly_stats which = BF_STAT_MIN; which < BF_STAT_MAX; which++) { + data->stat_ids[which] = add_custom_stat(&conf->stats_registry, bf_get_stats_field_name(which)); + } + + bf_add_directories(conf); + return id; +} diff --git a/src/modules/butterflies/butterflies_main.h b/src/modules/butterflies/butterflies_main.h new file mode 100644 index 0000000000000000000000000000000000000000..e4d7655c3c9d7f81f3a04233e43708fc8b37a0c6 --- /dev/null +++ b/src/modules/butterflies/butterflies_main.h @@ -0,0 +1,60 @@ +#ifndef CATS_BUTTERFLIES_MAIN_H +#define CATS_BUTTERFLIES_MAIN_H + +#include "cats_global.h" +#include <stdint.h> +#include "data/cats_datatypes.h" +#include "vital_rates/vital_rates.h" +#include "butterflies_stats.h" + + +struct grid_data_butterflies { + float **generations; + int32_t generation_current; + float **eggs; + int32_t **info_layer; + + +}; + +enum butterfly_cell_info { + BF_CELL_CLEAR = 0, + BF_CELL_EXCLUDED = 1 << 1, + BF_CELL_HABITAT_OK = 1 << 2, + BF_CELL_RESOURCE_AVAILABLE = 1 << 3, + BF_CELL_VALID_DISPERSAL_TARGET = 1 << 4 + + +}; + +#define BF_DEBUG_ROW 4319 +#define BF_DEBUG_COL 10502 + +struct conf_data_butterflies { + + // fixme -> move to grid data; + + int32_t generations_max; + int32_t generations_min; + int32_t animal_dispersal_max_radius; ///< maximal flight/dispersal distance + cats_dt_rates probability_to_stay; + bool debug_rw; + FILE *debug_rw_file; + char *debug_rw_filename; + cats_dt_rates egg_fraction_source; + cats_dt_rates egg_fraction_step; + struct cats_vital_rate eggs_per_female; + struct cats_vital_rate butterfly_egg_to_adult_survival; + struct cats_vital_rate butterfly_generations; + struct cats_vital_rate reproduction_rate; + cats_dt_rates female_fraction; + + bool actions_added; + + int64_t stat_ids[BF_STAT_MAX]; + FILE *stats_file; + + +}; + +#endif //CATS_BUTTERFLIES_MAIN_H diff --git a/src/modules/butterflies/butterflies_overlays.c b/src/modules/butterflies/butterflies_overlays.c new file mode 100644 index 0000000000000000000000000000000000000000..704b4fb5a10c1c0b60c34bd24a5e5335ad2f190b --- /dev/null +++ b/src/modules/butterflies/butterflies_overlays.c @@ -0,0 +1,103 @@ +#include "paths/paths.h" +#include "butterflies_populations.h" +#include "modules/module_header.h" +#include "actions/cats_actions.h" + +#include "butterflies_overlays.h" +#include "butterflies_main.h" +#include "inline_overlays.h" +#include "inline_population.h" + + +enum action_status bf_grid_overlay_update(const struct cats_configuration *conf, struct cats_grid *grid) +{ + if (conf->overlays.have_overlays == false) { + return ACTION_NOT_RUN; + } + + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + + const cats_dt_coord rows = grid->dimension.rows; + const cats_dt_coord cols = grid->dimension.cols; + int64_t cells_habitat_ok = 0; + int64_t cells_excluded = 0; + int64_t cells_resource_ok = 0; + int64_t cells_habitat_and_resource_ok = 0; + int64_t cells_eggs_removed = 0; + int64_t cells_adults_removed = 0; + + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { + + data->info_layer[row][col] = BF_CELL_CLEAR; + + if (cell_excluded_by_overlay(conf, row, col)) { + data->info_layer[row][col] |= BF_CELL_EXCLUDED; + if (data->eggs[row][col]) cells_eggs_removed += 1; + if (get_adult_population(grid, row, col)) cells_adults_removed += 1; + data->eggs[row][col] = 0; + set_population_ignore_cc(grid, row, col, 0); + cells_excluded += 1; + continue; + } + + + if (conf->overlays.overlay[OL_HABITAT_TYPE_CC].enabled && + conf->overlays.habitat_cc->data[row][col] > 0) { + data->info_layer[row][col] |= BF_CELL_HABITAT_OK; + cells_habitat_ok += 1; + } else if (!conf->overlays.overlay[OL_HABITAT_TYPE_CC].enabled) { + data->info_layer[row][col] |= BF_CELL_HABITAT_OK; + cells_habitat_ok += 1; + } + + if (conf->overlays.overlay[OL_RESOURCE].enabled && + conf->overlays.resources->data[row][col] > 0) { + data->info_layer[row][col] |= BF_CELL_RESOURCE_AVAILABLE; + cells_resource_ok += 1; + } else if (!conf->overlays.overlay[OL_RESOURCE].enabled) { + data->info_layer[row][col] |= BF_CELL_RESOURCE_AVAILABLE; + cells_resource_ok += 1; + } + + if ((data->info_layer[row][col] & BF_CELL_HABITAT_OK) && + (data->info_layer[row][col] & BF_CELL_RESOURCE_AVAILABLE)) { + data->info_layer[row][col] |= BF_CELL_VALID_DISPERSAL_TARGET; + cells_habitat_and_resource_ok += 1; + + } else { + if (data->eggs[row][col]) cells_eggs_removed += 1; + if (get_adult_population(grid, row, col)) cells_adults_removed += 1; + data->eggs[row][col] = 0; + set_population_ignore_cc(grid, row, col, 0); + } + } + } + + int64_t cells_with_eggs = 0; + double total_eggs = 0; + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { + float eggs = data->eggs[row][col]; + if (eggs > 0) { + cells_with_eggs += 1; + total_eggs += eggs; + } + + } + } + + log_message(LOG_INFO, + "Overlay update: %ld excluded, %ld habitat ok, %ld resource ok, %ld habitat + resource ok", + cells_excluded, cells_habitat_ok, cells_resource_ok, cells_habitat_and_resource_ok); + log_message(LOG_INFO, "Overlay update: %ld cells with eggs, total %f eggs, average %f eggs per cell", + cells_with_eggs, total_eggs, total_eggs / (float) cells_with_eggs); + log_message(LOG_INFO, "Overlay update: %ld cells with eggs removed", cells_eggs_removed); + log_message(LOG_INFO, "Overlay update: %ld cells with adults removed", cells_adults_removed); + + + return ACTION_RUN; +} + diff --git a/src/modules/butterflies/butterflies_overlays.h b/src/modules/butterflies/butterflies_overlays.h new file mode 100644 index 0000000000000000000000000000000000000000..4d9b11970130425f3d71cbcc161e4c70646e1c72 --- /dev/null +++ b/src/modules/butterflies/butterflies_overlays.h @@ -0,0 +1,7 @@ +#ifndef CATS_BUTTERFLIES_OVERLAYS_H +#define CATS_BUTTERFLIES_OVERLAYS_H +#include "configuration/configuration.h" + +enum action_status bf_grid_overlay_update(const struct cats_configuration *conf, struct cats_grid *grid); + +#endif //CATS_BUTTERFLIES_OVERLAYS_H diff --git a/src/modules/butterflies/butterflies_paths.c b/src/modules/butterflies/butterflies_paths.c new file mode 100644 index 0000000000000000000000000000000000000000..29ca2fef3a387c16d5dab0fa6921aa7609f0dfc4 --- /dev/null +++ b/src/modules/butterflies/butterflies_paths.c @@ -0,0 +1,110 @@ +#include "butterflies_generations.h" +#include "paths/paths.h" +#include "paths/output_paths.h" +#include "butterflies_populations.h" +#include "inline_overlays.h" +#include "butterflies_main.h" +#include "butterflies_actions.h" +#include "modules/module_header.h" +#include "actions/cats_actions.h" +#include "butterflies_paths.h" +#include "module.h" + +void bf_add_directories(struct cats_configuration *conf) +{ + add_module_output_directory(conf, "butterfly-adults"); + if (conf->output.write_all) { + add_module_output_directory(conf,"butterfly-eggs"); + add_module_output_directory(conf,"butterfly-overlay"); + } + +} + +char *bf_population_filename(struct cats_configuration *conf, struct cats_grid *grid) +{ + assert(grid != NULL); + assert(conf != NULL); + assert(conf->grid_count == 1); + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + int32_t max_generation = module_conf->generations_max; + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + + struct string_array *path = get_output_directory(conf, "butterfly-adults"); // FIXME MAKE DIRECTORY + + char *extension = get_extension(conf, "adults"); + struct string_array *name = standard_output_file_name(conf, NULL, NULL, NULL); + string_array_add_int(name, max_generation - data->generation_current, "g%03d"); + + char *filename = assemble_filename(path, name, "_", extension); + + free_string_array(&path); + free_string_array(&name); + free(extension); + + return filename; +} + +char *bf_population_eggs_filename(struct cats_configuration *conf, struct cats_grid *grid) +{ + assert(grid != NULL); + assert(conf != NULL); + assert(conf->grid_count == 1); + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + int32_t max_generation = module_conf->generations_max; + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + + struct string_array *path = get_output_directory(conf, "butterfly-eggs"); // FIXME MAKE DIRECTORY + + char *extension = get_extension(conf, "eggs"); + struct string_array *name = standard_output_file_name(conf, NULL, NULL, NULL); + string_array_add_int(name, max_generation - data->generation_current, "g%03d"); + + char *filename = assemble_filename(path, name, "_", extension); + + free_string_array(&path); + free_string_array(&name); + free(extension); + + return filename; +} + + +char *bf_population_overlay_filename(struct cats_configuration *conf, struct cats_grid *grid) +{ + assert(grid != NULL); + assert(conf != NULL); + assert(conf->grid_count == 1); + + + struct string_array *path = get_output_directory(conf, "butterfly-overlay"); // FIXME MAKE DIRECTORY + + char *extension = get_extension(conf, "overlay"); + struct string_array *name = standard_output_file_name(conf, NULL, NULL, NULL); + char *filename = assemble_filename(path, name, "_", extension); + + free_string_array(&path); + free_string_array(&name); + free(extension); + + return filename; +} + + +char *bf_stats_filename(struct cats_configuration *conf, struct cats_grid *grid) +{ + struct string_array *path = get_output_directory(conf, "stats"); + + struct string_array *name = new_string_array(); + string_array_add(name, module_name); + string_array_add(name, conf->run_name); + string_array_add_int(name, conf->simulation.replicate, "r%03d"); + char *filename = assemble_filename(path, name, "_", "csv"); + free_string_array(&path); + free_string_array(&name); + + return filename; +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_paths.h b/src/modules/butterflies/butterflies_paths.h new file mode 100644 index 0000000000000000000000000000000000000000..a109363339cf3b5e428182be01ffd534304da700 --- /dev/null +++ b/src/modules/butterflies/butterflies_paths.h @@ -0,0 +1,10 @@ +#ifndef CATS_BUTTERFLIES_PATHS_H +#define CATS_BUTTERFLIES_PATHS_H + +#include "configuration/configuration.h" +char *bf_population_eggs_filename(struct cats_configuration *conf, struct cats_grid *grid); +char *bf_population_filename(struct cats_configuration *conf, struct cats_grid *grid); +char *bf_stats_filename(struct cats_configuration *conf, struct cats_grid *grid); +char *bf_population_overlay_filename(struct cats_configuration *conf, struct cats_grid *grid); +void bf_add_directories(struct cats_configuration *conf); +#endif //CATS_BUTTERFLIES_PATHS_H diff --git a/src/modules/butterflies/butterflies_populations.c b/src/modules/butterflies/butterflies_populations.c new file mode 100644 index 0000000000000000000000000000000000000000..07c3c6a1b099f7a7cba247ea7dad1c168e9790f8 --- /dev/null +++ b/src/modules/butterflies/butterflies_populations.c @@ -0,0 +1,202 @@ +#include <assert.h> +#include <math.h> + +#include "paths/paths.h" +#include "paths/output_paths.h" +#include "actions/cats_actions.h" +#include "misc/cats_random.h" +#include "data/cats_grid.h" +#include "threading/threading-helpers.h" +#include "modules/module_header.h" + +#include "butterflies_populations.h" +#include "butterflies_main.h" +#include "butterflies_inline.h" +#include "inline.h" +#include "populations/population.h" + +int64_t count_populated_cells(const struct cats_grid *grid) +{ + int64_t cells = 0; + const cats_dt_coord rows = grid->dimension.rows; + const cats_dt_coord cols = grid->dimension.cols; + + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { + if (get_adult_population(grid, row, col)) { + cells += 1; + } + } + } + + return cells; +} + + +int64_t butterflies_prune_invalid_cells(struct cats_grid *grid) +{ + int64_t invalid_habitat = 0; + const int module_id = CATS_MODULE_ID; + const struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + const cats_dt_coord rows = grid->dimension.rows; + const cats_dt_coord cols = grid->dimension.cols; + + for (cats_dt_coord row = 0; row < rows; row++) { + for (cats_dt_coord col = 0; col < cols; col++) { + + if (!(data->info_layer[row][col] & BF_CELL_VALID_DISPERSAL_TARGET)) { + if(get_adult_population(grid, row, col)) { + invalid_habitat += 1; + } + set_population_ignore_cc(grid, row, col, 0); + } + } + } + + return invalid_habitat; +} + + +cats_dt_rates +bf_egg_to_adult_survival_rate(cats_dt_rates reproduction_rate, cats_dt_rates eggs) +{ + return reproduction_rate / eggs; +} + +float get_fractional_generation(struct cats_grid *grid, cats_dt_coord row, cats_dt_coord col) +{ + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + const int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + float this_generation_fraction = 1.0f; + + float local_generation = data->generations[row][col]; + int32_t global_max_generation = module_conf->generations_max; + int32_t local_max_generation = (int32_t) ceilf(local_generation); + int32_t global_current_generation = data->generation_current; + float current_generation_float = (float) data->generation_current; + + if (global_current_generation == global_max_generation) { + this_generation_fraction = 1.0f; + } else if (current_generation_float == local_generation + || data->generation_current == module_conf->generations_min) { + this_generation_fraction = 1.0f; + } else if (data->generation_current == local_max_generation) { + this_generation_fraction = (float) local_max_generation - local_generation; + } + + assert(this_generation_fraction > 0); + assert(this_generation_fraction <= 1.0); + + return this_generation_fraction; +} + + +void bf_cell_maturation(struct cats_grid *grid, struct cats_thread_info *ts, cats_dt_coord row, cats_dt_coord col, + bool check_exclusion) +{ + + const struct cats_configuration *conf = ts->conf; + assert (conf->grid_count == 1); + if (check_exclusion) { + if (bf_cell_excluded_by_generation(grid, row, col)) return; + } + + + const int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + if (data->eggs[row][col] == 0) return; + if (data->eggs[row][col] < 0) { + log_message(LOG_ERROR, "Number of eggs < 0: row %d col %d: %f", row, col, data->eggs[row][col]); + exit(EXIT_FAILURE); + } + + + // The number of generations per cell is usually not an integer value: + // the non-integer part of the number of generations is used in the generation which is + // one greater than the integer part of the number of generations. + // i.e. if the number of generations is 2.4, 40% of all eggs in the cell + // will be considered as if they belonged to generation 3 + // the minimum number of generations is 1 if the cell is climatically viable (and the host species is present) + // if the number of generations is 0, the cell will be ignored + // + // note that the generations are processed backwards + // i.e. if the maximum number of generations is 5, individual cells will have 1 ... 5 generations + // first the all cells which have 5 (> 4) generations are modelled + // then all the cells with have at least 4 (> 3) generations are modelled + // ... + // at last all cells with have at least 1 generation are modelled + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + + float this_generation_fraction = get_fractional_generation(grid, row, col); + + + + //cats_dt_population max_cc = (cats_dt_population) grid->param.carrying_capacity.max_rate; + const float eggs = this_generation_fraction * data->eggs[row][col]; +#ifdef BF_DEBUG + printf("BFDBG::%s::row %d, col %d: local gen %f gen fraction %f, eggs %f, this gen eggs %f\n", + __func__, row, col, local_generation, this_generation_fraction, data->eggs[row][col], eggs); +#endif + if (eggs == 0) return; + + if (eggs > data->eggs[row][col]) { + log_message(LOG_ERROR, "Removing more eggs than present: %d %d: %f/%f", row, col, data->eggs[row][col], + eggs); + exit_cats(EXIT_FAILURE); + } + + data->eggs[row][col] -= eggs; + assert(data->eggs[row][col] >= 0); + + // not capped, we can have more adults than CC + + cats_dt_environment suit = get_suitability(grid, row, col); + cats_dt_rates reproduction_rate = calculate_rate(&module_conf->reproduction_rate, NAN, &grid->param, + grid, row, col, NULL); + if (suit < grid->param.OT && reproduction_rate > 0) { + log_message(LOG_ERROR, "Suitability %f under OT %Lf, but adults per female = %Lf", suit, grid->param.OT, + reproduction_rate); + exit_cats(EXIT_FAILURE); + } + + //printf("row %d col %d: suitability: %f, eggs: %f, reproduction rate %Lf\n", row, col, suit, eggs, reproduction_rate); + + if (reproduction_rate == 0) { + data->eggs[row][col] = 0; + set_population_ignore_cc(grid, row, col, 0); + return; + } + + cats_dt_rates expected_eggs = bf_expected_local_eggs_per_female(ts->conf, grid, row, col); + cats_dt_rates survival = bf_egg_to_adult_survival_rate(reproduction_rate, expected_eggs)/module_conf->female_fraction; + cats_dt_population adults = poisson(ts->rng, eggs * survival); + assert(adults >= 0); + set_population(grid, row, col, adults); +#ifdef BF_DEBUG + adults = get_adult_population(grid, row, col); + printf("BFDBG::%s::row %d, col %d: year %d, suitability %f, OT %Lf\n", __func__, row, col, conf->time.year_current, suit, grid->param.OT); + printf("BFDBG::%s::row %d, col %d: year %d, eggs %f, survival rate %Lf, reproduction rate %Lf, adults %d\n", + __func__, row, col, conf->time.year_current, eggs, survival, reproduction_rate, adults); + printf("XXXX,%d,%f,%d\n", conf->time.year_current, current_generation_float,adults); +#endif + + +} + + +void bf_area_kill_adults(struct cats_grid *grid, struct cats_thread_info *ts) +{ + const cats_dt_coord start_row = ts->area.start_row; + const cats_dt_coord end_row = ts->area.end_row; + const cats_dt_coord start_col = ts->area.start_col; + const cats_dt_coord end_col = ts->area.end_col; + + for (cats_dt_coord row = start_row; row < end_row; row++) { + for (cats_dt_coord col = start_col; col < end_col; col++) { + set_population_ignore_cc(grid, row, col, 0); + } + } +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_populations.h b/src/modules/butterflies/butterflies_populations.h new file mode 100644 index 0000000000000000000000000000000000000000..894fce21b4a0c8315181dc58ce50c016bf901cdb --- /dev/null +++ b/src/modules/butterflies/butterflies_populations.h @@ -0,0 +1,14 @@ +// +// Created by gattringera on 21/11/22. +// + +#ifndef CATS_BUTTERFLIES_POPULATIONS_H +#define CATS_BUTTERFLIES_POPULATIONS_H +#include "data/cats_grid.h" +int64_t count_populated_cells(const struct cats_grid *grid); +void bf_cell_maturation(struct cats_grid *grid, struct cats_thread_info *ts, cats_dt_coord row, cats_dt_coord col, bool check_exclusion); +void bf_area_kill_adults(struct cats_grid *grid, struct cats_thread_info *ts); +cats_dt_rates bf_egg_to_adult_survival_rate(cats_dt_rates reproduction_rate, cats_dt_rates eggs); +cats_dt_rates bf_expected_local_eggs_per_female(struct cats_configuration *conf, struct cats_grid *grid, cats_dt_coord row, cats_dt_coord col); +int64_t butterflies_prune_invalid_cells(struct cats_grid *grid); +#endif //CATS_BUTTERFLIES_POPULATIONS_H diff --git a/src/modules/butterflies/butterflies_scale.c b/src/modules/butterflies/butterflies_scale.c new file mode 100644 index 0000000000000000000000000000000000000000..53627e85d7c4126c8695708abea9bf05f83a4793 --- /dev/null +++ b/src/modules/butterflies/butterflies_scale.c @@ -0,0 +1,37 @@ +// +// Created by andreas on 03/07/23. +// +#include "modules/module_header.h" +#include "butterflies_main.h" +#include "butterflies_scale.h" +#include "butterflies_populations.h" + + +double *bf_leslie_matrix(struct cats_configuration *conf, struct lambda_parameters *l_param, + bool silent, int32_t *N_out){ + bool print_rate = !silent; + + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + cats_dt_rates eggs_per_female = calculate_rate_for_matrix(&module_conf->eggs_per_female, l_param, print_rate); + cats_dt_rates reproduction_rate = calculate_rate_for_matrix(&module_conf->reproduction_rate, l_param, print_rate); + cats_dt_rates stationary = module_conf->probability_to_stay; + cats_dt_rates mobile = 1.0 - stationary; + cats_dt_rates egg_fraction_source = module_conf->egg_fraction_source; + + cats_dt_rates local_eggs = (stationary + mobile * egg_fraction_source ) * eggs_per_female; + + // female -> female + // to achieve the target reproduction rate, the number of eggs per female laid in the cell + // that survive and become adult has to be the reproduction rate divided by the female fraction divided by the number of eggs + + cats_dt_rates eggs_to_adults_rate = bf_egg_to_adult_survival_rate(reproduction_rate, local_eggs) ; + cats_dt_rates result = local_eggs * eggs_to_adults_rate; + + printf("scale %Lf: eggs %Lf, eggs to adults %Lf\n", conf->param->scale_factor, eggs_per_female, eggs_to_adults_rate); + double *matrix = calloc_or_die(1, sizeof(double)); + + *matrix = (double) result; + *N_out = 1; + return matrix; + +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_scale.h b/src/modules/butterflies/butterflies_scale.h new file mode 100644 index 0000000000000000000000000000000000000000..3dbcd1d911a04e928e5159901818982804dfc5da --- /dev/null +++ b/src/modules/butterflies/butterflies_scale.h @@ -0,0 +1,16 @@ +// +// Created by andreas on 03/07/23. +// + +#ifndef CATS_BUTTERFLIES_SCALE_H +#define CATS_BUTTERFLIES_SCALE_H + +#include <stdbool.h> +#include <stdint.h> +#include "configuration/configuration.h" +#include "lambda/leslie_matrix.h" + +double *bf_leslie_matrix(struct cats_configuration *conf, struct lambda_parameters *l_param, + bool silent, int32_t *N_out); + +#endif //CATS_BUTTERFLIES_SCALE_H diff --git a/src/modules/butterflies/butterflies_stats.c b/src/modules/butterflies/butterflies_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..20925e9467b81c86abd65d121e3b86430ea1150f --- /dev/null +++ b/src/modules/butterflies/butterflies_stats.c @@ -0,0 +1,185 @@ + +#include "butterflies_stats.h" +#include "logging.h" +#include "cats_global.h" +#include "data/cats_grid.h" +#include "modules/module_header.h" +#include "butterflies_main.h" +#include "inline_overlays.h" +#include "inline_population.h" +#include "inline.h" +#include "butterflies_paths.h" +#include "temporal/phase_names.h" +#include "temporal/years.h" + +const char *bf_get_stats_field_name(enum butterfly_stats which) +{ + switch (which) { + case BF_STAT_POPULATED_FIT: + return "populated_fit"; + case BF_STAT_POPULATED_UNFIT: + return "populated_unfit"; + case BF_STAT_UNPOPULATED_FIT: + return "unpopulated_fit"; + case BF_STAT_UNPOPULATED_UNFIT: + return "unpopulated_unfit"; + case BF_STAT_EXCLUDED: + return "excluded"; + case BF_STAT_GENERATION_ACTIVE: + return "generation_active"; + case BF_POPULATED_AT_DISPERSAL: + return "populated_at_dispersal"; + + case BF_RANDOM_WALK_COUNT: + return "random_walk_count"; + + case BF_RANDOM_WALK_STEP_COUNT: + return "random_walk_step_count"; + case BF_OUTPUT_STAT_MAX: + return "<guard value>"; + + case BF_STAT_MAX: + break; + case BF_CELLS_WITH_EGGS: + return "cells with eggs"; + case BF_CELLS_WITH_EGGS_REMOVED: + return "removed cells with eggs"; + + } + + log_message(LOG_ERROR, "unknown butterfly stats name with id %d", which); + exit_cats(EXIT_FAILURE); +} + +struct string_array *bf_assemble_stats(struct cats_configuration *conf, struct cats_grid *grid, bool header) +{ + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + assert(module_conf->stats_file != NULL); + const int32_t grid_id = grid->id; + int module_id = CATS_MODULE_ID; + struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data; + + + struct string_array *x = new_string_array(); + + if (header) { + + string_array_add(x, "phase"); + string_array_add(x, "year"); + string_array_add(x, "run"); + string_array_add(x, "replicate"); + string_array_add(x, "species"); + string_array_add(x, "id"); + string_array_add(x, "generation"); + + for (enum butterfly_stats which = BF_STAT_MIN; which < BF_OUTPUT_STAT_MAX; which++) { + const char *name = bf_get_stats_field_name(which); + string_array_add(x, name); + } + } else { + string_array_add(x, get_phase_shortname(conf->time.phase)); + string_array_add_int64(x, get_phase_year_abs(conf), NULL); + string_array_add(x, conf->run_name); + string_array_add_int64(x, conf->simulation.replicate, NULL); + string_array_add(x, conf->param[grid_id].species_name); + string_array_add_int64(x, grid_id, NULL); + string_array_add_int(x, data->generation_current, NULL); + + for (enum butterfly_stats which = BF_STAT_MIN; which < BF_OUTPUT_STAT_MAX; which++) { + int64_t stat_id = module_conf->stat_ids[which]; + string_array_add_int64(x, grid->stats.custom_stats[stat_id], NULL); + } + + } + + + return x; + +} + + +void bf_stats_write(struct cats_configuration *conf, struct cats_grid *grid) +{ + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + struct string_array *data = NULL; + + if (module_conf->stats_file == NULL) { + char *fn = bf_stats_filename(conf, grid); + module_conf->stats_file = fopen(fn, "w"); + ENSURE_FILE_OPENED(module_conf->stats_file, fn); + free(fn); + data = bf_assemble_stats(conf, grid, true); + + char *string = string_array_paste(data, ","); + fprintf(module_conf->stats_file, "%s\n", string); + free(string); + + free_string_array(&data); + + } + + data = bf_assemble_stats(conf, grid, false); + char *string = string_array_paste(data, ","); + fprintf(module_conf->stats_file, "%s\n", string); + free(string); + free_string_array(&data); +} + +void bf_area_stats_gather(struct cats_grid *grid, struct cats_thread_info *ts) +{ + struct conf_data_butterflies *module_conf = CATS_MODULE_DATA; + struct cats_configuration *conf = ts->conf; + const int64_t id_excluded = module_conf->stat_ids[BF_STAT_EXCLUDED]; + + const int64_t id_pop_fit = module_conf->stat_ids[BF_STAT_POPULATED_FIT]; + const int64_t id_pop_unfit = module_conf->stat_ids[BF_STAT_POPULATED_UNFIT]; + + const int64_t id_unpop_fit = module_conf->stat_ids[BF_STAT_UNPOPULATED_FIT]; + const int64_t id_unpop_unfit = module_conf->stat_ids[BF_STAT_UNPOPULATED_UNFIT]; + const int32_t grid_id = grid->id; + + //const cats_dt_rates zt = grid->param.ZT; + const cats_dt_rates ot = grid->param.OT; + struct statistics *stats = &ts->stats[grid_id]; + + const cats_dt_coord start_row = ts->area.start_row; + const cats_dt_coord end_row = ts->area.end_row; + const cats_dt_coord start_col = ts->area.start_col; + const cats_dt_coord end_col = ts->area.end_col; + + for (cats_dt_coord row = start_row; row < end_row; row++) { + for (cats_dt_coord col = start_col; col < end_col; col++) { + cats_dt_population pop = get_adult_population(grid, row, col); + + if (cell_excluded_by_overlay(conf, row, col)) { + stats->custom_stats[id_excluded] += 1; + if (pop > 0) { + log_message(LOG_ERROR, "%s: population > 0 (%d) in excluded cell %d %d", + __func__, pop, row, col); + exit_cats(EXIT_FAILURE); + } + continue; + } + + cats_dt_environment suit = get_suitability(grid, row, col); + + if (pop > 0) { + if (suit >= ot) { + stats->custom_stats[id_pop_fit] += 1; + + } else { + stats->custom_stats[id_pop_unfit] += 1; + } + } else { + if (suit >= ot) { + stats->custom_stats[id_unpop_fit] += 1; + + } else { + stats->custom_stats[id_unpop_unfit] += 1; + } + + } + } + } + +} \ No newline at end of file diff --git a/src/modules/butterflies/butterflies_stats.h b/src/modules/butterflies/butterflies_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..09223497053184c8829e09838263aa2431d1a5f8 --- /dev/null +++ b/src/modules/butterflies/butterflies_stats.h @@ -0,0 +1,40 @@ + +#ifndef CATS_BUTTERFLIES_STATS_H +#define CATS_BUTTERFLIES_STATS_H + +#include <assert.h> +#include "stats/statistics.h" +#include "threading/threading.h" + +enum butterfly_stats { + BF_STAT_MIN = 0, + BF_STAT_POPULATED_FIT = 0, + BF_STAT_POPULATED_UNFIT, + BF_STAT_UNPOPULATED_FIT, + BF_STAT_UNPOPULATED_UNFIT, + BF_STAT_EXCLUDED, + BF_STAT_GENERATION_ACTIVE, + BF_OUTPUT_STAT_MAX, + BF_POPULATED_AT_DISPERSAL, + BF_RANDOM_WALK_COUNT, + BF_RANDOM_WALK_STEP_COUNT, + BF_CELLS_WITH_EGGS, + BF_CELLS_WITH_EGGS_REMOVED, + BF_STAT_MAX +}; + +const char *bf_get_stats_field_name(enum butterfly_stats which); +void bf_area_stats_gather(struct cats_grid *grid, struct cats_thread_info *ts); +void bf_stats_write(struct cats_configuration *conf, struct cats_grid *grid); + +static inline void increase_custom_stat(struct statistics *stats, int64_t stat_id, int64_t by) +{ + assert(stats != NULL); + assert(stat_id >= 0 && stat_id < stats->custom_stat_count); + stats->custom_stats[stat_id] += by; + +} + + + +#endif //CATS_BUTTERFLIES_STATS_H diff --git a/src/modules/butterflies/butterflies_vital_rates.c b/src/modules/butterflies/butterflies_vital_rates.c new file mode 100644 index 0000000000000000000000000000000000000000..ed0e54a694dff3bdad96e4586799013d5889137c --- /dev/null +++ b/src/modules/butterflies/butterflies_vital_rates.c @@ -0,0 +1,26 @@ +#include "butterflies_vital_rates.h" + +void bf_add_vital_rates(struct cats_configuration *conf, struct conf_data_butterflies *data) +{ + // number of generations + register_module_vital_rate(conf, &data->butterfly_generations,"butterflies generations"); + set_vital_rate_name(&data->butterfly_generations, "butterflies generations"); + set_vital_rate_suitability_cutoff_hint(&data->butterfly_generations, HYBRID_SUIT_TS_ZT); + set_vital_rate_link_hybrid_function(&data->butterfly_generations, conf, LINK_SUITABILITY_SIGMOID); + set_vital_density(&data->butterfly_generations, NO_DENSITY_DEP); + set_vital_rate_minimum(&data->butterfly_generations, 1.0); + + + + // adult to eggs + register_module_vital_rate(conf, &data->eggs_per_female, "butterflies eggs per female"); + set_vital_rate_name(&data->eggs_per_female, "butterflies eggs per female"); + set_vital_rate_suitability_cutoff_hint(&data->eggs_per_female, HYBRID_SUIT_TS_ZT); + set_vital_rate_link_hybrid_function(&data->eggs_per_female, conf, LINK_CONSTANT); + + // eggs to adults + register_module_vital_rate(conf, &data->reproduction_rate, "butterflies reproduction rate"); + set_vital_rate_name(&data->reproduction_rate, "butterflies reproduction rate"); + set_vital_rate_link_hybrid_function(&data->reproduction_rate, conf, LINK_SUITABILITY_SIGMOID); + set_vital_rate_suitability_cutoff_hint(&data->reproduction_rate, HYBRID_SUIT_TS_ZT); +} diff --git a/src/modules/butterflies/butterflies_vital_rates.h b/src/modules/butterflies/butterflies_vital_rates.h new file mode 100644 index 0000000000000000000000000000000000000000..1a1d097bc9b7e5b57376102937c6a8cc34f3d99c --- /dev/null +++ b/src/modules/butterflies/butterflies_vital_rates.h @@ -0,0 +1,10 @@ + +#ifndef CATS_BUTTERFLIES_VITAL_RATES_H +#define CATS_BUTTERFLIES_VITAL_RATES_H +#include "configuration/configuration.h" +#include "butterflies_main.h" +#include "vital_rates/setup_rates.h" +#include "modules/module_header.h" + +void bf_add_vital_rates(struct cats_configuration *conf, struct conf_data_butterflies *data) ; +#endif //CATS_BUTTERFLIES_VITAL_RATES_H diff --git a/src/modules/butterflies/module.h b/src/modules/butterflies/module.h new file mode 100644 index 0000000000000000000000000000000000000000..6da8133282863e8ada0fc405c2893b82d98540de --- /dev/null +++ b/src/modules/butterflies/module.h @@ -0,0 +1,6 @@ +#ifndef CATS_MODULE_H +#define CATS_MODULE_H + +static const char *module_name = "cats-butterflies"; + +#endif //CATS_MODULE_H diff --git a/src/modules/cats_test_module/register_module.c b/src/modules/cats_test_module/register_module.c index 7e12973861abfb7036f210e2db19e50bbd2aab72..c97616d35e85e56ecbdeb33dfc15df8e1af0b889 100644 --- a/src/modules/cats_test_module/register_module.c +++ b/src/modules/cats_test_module/register_module.c @@ -59,7 +59,7 @@ void cats_module_init(struct cats_configuration *conf) enum cats_module_flags flags = MODULE_NO_FLAGS; int32_t id = register_module(conf, module_name, data, flags); register_cats_grid_init_function(conf, grid_init, grid_cleanup); - log_message(LOG_INFO, "Hello from %s (id: %d)\n", module_name, id); + log_message(LOG_INFO, "Hello from '%s' (id: %d)", module_name, id); greeting2(); }