Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions plugins/planthydraulics/doc/PlantHydraulics.dox
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,13 @@ int main() {
float psi_soil = hydraulics.getSoilWaterPotential(leafUUID);
float psi_root = hydraulics.getRootWaterPotential(leafUUID);
float psi_stem = hydraulics.getStemWaterPotential(leafUUID);
float psi_leaf;
context.getPrimitiveData(leafUUID,"water_potential",psi_leaf);
std::cout << "Leaf UUID: " << leafUUID
<< ", Soil Water Potential: " << psi_soil
<< ", Root Water Potential: " << psi_root
<< ", Stem Water Potential: " << psi_stem
<< ", Leaf Water Potential: " << psi_leaf
<< std::endl;
}
}
Expand Down Expand Up @@ -252,12 +255,55 @@ int main() {
float psi_soil = hydraulics.getSoilWaterPotential(leafUUID);
float psi_root = hydraulics.getRootWaterPotential(leafUUID);
float psi_stem = hydraulics.getStemWaterPotential(leafUUID);
float psi_leaf;
context.getPrimitiveData(leafUUID,"water_potential",psi_leaf);
std::cout << "Leaf UUID: " << leafUUID
<< ", Soil Water Potential: " << psi_soil
<< ", Root Water Potential: " << psi_root
<< ", Stem Water Potential: " << psi_stem
<< ", Leaf Water Potential: " << psi_leaf
<< std::endl;
}
}
\endcode

Any vector of leaf primitives can grouped into a plant that share a plantID, soil, root, and stem water potentials, without
the use of the Plant Architecture or other plug-in. The function to do so is \ref PlantHydraulicsModel::groupPrimitivesIntoPlantObject( const std::vector<uint> &UUIDs )
as demonstrated below.

\code{.cpp}
#include "PlantHydraulicsModel.h"
using namespace helios;

int main() {
Context context;

// Add leaf 'Patch' primitives
vec3 center = make_vec3(0,0,1);
vec2 size = make_vec2(1,1);
uint leaf1 = context.addPatch( center, size );
uint leaf2 = context.addPatch( center, size );

std::vector<uint> leaves = {leaf1,leaf2};

PlantHydraulicsModel hydraulics(&context);

// Assign an arbitrary vector of leaf primitives a plantID grouping
hydraulics.groupPrimitivesIntoPlantObject(leaves);

...

int plantID = hydraulics.getPlantID(leaves);
float soil_water_potential = -0.05; // (MPa)

hydraulics.setSoilWaterPotentialOfPlant(plantID,soil_water_potential);
hydraulics.run(leaves);

...
}
\endcode

If the model is run on a vector of leaves that have not been grouped by the Plant Architecture plugin or with the
groupPrimitivesIntoPlantObject function, the entire vector of leaves will be assumed to belong to one plant by default.

