Skip to content

Commit

Permalink
Release version 1.1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolfgang Traylor committed Jul 27, 2021
2 parents ec4347e + ac182c1 commit 86ec3c0
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 90 deletions.
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ The format is based on [Keep a Changelog][] by Olivier Lacan, and this project a
[Keep a Changelog]: <https://keepachangelog.com/en/1.0.0/>
[Semantic Versioning]: <https://semver.org/spec/v2.0.0.html>

## [1.1.2] - 2021-07-27

### Added
- `demo_results.Rmd` now only tries to plot those files that are available [#39]

### Fixed
- Allow `simulation.one_hft_per_habitat` only for `herbivore_type = "Cohort"`
- Issue that only one HFT got created and outputted if `simulation.one_hft_per_habitat = true`

## [1.1.1] - 2021-07-26

### Fixed
Expand Down Expand Up @@ -239,8 +248,9 @@ The format is based on [Keep a Changelog][] by Olivier Lacan, and this project a
[Illius & O’Connor (2000)]: <https://doi.org/10.2307/3800911>
[Taylor et al. (1981)]: <https://doi.org/10.1017/S0003356100040617>

[Unreleased]: https://github.com/wtraylor/modular_megafauna_model/compare/1.1.0...develop
[1.1.1]: https://github.com/wtraylor/modular_megafauna_model/compare/1.1.0...1.1.0
[Unreleased]: https://github.com/wtraylor/modular_megafauna_model/compare/1.1.2...develop
[1.1.2]: https://github.com/wtraylor/modular_megafauna_model/compare/1.1.1...1.1.2
[1.1.1]: https://github.com/wtraylor/modular_megafauna_model/compare/1.1.0...1.1.1
[1.1.0]: https://github.com/wtraylor/modular_megafauna_model/compare/1.0.3...1.1.0
[1.0.3]: https://github.com/wtraylor/modular_megafauna_model/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/wtraylor/modular_megafauna_model/compare/1.0.1...1.0.2
Expand Down Expand Up @@ -280,6 +290,7 @@ The format is based on [Keep a Changelog][] by Olivier Lacan, and this project a
[#31]: https://github.com/wtraylor/modular_megafauna_model/issues/31
[#32]: https://github.com/wtraylor/modular_megafauna_model/issues/32
[#34]: https://github.com/wtraylor/modular_megafauna_model/issues/34
[#39]: https://github.com/wtraylor/modular_megafauna_model/issues/39
[#41]: https://github.com/wtraylor/modular_megafauna_model/issues/41
[#43]: https://github.com/wtraylor/modular_megafauna_model/issues/43
[#44]: https://github.com/wtraylor/modular_megafauna_model/issues/44
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ authors:
affiliation: Senckenberg Biodiversity and Climate Research Centre
orcid: https://orcid.org/0000-0003-4925-7248
title: "Modular Megafauna Model"
version: 1.1.1
version: 1.1.2
# This DOI represents all versions, and will always resolve to the latest one.
doi: 10.5281/zenodo.4710254
date-released: 2021-07-26
date-released: 2021-07-27
license: LGPL-3.0-or-later
repository-code: https://github.com/wtraylor/modular_megafauna_model
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ cmake_minimum_required (VERSION 3.10)
project ("Modular Megafauna Model"
# This version must match exactly the Git tag!
# Compare the "check_version" job in ".gitlab-ci.yml".
VERSION 1.1.1
VERSION 1.1.2
DESCRIPTION "A physiological, dynamic herbivore simulator in C++."
LANGUAGES CXX
)
Expand Down
4 changes: 2 additions & 2 deletions codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"codeRepository": "git+https://github.com/wtraylor/modular_megafauna_model.git",
"contIntegration": "https://gitlab.com/wtraylor/modular_megafauna_model/pipelines",
"datePublished": "2021-04-26",
"dateModified": "2021-07-26",
"dateModified": "2021-07-27",
"issueTracker": "https://github.com/wtraylor/modular_megafauna_model/issues/",
"name": "Modular Megafauna Model (MMM)",
"version": "1.1.1",
"version": "1.1.2",
"identifier": "10.5281/zenodo.4710254. ",
"description": "Extendable, dynamic, process-based herbivore simulator that can be integrated into a dynamic vegetation model. Written as a C++ library.\n",
"applicationCategory": "Ecological Modeling",
Expand Down
15 changes: 2 additions & 13 deletions include/Fauna/world.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,6 @@ class World {
World(const std::shared_ptr<const Parameters> params,
const std::shared_ptr<const HftList> hftlist);

/// Constructor: Create deactivated `World` object.
/**
* Even if the megafauna model should be completely deactivated, there might
* be a need to create a "dummy" \ref World instance. An object created with
* this constructor will not simulate anything.
*/
World();

/// Default destructor.
~World();

Expand Down Expand Up @@ -115,9 +107,6 @@ class World {
*/
const std::list<SimulationUnit>& get_sim_units() const { return sim_units; }

/// Whether this \ref World object has been created with parameters and HFTs.
const bool is_activated() const { return activated; }

/// Iterate through all simulation units and perform simulation for this day.
/**
* This is the central access point to start the perform the herbivore
Expand Down Expand Up @@ -166,8 +155,8 @@ class World {
*/
Output::WriterInterface* construct_output_writer() const;

/// Whether the whole model is active or not.
const bool activated;
/// Whether this object is going to simulate or just lint an instruction file.
const SimMode mode = SimMode::Simulate;

/// Whether the habitat counts per aggregation unit have been checked.
/**
Expand Down
24 changes: 13 additions & 11 deletions src/Fauna/Output/combined_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,25 @@ CombinedData& CombinedData::merge(const CombinedData& other) {
// HERBIVORE DATA
// Merge herbivore data for each output group.

// Groups can be for instance HFTs.
typedef std::map<const std::string, HerbivoreData> GroupMap;

// First, create empty HerbivoreData objects for all groups that are not
// yet present in this object.
for (const auto& itr : hft_data) {
// create new object if group not found.
// First, create empty HerbivoreData objects for all HFTs/groups from the
// *other* object that are not yet present in *this* object.
for (const auto& itr : other.hft_data) {
// Create new, empty object if the group is new to this object.
if (!hft_data.count(itr.first)) hft_data[itr.first] = HerbivoreData();
}

// Second, merge all herbivore data.
// Now we have all groups in this->hft_data, and we can merge for each
// group/HFT.
for (auto& itr : hft_data) {
// try to find this group in the other object
GroupMap::const_iterator found = other.hft_data.find(itr.first);

// If the other object doesn’t contain this HFT, we use an empty
// object.
HerbivoreData other_herbi_data;
// If an HFT/group in this->hft_data is not in other.hft_data, we create a
// temporary empty HerbivoreData object.
const auto& found = other.hft_data.find(itr.first);
HerbivoreData other_herbi_data; // Temporary empty object for now.
// If we did find the HFT/group in the other object, we use that for
// merging.
if (found != other.hft_data.end()) other_herbi_data = found->second;

// Let the class HerbivoreData do the actual merge.
Expand Down
28 changes: 28 additions & 0 deletions src/Fauna/Output/combined_data.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ TEST_CASE("Fauna::Output::CombinedData") {
// merge with equal weight
c1.datapoint_count = c2.datapoint_count = 3;

// Merge c2 into c1. The HFTs in c2 are a *subset* of those in c1.
c1.merge(c2);

CHECK(c1.datapoint_count == 6);
Expand All @@ -90,6 +91,33 @@ TEST_CASE("Fauna::Output::CombinedData") {
Approx(3.0)); // weighted by inddens, but only one data point
}

// This is the same as before, but merging the other way round.
SECTION("merge() and create new HFT record") {
// merge with equal weight
c1.datapoint_count = c2.datapoint_count = 3;

// Merge c1 into c2. Now there is one new HFT coming from c1.
c2.merge(c1);

CHECK(c2.datapoint_count == 6);

CHECK(c2.habitat_data.available_forage.grass.get_mass() == Approx(1.5));
// c1 now has data for both HFTs
REQUIRE(c2.hft_data.size() == 2);
// in HFT 0, two datapoints are merged
CHECK(c2.hft_data[hfts[0].get()->name].inddens ==
Approx((1.0 + 2.0) / 2.0)); // normal average
CHECK(
c2.hft_data[hfts[0].get()->name].expenditure ==
Approx((1.0 * 1.0 + 2.0 * 2.0) / (1.0 + 2.0))); // weighted by inddens

// in HFT 1, one datapoint is merged with zero values
CHECK(c2.hft_data[hfts[1].get()->name].inddens ==
Approx((0.0 + 3.0) / 2.0)); // normal average
CHECK(c2.hft_data[hfts[1].get()->name].expenditure ==
Approx(3.0)); // weighted by inddens, but only one data point
}

SECTION("merge() with different data point counts") {
c1.datapoint_count = 1;
c2.datapoint_count = 2;
Expand Down
7 changes: 7 additions & 0 deletions src/Fauna/parameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ bool Parameters::is_valid(std::string& messages) const {
//------------------------------------------------------------
// add new checks in alphabetical order

if (one_hft_per_habitat && (herbivore_type != HerbivoreType::Cohort)) {
stream << "'simulation.one_hft_per_habitat' is only defined for "
"'simulation.herbivore_type = \"cohort\"'."
<< std::endl;
is_valid = false;
}

if (herbivore_establish_interval < 0) {
stream << "herbivore_establish_interval must be >=0" << std::endl;
is_valid = false;
Expand Down
38 changes: 19 additions & 19 deletions src/Fauna/world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,27 @@ Output::WriterInterface* World::construct_output_writer() const {
}

World::World(const std::string instruction_filename, const SimMode mode)
: activated(true),
: mode(mode),
insfile(read_instruction_file(instruction_filename)),
days_since_last_establishment(get_params().herbivore_establish_interval),
output_aggregator(new Output::Aggregator()),
output_writer(mode == SimMode::Lint ? NULL : construct_output_writer()),
world_constructor(new WorldConstructor(insfile.params, get_hfts())) {}

World::World() : activated(false) {}

World::World(const std::shared_ptr<const Parameters> params,
const std::shared_ptr<const HftList> hftlist)
: activated(true),
insfile({hftlist, params}),
: insfile({hftlist, params}),
days_since_last_establishment(get_params().herbivore_establish_interval),
output_aggregator(new Output::Aggregator()),
output_writer(construct_output_writer()),
world_constructor(new WorldConstructor(insfile.params, get_hfts())) {}
world_constructor(new WorldConstructor(insfile.params, get_hfts())) {
if (params.get() == NULL)
throw std::invalid_argument(
"Fauna::World::World() The argument 'params' is NULL.");
if (hftlist.get() == NULL)
throw std::invalid_argument(
"Fauna::World::World() The argument 'hftlist' is NULL.");
}

// The destructor must be implemented here in the source file, where the
// forward-declared types are complete.
Expand All @@ -71,14 +75,16 @@ void World::create_simulation_unit(std::shared_ptr<Habitat> habitat) {
if (habitat == NULL)
throw std::invalid_argument(
"World::create_simulation_unit(): Pointer to habitat is NULL.");
if (!activated) return;
if (mode != SimMode::Simulate) return;

int habitat_ctr = 0;
// Find the number of habitats already created in this aggregation unit.
for (const auto& sim_unit : sim_units)
if (sim_unit.get_habitat().get_aggregation_unit() ==
habitat->get_aggregation_unit())
habitat_ctr++;
const std::string this_agg_unit(habitat->get_aggregation_unit());
for (const auto& sim_unit : sim_units) {
const std::string existing_agg_unit(
sim_unit.get_habitat().get_aggregation_unit());
if (this_agg_unit == existing_agg_unit) habitat_ctr++;
}
PopulationList* populations =
world_constructor->create_populations(habitat_ctr);
assert(populations);
Expand All @@ -100,11 +106,6 @@ const HftList& World::get_hfts() const {
}

const Parameters& World::get_params() const {
if (!activated)
throw std::logic_error(
"Fauna::World::get_params() "
"The megafauna model was created without an instruction file. "
"Parameters are not available.");
if (!insfile.params)
throw std::logic_error(
"Fauna::World::get_params() "
Expand Down Expand Up @@ -164,14 +165,15 @@ World::InsfileContent World::read_instruction_file(
}

void World::simulate_day(const Date& date, const bool do_herbivores) {
if (!activated) return;
if (mode != SimMode::Simulate) return;

// Sanity checks for simulation units
if (!simulation_units_checked) {
// Count the number of habitats in any case to throw an exception if they
// differ. Compare the habitat count against HFT count only if necessary.
const int habitat_count = get_habitat_count_per_agg_unit();
if (get_params().one_hft_per_habitat &&
(get_params().herbivore_type == HerbivoreType::Cohort) &&
(habitat_count % get_hfts().size() != 0))
throw std::logic_error(
"Fauna::World::simulate_day() "
Expand Down Expand Up @@ -230,8 +232,6 @@ void World::simulate_day(const Date& date, const bool do_herbivores) {
if (do_herbivores) days_since_last_establishment++;

// Create function object to delegate all simulations for this day to.
// TODO: Create function object only once per day and for all simulation
// units.
SimulateDay simulate_day(date.get_julian_day(), sim_unit, feed_herbivores);

// Call the function object.
Expand Down
Loading

0 comments on commit 86ec3c0

Please sign in to comment.