// Copyright(C) 1999-2021 National Technology & Engineering Solutions // of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with // NTESS, the U.S. Government retains certain rights in this software. // // See packages/seacas/LICENSE for details #include "ED_SystemInterface.h" #include "exo_entity.h" #include "exodusII.h" // for ex_get_var, EX_INVALID_ID, etc #include "fmt/color.h" #include "fmt/ostream.h" #include "smart_assert.h" // for SMART_ASSERT #include "stringx.h" // for to_lower #include // for int64_t #include // for exit #include // for strlen #include #include namespace { size_t get_index(int file_id, EXOTYPE exo_type, size_t id, const char *label); size_t get_num_entities(int file_id, EXOTYPE exo_type); size_t get_num_variables(int file_id, EXOTYPE type, const char *label); size_t get_num_attributes(int file_id, EXOTYPE type, size_t id, const char *label); #ifndef NDEBUG size_t get_num_timesteps(int file_id); #endif } // namespace Exo_Entity::Exo_Entity(int file_id, size_t id) : fileId(file_id), id_(id) { SMART_ASSERT(file_id > 0); SMART_ASSERT((int)id > EX_INVALID_ID); } Exo_Entity::Exo_Entity(int file_id, size_t id, size_t nnodes) : fileId(file_id), id_(id), numEntity(nnodes) { SMART_ASSERT(file_id > 0); SMART_ASSERT((int)id > EX_INVALID_ID); } Exo_Entity::~Exo_Entity() { delete[] truth_; if (numVars > 0) { for (int i = 0; i < numVars; ++i) { delete[] results_[i]; } delete[] results_; } if (numAttr > 0) { for (int i = 0; i < numAttr; ++i) { delete[] attributes_[i]; } } } int Exo_Entity::Check_State() const { SMART_ASSERT(id_ >= EX_INVALID_ID); SMART_ASSERT(!(id_ == EX_INVALID_ID && numEntity > 0)); return 1; } void Exo_Entity::initialize(int file_id, size_t id) { fileId = file_id; id_ = id; index_ = get_index(fileId, exodus_type(), id_, label()); entity_load_params(); internal_load_params(); } bool Exo_Entity::is_valid_var(size_t var_index) const { SMART_ASSERT((int)var_index < numVars); if (truth_ == nullptr) { get_truth_table(); SMART_ASSERT(truth_ != nullptr); } return (truth_[var_index] != 0); } std::string Exo_Entity::Load_Results(int time_step, int var_index) { SMART_ASSERT(Check_State()); if (fileId < 0) { return "exodiff: ERROR: Invalid file id!"; } if (id_ == EX_INVALID_ID) { return "exodiff: ERROR: Must initialize block parameters first!"; } if (var_index < 0 || var_index >= numVars) { Error("Exo_Entity::Load_Results(): var_index is invalid. Aborting...\n"); } SMART_ASSERT(time_step >= 1 && time_step <= (int)get_num_timesteps(fileId)); if (time_step != currentStep) { Free_Results(); currentStep = time_step; } if (truth_ == nullptr) { get_truth_table(); SMART_ASSERT(truth_ != nullptr); } if (truth_[var_index] != 0) { if ((results_[var_index] == nullptr) && (numEntity != 0u)) { results_[var_index] = new double[numEntity]; SMART_ASSERT(results_[var_index] != nullptr); } if (numEntity != 0u) { int err = 0; err = ex_get_var(fileId, time_step, exodus_type(), var_index + 1, id_, numEntity, results_[var_index]); if (err < 0) { Error(fmt::format("Exo_Entity::Load_Results(): Call to exodus routine" " returned error value! {} id = {}\n" "Aborting...\n", label(), id_)); } else if (err > 0) { return fmt::format("WARNING: Number {} returned from call to exodus get variable routine.", err); } } else { return std::string("WARNING: No items in this ") + label(); } } else { return std::string("WARNING: Variable not stored in this ") + label(); } return ""; } std::string Exo_Entity::Load_Results(int t1, int t2, double proportion, int var_index) { static std::vector results2; SMART_ASSERT(Check_State()); if (fileId < 0) { return "exodiff: ERROR: Invalid file id!"; } if (id_ == EX_INVALID_ID) { return "exodiff: ERROR: Must initialize block parameters first!"; } SMART_ASSERT(var_index >= 0 && var_index < numVars); SMART_ASSERT(t1 >= 1 && t1 <= (int)get_num_timesteps(fileId)); SMART_ASSERT(t2 >= 1 && t2 <= (int)get_num_timesteps(fileId)); if (t1 != currentStep) { Free_Results(); currentStep = t1; } if (truth_ == nullptr) { get_truth_table(); SMART_ASSERT(truth_ != nullptr); } if (truth_[var_index] != 0) { if ((results_[var_index] == nullptr) && (numEntity != 0u)) { results_[var_index] = new double[numEntity]; SMART_ASSERT(results_[var_index] != nullptr); } if (numEntity != 0u) { int err = ex_get_var(fileId, t1, exodus_type(), var_index + 1, id_, numEntity, results_[var_index]); if (err < 0) { Error(fmt::format( "Exo_Entity::Load_Results(): Call to exodus routine returned error value! {} id = {}\n" "Aborting...\n", label(), id_)); } else if (err > 0) { return fmt::format("WARNING: Number {} returned from call to exodus get variable routine.", err); } if (t1 != t2) { results2.resize(numEntity); err = ex_get_var(fileId, t2, exodus_type(), var_index + 1, id_, numEntity, &results2[0]); if (err < 0) { Error(fmt::format("Exo_Entity::Load_Results(): Call to exodus routine" " returned error value! {} id = {}\n" "Aborting...\n", label(), id_)); } double *results1 = results_[var_index]; for (size_t i = 0; i < numEntity; i++) { results1[i] = (1.0 - proportion) * results1[i] + proportion * results2[i]; } } } else { return std::string("WARNING: No items in this ") + label(); } } else { return std::string("WARNING: Variable not stored in this ") + label(); } return ""; } const double *Exo_Entity::Get_Results(int var_index) const { SMART_ASSERT(Check_State()); if (currentStep == 0) { return nullptr; } SMART_ASSERT(var_index >= 0 && var_index < numVars); if (var_index >= 0 && var_index < numVars) { return results_[var_index]; } return nullptr; } void Exo_Entity::Free_Results() { SMART_ASSERT(Check_State()); currentStep = 0; for (int v = 0; v < numVars; ++v) { delete[] results_[v]; results_[v] = nullptr; } } void Exo_Entity::get_truth_table() const { if (numVars > 0 && truth_ == nullptr) { truth_ = new int[numVars]; SMART_ASSERT(truth_ != nullptr); // initialize to true for the case of no objects in the block (some older // versions of ex_get_object_truth_vector do not set the values at all) for (int i = 0; i < numVars; ++i) { truth_[i] = 1; } int err = ex_get_object_truth_vector(fileId, exodus_type(), id_, numVars, truth_); if (err < 0) { Error("Exo_Entity::get_truth_table(): ex_get_object_truth_vector returned error.\n"); } } } std::string Exo_Entity::Load_Attributes(int attr_index) { SMART_ASSERT(Check_State()); if (fileId < 0) { return "exodiff: ERROR: Invalid file id!"; } if (id_ == EX_INVALID_ID) { return "exodiff: ERROR: Must initialize block parameters first!"; } SMART_ASSERT(attr_index >= 0 && attr_index < numAttr); if ((attributes_[attr_index] == nullptr) && (numEntity != 0u)) { attributes_[attr_index] = new double[numEntity]; SMART_ASSERT(attributes_[attr_index] != nullptr); } if (numEntity != 0u) { int err = 0; err = ex_get_one_attr(fileId, exodus_type(), id_, attr_index + 1, attributes_[attr_index]); if (err < 0) { Error(fmt::format("Exo_Entity::Load_Attributes(): Call to exodus routine" " returned error value! {} id = {}\n" "Aborting...\n", label(), id_)); } else if (err > 0) { return fmt::format("WARNING: Number {} returned from call to exodus get variable routine.", err); } } else { return std::string("WARNING: No items in this ") + label(); } return ""; } const double *Exo_Entity::Get_Attributes(int attr_index) const { SMART_ASSERT(Check_State()); SMART_ASSERT(attr_index >= 0 && attr_index < numAttr); return attributes_[attr_index]; } void Exo_Entity::Free_Attributes() { SMART_ASSERT(Check_State()); for (int v = 0; v < numAttr; ++v) { delete[] attributes_[v]; attributes_[v] = nullptr; } } const std::string &Exo_Entity::Get_Attribute_Name(int attr_index) const { SMART_ASSERT(attr_index >= 0 && attr_index < numAttr); return attributeNames[attr_index]; } int Exo_Entity::Find_Attribute_Index(const std::string &name) const { std::string lower_name = name; to_lower(lower_name); int indx = -1; for (int i = 0; i < numAttr; i++) { if (attributeNames[i] == lower_name) { indx = i; break; } } return indx; } void Exo_Entity::internal_load_params() { int name_size = ex_inquire_int(fileId, EX_INQ_MAX_READ_NAME_LENGTH); { std::vector name(name_size + 1); ex_get_name(fileId, exodus_type(), id_, name.data()); if (name[0] != '\0') { name_ = name.data(); to_lower(name_); } else { name_ = short_label(); name_ += "_"; name_ += std::to_string(id_); } } numVars = get_num_variables(fileId, exodus_type(), label()); if (numVars != 0) { results_ = new double *[numVars]; SMART_ASSERT(results_ != nullptr); for (int i = 0; i < numVars; ++i) { results_[i] = nullptr; } } numAttr = get_num_attributes(fileId, exodus_type(), id_, label()); if (numAttr != 0) { attributes_.resize(numAttr); char **names = get_name_array(numAttr, name_size); int err = ex_get_attr_names(fileId, exodus_type(), id_, names); if (err < 0) { Error(fmt::format( "ExoII_Read::Get_Init_Data(): Failed to get {} attribute names! Aborting...\n", label())); } for (int vg = 0; vg < numAttr; ++vg) { SMART_ASSERT(names[vg] != nullptr); if (std::strlen(names[vg]) == 0) { std::string name = "attribute_" + std::to_string(vg + 1); attributeNames.push_back(name); } else if (static_cast(std::strlen(names[vg])) > name_size) { fmt::print(stderr, fmt::fg(fmt::color::red), "exodiff: ERROR: {} attribute names appear corrupt\n" " A length is 0 or greater than name_size({})\n" " Here are the names that I received from" " a call to ex_get_attr_names(...):\n", label(), name_size); for (int k = 1; k <= numAttr; ++k) { fmt::print(stderr, fmt::fg(fmt::color::red), "\t\t{}) \"{}\"\n", k, names[k - 1]); } fmt::print(stderr, fmt::fg(fmt::color::red), " Aborting...\n"); exit(1); } else { std::string n(names[vg]); to_lower(n); attributeNames.push_back(n); } } free_name_array(names, numAttr); } } namespace { size_t get_index(int file_id, EXOTYPE exo_type, size_t id, const char *label) { // Get ids... size_t count = get_num_entities(file_id, exo_type); if ((ex_int64_status(file_id) & EX_IDS_INT64_API) != 0) { std::vector ids(count); ex_get_ids(file_id, exo_type, ids.data()); for (size_t i = 0; i < count; i++) { if (static_cast(ids[i]) == id) { return i; } } } else { std::vector ids(count); ex_get_ids(file_id, exo_type, ids.data()); for (size_t i = 0; i < count; i++) { if (static_cast(ids[i]) == id) { return i; } } } Error(fmt::format("{} id {} does not exist!\n", label, id)); return 0; } size_t get_num_entities(int file_id, EXOTYPE exo_type) { ex_inquiry inquiry = EX_INQ_INVALID; switch (exo_type) { case EX_ELEM_BLOCK: inquiry = EX_INQ_ELEM_BLK; break; case EX_NODE_SET: inquiry = EX_INQ_NODE_SETS; break; case EX_SIDE_SET: inquiry = EX_INQ_SIDE_SETS; break; case EX_EDGE_BLOCK: inquiry = EX_INQ_EDGE_BLK; break; case EX_FACE_BLOCK: inquiry = EX_INQ_FACE_BLK; break; default: Error("Invalid entity type in get_num_entities\n"); } SMART_ASSERT(inquiry != EX_INQ_INVALID); return ex_inquire_int(file_id, inquiry); } size_t get_num_variables(int file_id, EXOTYPE type, const char *label) { int num_vars = 0; int err = ex_get_variable_param(file_id, type, &num_vars); if (err < 0) { Error(fmt::format("Failed to get number of '{}' variables! Aborting...\n", label)); } return num_vars; } size_t get_num_attributes(int file_id, EXOTYPE type, size_t id, const char *label) { int num_attr = 0; int err = ex_get_attr_param(file_id, type, id, &num_attr); if (err < 0) { Error(fmt::format("Failed to get number of '{}' attributes! Aborting...\n", label)); } return num_attr; } #ifndef NDEBUG size_t get_num_timesteps(int file_id) { return ex_inquire_int(file_id, EX_INQ_TIME); } #endif } // namespace