*/
9 changes: 8 additions & 1 deletion plugins/planthydraulics/include/PlantHydraulicsModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,13 @@ class PlantHydraulicsModel {
* */
float getOrInitializePrimitiveData(uint UUID, const std::string &primitive_data_label, float default_value, bool message_flag);

//! Query the leaf primitives of a plantID
/**
* \param[in] plantID plantID that is either object data or primitive data
* \return std::vector<int> leafUUIDs List of leaf primitives belonging to that plantID
* */
std::vector<uint> getPrimitivesByPlantID(int plantID);

//! Query the unique plantIDs of leaf primitives
/**
* \param[in] UUIDs List of leaf primitives who have plantID primitive data
Expand Down Expand Up @@ -660,7 +667,7 @@ class PlantHydraulicsModel {
std::map<uint, float> stemWaterPotentialsByPlantID;
std::map<uint, float> rootWaterPotentialsByPlantID;
std::map<uint, float> soilWaterPotentialsByPlantID;
std::vector<uint> plantIDs;
std::vector<int> plantIDs;
bool message_flag = false;

bool OutputConductance = false;
Expand Down
153 changes: 109 additions & 44 deletions plugins/planthydraulics/src/PlantHydraulicsModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,14 +436,43 @@ void PlantHydraulicsModel::outputCapacitancePrimitiveData(bool toggle) {

void PlantHydraulicsModel::groupPrimitivesIntoPlantObject(const std::vector<uint> &UUIDs) {
if (!UUIDs.empty()) {
bool found = false;
for(auto UUID:UUIDs) {
uint parentID = context->getPrimitiveParentObjectID(UUID);
if (parentID != 0u && context->doesObjectDataExist(parentID, "plantID")) {
std::stringstream ss;
ss << "(PlantHydraulicsModel::groupPrimitivesIntoPlantObject): UUID " << UUID
<< " already has parent object data 'plantID'; cannot re-group into new plant object.";
helios_runtime_error(ss.str());
}
}

int plantID = context->addPolymeshObject(UUIDs);
context->setObjectData(plantID, "plantID", plantID);
context->setPrimitiveData(UUIDs, "plantID", plantID);
}
}

int PlantHydraulicsModel::getPlantID(uint UUID) {
int plantID;
context->getObjectData(context->getPrimitiveParentObjectID(UUID), "plantID", plantID);
bool found = false;
uint parentID = context->getPrimitiveParentObjectID(UUID);

if (parentID != 0u && context->doesObjectDataExist(parentID, "plantID")) {
context->getObjectData(parentID, "plantID", plantID);
found = true;
}
else if (context->doesPrimitiveDataExist(UUID, "plantID")) {
context->getPrimitiveData(UUID, "plantID", plantID);
found = true;
}

if (!found) {
std::stringstream ss;
ss << "(PlantHydraulicsModel::getPlantID): UUID " << UUID
<< " has no parent object data or primitive data 'plantID'.";
helios_runtime_error(ss.str());
}

return plantID;
}

Expand All @@ -455,8 +484,8 @@ int PlantHydraulicsModel::getPlantID(const std::vector<uint> &UUIDs) {
int plantIDfront;
int plantIDback;
int plantID;
context->getObjectData(context->getPrimitiveParentObjectID(UUIDs.front()), "plantID", plantIDfront);
context->getObjectData(context->getPrimitiveParentObjectID(UUIDs.back()), "plantID", plantIDback);
plantIDfront = getPlantID(UUIDs.front());
plantIDback = getPlantID(UUIDs.back());
if (plantIDfront == plantIDback) {
plantID = plantIDfront;
return plantID;
Expand All @@ -468,13 +497,67 @@ int PlantHydraulicsModel::getPlantID(const std::vector<uint> &UUIDs) {
return 0; // This should never be reached, but avoids compiler warning
}

std::vector<uint> PlantHydraulicsModel::getPrimitivesByPlantID(const int plantID) {
std::vector<uint> leafUUIDs;

// From object data
auto allUUIDs = context->getAllUUIDs();
auto objectIDs = context->getUniquePrimitiveParentObjectIDs(allUUIDs);

for (uint objID : objectIDs) {
if (objID != 0u && context->doesObjectDataExist(objID, "plantID")) {
int objPlantID;
context->getObjectData(objID, "plantID", objPlantID);
if (objPlantID == plantID) {
auto objectUUIDs = context->getObjectPrimitiveUUIDs(objID);
leafUUIDs.insert(leafUUIDs.end(), objectUUIDs.begin(), objectUUIDs.end());
}
}
}

// From primitive data
for (uint UUID : allUUIDs) {
if (context->doesPrimitiveDataExist(UUID, "plantID")) {
int primPlantID;
context->getPrimitiveData(UUID, "plantID", primPlantID);
if (primPlantID == plantID) {
leafUUIDs.push_back(UUID);
}
}
}

// Deduplicate UUIDs
std::sort(leafUUIDs.begin(), leafUUIDs.end());
auto it = std::unique(leafUUIDs.begin(), leafUUIDs.end());
leafUUIDs.erase(it, leafUUIDs.end());

return leafUUIDs;
}

std::vector<int> PlantHydraulicsModel::getUniquePlantIDs(const std::vector<uint> &UUIDs) {
std::vector<int> plantIDs;
int plantID;
std::vector<uint> plantObjectIDs;
plantObjectIDs = context->getUniquePrimitiveParentObjectIDs(UUIDs);
for (auto plantObjID: plantObjectIDs) {
context->getObjectData(plantObjID, "plantID", plantID);

for (auto UUID : UUIDs) {
bool found = false;
uint parentID = context->getPrimitiveParentObjectID(UUID);

if (parentID != 0u && context->doesObjectDataExist(parentID, "plantID")) {
context->getObjectData(parentID, "plantID", plantID);
found = true;
}
else if (context->doesPrimitiveDataExist(UUID, "plantID")) {
context->getPrimitiveData(UUID, "plantID", plantID);
found = true;
}

if (!found) {
std::stringstream ss;
ss << "(PlantHydraulicsModel::getUniquePlantIDs): UUID " << UUID
<< " has no parent object data or primitive data 'plantID'.";
helios_runtime_error(ss.str());
}

if (std::find(plantIDs.begin(), plantIDs.end(), plantID) == plantIDs.end()) {
plantIDs.emplace_back(plantID);
}
Expand Down Expand Up @@ -531,28 +614,24 @@ void PlantHydraulicsModel::setSoilWaterPotentialOfPlant(uint plantID, float soil
}

std::vector<uint> PlantHydraulicsModel::getPrimitivesWithoutPlantID(std::vector<uint> UUIDs) {
auto objectIDs = context->getUniquePrimitiveParentObjectIDs(UUIDs);
std::vector<uint> allPrimitivesWithPlantID;
for (auto objectID: objectIDs) {
if (context->doesObjectDataExist(objectID, "plantID")) {
auto newleaves = context->getObjectPrimitiveUUIDs(objectID);
allPrimitivesWithPlantID.insert(allPrimitivesWithPlantID.end(), newleaves.begin(), newleaves.end());
}
}

std::unordered_set<uint> seen;
seen.reserve(allPrimitivesWithPlantID.size());
for (auto UUID: allPrimitivesWithPlantID) {
seen.insert(UUID);
}

std::vector<uint> PrimitivesWithoutPlantID;
PrimitivesWithoutPlantID.reserve(UUIDs.size());

for (auto UUID: UUIDs) {
if (seen.find(UUID) == seen.end()) {
bool found = false;
uint parentID = context->getPrimitiveParentObjectID(UUID);

if (parentID != 0u && context->doesObjectDataExist(parentID, "plantID")) {
found = true;
}
else if (context->doesPrimitiveDataExist(UUID, "plantID")) {
found = true;
}

if (!found) {
PrimitivesWithoutPlantID.emplace_back(UUID);
}
}

return PrimitivesWithoutPlantID;
}

Expand All @@ -561,17 +640,10 @@ void PlantHydraulicsModel::run(int timespan, int timestep) {

groupPrimitivesIntoPlantObject(getPrimitivesWithoutPlantID(UUIDs));

std::vector<uint> plantObjectIDs = context->getUniquePrimitiveParentObjectIDs(UUIDs);
for (uint plantObjectID: plantObjectIDs) {
int plantID;
context->getObjectData(plantObjectID, "plantID", plantID);
if (std::find(plantIDs.begin(), plantIDs.end(), plantID) == plantIDs.end()) {
plantIDs.emplace_back(plantID);
}
}
plantIDs = getUniquePlantIDs(UUIDs);

for (int plantID: plantIDs) {
std::vector<uint> leavesOfPlant = context->getObjectPrimitiveUUIDs(context->filterObjectsByData(context->getUniquePrimitiveParentObjectIDs(UUIDs), std::string("plantID"), plantID, "=="));
std::vector<uint> leavesOfPlant = getPrimitivesByPlantID(plantID);
updateRootAndStemWaterPotentialsOfPlant(leavesOfPlant, timespan, timestep);
updateLeafWaterPotentialsOfPlant(leavesOfPlant, timespan, timestep);
}
Expand All @@ -582,7 +654,7 @@ void PlantHydraulicsModel::run(int timespan, int timestep) {
void PlantHydraulicsModel::run(const uint UUID, int timespan, int timestep) {
std::vector<uint> leavesOfPlant = {UUID};
groupPrimitivesIntoPlantObject(leavesOfPlant);
plantIDs = context->getUniquePrimitiveParentObjectIDs({UUID});
plantIDs = getUniquePlantIDs({UUID});


updateRootAndStemWaterPotentialsOfPlant(leavesOfPlant, timespan, timestep);
Expand All @@ -598,17 +670,10 @@ void PlantHydraulicsModel::run(const std::vector<uint> &UUIDs, int timespan, int
}
groupPrimitivesIntoPlantObject(getPrimitivesWithoutPlantID(UUIDs));

std::vector<uint> plantObjectIDs = context->getUniquePrimitiveParentObjectIDs(UUIDs);
for (uint plantObjectID: plantObjectIDs) {
int plantID;
context->getObjectData(plantObjectID, "plantID", plantID);
if (std::find(plantIDs.begin(), plantIDs.end(), plantID) == plantIDs.end()) {
plantIDs.emplace_back(plantID);
}
}
plantIDs = getUniquePlantIDs(UUIDs);

for (int plantID: plantIDs) {
std::vector<uint> leavesOfPlant = context->getObjectPrimitiveUUIDs(context->filterObjectsByData(context->getUniquePrimitiveParentObjectIDs(UUIDs), std::string("plantID"), plantID, "=="));
std::vector<uint> leavesOfPlant = getPrimitivesByPlantID(plantID);
updateRootAndStemWaterPotentialsOfPlant(leavesOfPlant, timespan, timestep);
updateLeafWaterPotentialsOfPlant(leavesOfPlant, timespan, timestep);
}
Expand Down
49 changes: 42 additions & 7 deletions plugins/planthydraulics/src/selfTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,42 @@ int PlantHydraulicsModel::selfTest() {
}
}

//Test 11: Run first code example in documentation
//Test 11: getUniquePlantIDs
{
try {
Context context;
std::vector<uint> leaves;
vec3 center = vec3(0,0,0);
vec2 size = vec2(0.1,0.1);
uint leaf1 = context.addPatch(center,size);
context.setPrimitiveData(leaf1,"plantID",1);
uint leaf2 = context.addPatch(center+vec3(0.1,0.1,0.1),size);
context.setPrimitiveData(leaf2,"plantID",2);
leaves = {leaf1,leaf2};

PlantHydraulicsModel hydraulics(&context);
std::vector<int> IDs = hydraulics.getUniquePlantIDs(leaves);
int check11 = IDs.front();
int check12 = IDs.back();
int check21 = hydraulics.getPlantID(leaf1);
int check22 = hydraulics.getPlantID(leaf2);

if (check11 != check21 && check12 != check22) {
error_count++;
std::cerr << "Test 11 (getUniquePlantIDs uint): Failed - Expected plantID" << check11 << ", got " << check21 <<
"and expected "<< check12 << ", got " << check22 << std::endl;
} else {
std::cout << "Test 11 (getUniquePlantIDs uint): Passed." << std::endl;
}


} catch (const std::exception& e) {
error_count++;
std::cerr <<"Test 11 (getUniquePlantIDs): Failed - " << e.what() << std::endl;
}
}

//Test 12: Run first code example in documentation
{
try {
Context context;
Expand Down Expand Up @@ -264,18 +299,18 @@ int PlantHydraulicsModel::selfTest() {
}
}
if ( failed ) {
std::cerr << "Test 11 (run doc example 1): Failed." << std::endl;
std::cerr << "Test 12 (run doc example 1): Failed." << std::endl;
error_count++;
}else {
std::cout << "Test 11 (run doc example 1): Passed." << std::endl;
std::cout << "Test 12 (run doc example 1): Passed." << std::endl;
}
} catch (const std::exception &e) {
error_count++;
std::cerr << "Test 11 (run doc example 1): Failed - " << e.what() << std::endl;
std::cerr << "Test 12 (run doc example 1): Failed - " << e.what() << std::endl;
}
}

//Test 12: Run second code example in documentation
//Test 13: Run second code example in documentation
{
try {
Context context;
Expand Down Expand Up @@ -304,10 +339,10 @@ int PlantHydraulicsModel::selfTest() {
float psi_root = hydraulics.getRootWaterPotential(leafUUID);
float psi_stem = hydraulics.getStemWaterPotential(leafUUID);
}
std::cout << "Test 12 (run doc example 2): Passed." << std::endl;
std::cout << "Test 13 (run doc example 2): Passed." << std::endl;
} catch (const std::exception &e) {
error_count++;
std::cerr << "Test 12 (run doc example 2): Failed - " << e.what() << std::endl;
std::cerr << "Test 13 (run doc example 2): Failed - " << e.what() << std::endl;
}
}

Expand Down