From 7a02449e61d7d57e209dc23553a941b7cda3cea9 Mon Sep 17 00:00:00 2001 From: Jonathan Lifflander Date: Wed, 11 Dec 2024 09:37:35 -0800 Subject: [PATCH] #130: api: add LB iterations: parsing and visualization --- src/vt-tv/api/info.h | 406 +++++++++++++++++-------- src/vt-tv/api/phase_work.h | 141 ++++++++- src/vt-tv/api/rank.h | 16 +- src/vt-tv/api/types.h | 3 + src/vt-tv/render/render.cc | 351 ++++++++++++--------- src/vt-tv/render/render.h | 53 ++-- src/vt-tv/utility/json_reader.cc | 320 ++++++++++--------- src/vt-tv/utility/json_reader.h | 17 ++ tests/unit/api/test_info.cc | 48 +-- tests/unit/api/test_rank.cc | 12 +- tests/unit/utility/test_json_reader.cc | 4 +- 11 files changed, 903 insertions(+), 468 deletions(-) diff --git a/src/vt-tv/api/info.h b/src/vt-tv/api/info.h index 31908c411..0fac5d85f 100644 --- a/src/vt-tv/api/info.h +++ b/src/vt-tv/api/info.h @@ -196,46 +196,46 @@ struct Info { * \brief Returns a getter to a specified rank QOI */ template - std::function + std::function getRankQOIGetter(std::string const& rank_qoi) const { - std::function qoi_getter; + std::function qoi_getter; if (rank_qoi == "load") { - qoi_getter = [&](Rank rank, PhaseType phase) { - return convertQOIVariantTypeToT_(getRankLoad(rank, phase)); + qoi_getter = [&](Rank rank, PhaseType phase, LBIterationType lb_iter) { + return convertQOIVariantTypeToT_(getRankLoad(rank, phase, lb_iter)); }; } else if (rank_qoi == "received_volume") { - qoi_getter = [&](Rank rank, PhaseType phase) { + qoi_getter = [&](Rank rank, PhaseType phase, LBIterationType lb_iter) { return convertQOIVariantTypeToT_( - getRankReceivedVolume(rank, phase)); + getRankReceivedVolume(rank, phase, lb_iter)); }; } else if (rank_qoi == "sent_volume") { - qoi_getter = [&](Rank rank, PhaseType phase) { - return convertQOIVariantTypeToT_(getRankSentVolume(rank, phase)); + qoi_getter = [&](Rank rank, PhaseType phase, LBIterationType lb_iter) { + return convertQOIVariantTypeToT_(getRankSentVolume(rank, phase, lb_iter)); }; } else if (rank_qoi == "number_of_objects") { - qoi_getter = [&](Rank rank, PhaseType phase) { - return convertQOIVariantTypeToT_(getRankNumObjects(rank, phase)); + qoi_getter = [&](Rank rank, PhaseType phase, LBIterationType lb_iter) { + return convertQOIVariantTypeToT_(getRankNumObjects(rank, phase, lb_iter)); }; } else if (rank_qoi == "number_of_migratable_objects") { - qoi_getter = [&](Rank rank, PhaseType phase) { + qoi_getter = [&](Rank rank, PhaseType phase, LBIterationType lb_iter) { return convertQOIVariantTypeToT_( - getRankNumMigratableObjects(rank, phase)); + getRankNumMigratableObjects(rank, phase, lb_iter)); }; } else if (rank_qoi == "migratable_load") { - qoi_getter = [&](Rank rank, PhaseType phase) { - return convertQOIVariantTypeToT_(getRankMigratableLoad(rank, phase)); + qoi_getter = [&](Rank rank, PhaseType phase, LBIterationType lb_iter) { + return convertQOIVariantTypeToT_(getRankMigratableLoad(rank, phase, lb_iter)); }; } else if (rank_qoi == "sentinel_load") { - qoi_getter = [&](Rank rank, PhaseType phase) { - return convertQOIVariantTypeToT_(getRankSentinelLoad(rank, phase)); + qoi_getter = [&](Rank rank, PhaseType phase, LBIterationType lb_iter) { + return convertQOIVariantTypeToT_(getRankSentinelLoad(rank, phase, lb_iter)); }; } else if (rank_qoi == "id") { - qoi_getter = [&](Rank rank, PhaseType) { + qoi_getter = [&](Rank rank, PhaseType, LBIterationType) { return convertQOIVariantTypeToT_(getRankID(rank)); }; } else { // Look in attributes (will throw an error if QOI doesn't exist) - qoi_getter = [&](Rank rank, PhaseType) { + qoi_getter = [&](Rank rank, PhaseType, LBIterationType) { return convertQOIVariantTypeToT_(getRankAttribute(rank, rank_qoi)); }; } @@ -292,11 +292,12 @@ struct Info { */ template T getRankQOIAtPhase( - ElementIDType rank_id, PhaseType phase, std::string const& rank_qoi + ElementIDType rank_id, PhaseType phase, LBIterationType lb_iter, + std::string const& rank_qoi ) const { auto qoi_getter = getRankQOIGetter(rank_qoi); auto const& rank = this->ranks_.at(rank_id); - return qoi_getter(rank, phase); + return qoi_getter(rank, phase, lb_iter); } /** @@ -305,78 +306,106 @@ struct Info { * \return a map of QOI per rank */ template - std::unordered_map + std::vector getAllQOIAtRank(ElementIDType rank_id, std::string const& rank_qoi) const { auto const& rank = ranks_.at(rank_id); auto const& phase_work = rank.getPhaseWork(); - std::unordered_map rank_qois; + std::vector rank_qois; if (hasRankUserDefined(rank_qoi)) { auto const& test_value = getFirstRankUserDefined(rank_qoi); - for (auto const& [phase, _] : phase_work) { + for (auto const& [phase, pw] : phase_work) { if (std::holds_alternative(test_value)) { - rank_qois.emplace( - phase, static_cast( - std::get(getRankUserDefined(rank, phase, rank_qoi)) + rank_qois.push_back( + static_cast( + std::get( + getRankUserDefined(rank, phase, no_lb_iter, rank_qoi) + ) ) ); } else if (std::holds_alternative(test_value)) { - rank_qois.emplace( - phase, static_cast( - std::get(getRankUserDefined(rank, phase, rank_qoi)) + rank_qois.push_back( + static_cast( + std::get( + getRankUserDefined(rank, phase, no_lb_iter, rank_qoi) + ) ) ); } - } - } else { - auto qoi_getter = getRankQOIGetter(rank_qoi); - for (auto const& [phase, _] : phase_work) { - rank_qois.emplace(phase, qoi_getter(rank, phase)); - } - } - - return rank_qois; - } - - /** - * \brief Get QOI of all ranks at given phase - * - * \return a map of QOI per rank - */ - template - std::unordered_map - getAllRankQOIAtPhase(PhaseType phase, std::string const& rank_qoi) const { - std::unordered_map rank_qois; - if (hasRankUserDefined(rank_qoi)) { - auto const& test_value = getFirstRankUserDefined(rank_qoi); - for (uint64_t rank_id = 0; rank_id < ranks_.size(); rank_id++) { - auto const& rank = ranks_.at(rank_id); - if (std::holds_alternative(test_value)) { - rank_qois.emplace( - phase, static_cast( - std::get(getRankUserDefined(rank, phase, rank_qoi)) - ) - ); - } else if (std::holds_alternative(test_value)) { - rank_qois.emplace( - phase, static_cast( - std::get(getRankUserDefined(rank, phase, rank_qoi)) - ) - ); + // Now, loop through all the LB iterations + for (auto const& [lb_iter, lb_iter_work] : pw.getLBIterations()) { + if (std::holds_alternative(test_value)) { + rank_qois.push_back( + static_cast( + std::get( + getRankUserDefined(rank, phase, lb_iter, rank_qoi) + ) + ) + ); + } else if (std::holds_alternative(test_value)) { + rank_qois.push_back( + static_cast( + std::get( + getRankUserDefined(rank, phase, lb_iter, rank_qoi) + ) + ) + ); + } } } } else { auto qoi_getter = getRankQOIGetter(rank_qoi); - for (auto const& [rank_id, rank] : this->ranks_) { - rank_qois.emplace(rank_id, qoi_getter(rank, phase)); + for (auto const& [phase, pw] : phase_work) { + rank_qois.push_back(qoi_getter(rank, phase, -1)); + for (auto const& [lb_iter, lb_iter_work] : pw.getLBIterations()) { + rank_qois.push_back(qoi_getter(rank, phase, lb_iter)); + } } } return rank_qois; } + // /** + // * \brief Get QOI of all ranks at given phase + // * + // * \return a map of QOI per rank + // */ + // template + // std::unordered_map + // getAllRankQOIAtPhase(PhaseType phase, std::string const& rank_qoi) const { + // std::unordered_map rank_qois; + + // if (hasRankUserDefined(rank_qoi)) { + // auto const& test_value = getFirstRankUserDefined(rank_qoi); + // for (uint64_t rank_id = 0; rank_id < ranks_.size(); rank_id++) { + // auto const& rank = ranks_.at(rank_id); + // if (std::holds_alternative(test_value)) { + // rank_qois.emplace( + // phase, static_cast( + // std::get(getRankUserDefined(rank, phase, rank_qoi)) + // ) + // ); + // } else if (std::holds_alternative(test_value)) { + // rank_qois.emplace( + // phase, static_cast( + // std::get(getRankUserDefined(rank, phase, rank_qoi)) + // ) + // ); + // } + // } + // } else { + // auto qoi_getter = getRankQOIGetter(rank_qoi); + // for (auto const& [rank_id, rank] : this->ranks_) { + // rank_qois.emplace(rank_id, qoi_getter(rank, phase, no_lb_iter)); + // } + // } + + // return rank_qois; + // } + /* -------------------------------- Object Getters -------------------------------- */ /** @@ -386,9 +415,10 @@ struct Info { */ template T getObjectQOIAtPhase( - ElementIDType obj_id, PhaseType phase, std::string const& obj_qoi + ElementIDType obj_id, PhaseType phase, LBIterationType lb_iter, + std::string const& obj_qoi ) const { - auto const& objects = this->getPhaseObjects(phase); + auto const& objects = getPhaseObjects(phase, lb_iter); auto const& obj = objects.at(obj_id); auto const& ud = obj.getUserDefined(); @@ -404,6 +434,31 @@ struct Info { return qoi_getter(obj); } + /** + * \brief Get a work distribution + * + * \param[in] phase the phase + * \param[in] lb_iter the LB iteration + * + * \return the work distribution + */ + WorkDistribution const& getWorkDistribution( + Rank const& rank, PhaseType phase, LBIterationType lb_iter + ) const { + auto const& pw = rank.getPhaseWork(); + if (auto iter = pw.find(phase); iter == pw.end()) { + auto ex = "info::getPhaseObjects: Phase " + std::to_string(phase) + + " doesn't exist for rank " + std::to_string(rank.getRankID()); + throw std::runtime_error(ex); + } + + if (lb_iter == no_lb_iter) { + return pw.at(phase); + } else { + return pw.at(phase).getLBIteration(lb_iter); + } + } + /** * \brief Get all objects for a given rank and phase * @@ -413,21 +468,22 @@ struct Info { * \return the objects */ std::unordered_map - getRankObjects(ElementIDType rank_id, PhaseType phase) const { + getRankObjects( + ElementIDType rank_id, PhaseType phase, LBIterationType lb_iter + ) const { std::unordered_map objects; // Get Rank info for specified rank auto const& rank_info = ranks_.at(rank_id); - // Get history of phases for this rank - auto const& phase_history_at_rank = rank_info.getPhaseWork(); - // Get phase work at specified phase - auto const& phase_work_at_rank = phase_history_at_rank.find(phase); + auto const& phase_work_at_rank = getWorkDistribution( + rank_info, phase, lb_iter + ); // Get all objects at specified phase auto const& object_work_at_phase_at_rank = - phase_work_at_rank->second.getObjectWork(); + phase_work_at_rank.getObjectWork(); for (auto const& [elm_id, obj_work] : object_work_at_phase_at_rank) { objects.insert(std::make_pair(elm_id, obj_work)); @@ -437,38 +493,31 @@ struct Info { } /** - * \brief Get all objects in all ranks for a given phase + * \brief Get all objects in all ranks for a given phase and LB iteration * * \param[in] phase the phase + * \param[in] lb_iter the LB iterations * * \return the objects */ std::unordered_map - getPhaseObjects(PhaseType phase) const { + getPhaseObjects(PhaseType phase, LBIterationType lb_iter) const { // fmt::print("Phase: {}\n", phase); // Map of objects at given phase std::unordered_map objects_at_phase; // Go through all ranks and get all objects at given phase - for (uint64_t rank = 0; rank < this->ranks_.size(); rank++) { + for (uint64_t rank = 0; rank < ranks_.size(); rank++) { // fmt::print(" Rank: {}\n",rank); // Get Rank info for specified rank auto const& rank_info = ranks_.at(rank); // Get history of phases for this rank - auto const& phase_history = rank_info.getPhaseWork(); - - // Get phase work at specified phase - auto const& phase_work = phase_history.find(phase); - if (phase_work == phase_history.end()) { - auto ex = "info::getPhaseObjects: Phase " + std::to_string(phase) + - " doesn't exist for rank " + std::to_string(rank); - throw std::runtime_error(ex); - } + auto const& phase_work = getWorkDistribution(rank_info, phase, lb_iter); // Get all objects at specified phase - auto const& object_work_at_phase = phase_work->second.getObjectWork(); + auto const& object_work_at_phase = phase_work.getObjectWork(); for (auto const& [elm_id, obj_work] : object_work_at_phase) { // fmt::print(" Object Id: {}\n", elm_id); @@ -479,33 +528,53 @@ struct Info { } /** - * \brief Get maximum inter-object communication volume across all ranks and phases + * \brief Get maximum inter-object communication volume across all ranks and + * phases * * \return the maximum volume */ double getMaxVolume() const { double ov_max = 0.; - /* Iterate over all phases: each object is re-initialized when - advancing to the next phase (in the JSON-reader), thus different memory spaces - are used for an object of the same id but of a different phase. - This means the object communications are not phase persistent, so one can't obtain - the maximum volume by iterated through object ids. + /* + * Iterate over all phases: each object is re-initialized when advancing to + * the next phase (in the JSON-reader), thus different memory spaces are + * used for an object of the same id but of a different phase. This means + * the object communications are not phase persistent, so one can't obtain + * the maximum volume by iterated through object ids. */ - auto n_phases = this->getNumPhases(); + auto const n_phases = getNumPhases(); if (selected_phase_ != std::numeric_limits::max()) { - auto const& objects = this->getPhaseObjects(selected_phase_); + auto const& objects = getPhaseObjects(selected_phase_, no_lb_iter); for (auto const& [obj_id, obj_work] : objects) { auto obj_max_v = obj_work.getMaxVolume(); if (obj_max_v > ov_max) ov_max = obj_max_v; } + auto const& lb_iters = + getRank(0).getPhaseWork().at(selected_phase_).getLBIterations(); + for (auto const& [lb_iter_id, _] : lb_iters) { + auto const& objects2 = getPhaseObjects(selected_phase_, lb_iter_id); + for (auto const& [obj_id, obj_work] : objects2) { + auto obj_max_v = obj_work.getMaxVolume(); + if (obj_max_v > ov_max) + ov_max = obj_max_v; + } + } } else { for (PhaseType phase = 0; phase < n_phases; phase++) { - { - auto const& objects = this->getPhaseObjects(phase); - for (auto const& [obj_id, obj_work] : objects) { + auto const& objects = getPhaseObjects(phase, no_lb_iter); + for (auto const& [obj_id, obj_work] : objects) { + auto obj_max_v = obj_work.getMaxVolume(); + if (obj_max_v > ov_max) + ov_max = obj_max_v; + } + auto const& lb_iters = + getRank(0).getPhaseWork().at(phase).getLBIterations(); + for (auto const& [lb_iter_id, _] : lb_iters) { + auto const& objects2 = getPhaseObjects(phase, lb_iter_id); + for (auto const& [obj_id, obj_work] : objects2) { auto obj_max_v = obj_work.getMaxVolume(); if (obj_max_v > ov_max) ov_max = obj_max_v; @@ -524,30 +593,51 @@ struct Info { double getMaxLoad() const { double ol_max = 0.; - auto n_phases = this->getNumPhases(); + auto const n_phases = getNumPhases(); if (selected_phase_ != std::numeric_limits::max()) { - auto const& objects = this->getPhaseObjects(selected_phase_); + auto const& objects = getPhaseObjects(selected_phase_, no_lb_iter); for (auto const& [obj_id, obj_work] : objects) { auto obj_load = obj_work.getLoad(); if (obj_load > ol_max) ol_max = obj_load; } + auto const& lb_iters = + getRank(0).getPhaseWork().at(selected_phase_).getLBIterations(); + for (auto const& [lb_iter_id, _] : lb_iters) { + auto const& objects2 = getPhaseObjects(selected_phase_, lb_iter_id); + for (auto const& [obj_id, obj_work] : objects2) { + auto obj_load = obj_work.getLoad(); + if (obj_load > ol_max) + ol_max = obj_load; + } + } } else { for (PhaseType phase = 0; phase < n_phases; phase++) { - auto const& objects = this->getPhaseObjects(phase); + auto const& objects = getPhaseObjects(phase, no_lb_iter); for (auto const& [obj_id, obj_work] : objects) { auto obj_load = obj_work.getLoad(); if (obj_load > ol_max) ol_max = obj_load; } + auto const& lb_iters = + getRank(0).getPhaseWork().at(phase).getLBIterations(); + for (auto const& [lb_iter_id, _] : lb_iters) { + auto const& objects2 = getPhaseObjects(phase, lb_iter_id); + for (auto const& [obj_id, obj_work] : objects2) { + auto obj_load = obj_work.getLoad(); + if (obj_load > ol_max) + ol_max = obj_load; + } + } } } return ol_max; } /** - * \brief Create mapping of all objects in all ranks for a given phase (made for allowing changes to these objects) + * \brief Create mapping of all objects in all ranks for a given phase (made + * for allowing changes to these objects) * * \param[in] phase the phase * @@ -561,7 +651,7 @@ struct Info { std::unordered_map objects_at_phase; // Go through all ranks and get all objects at given phase - for (uint64_t rank = 0; rank < this->ranks_.size(); rank++) { + for (uint64_t rank = 0; rank < ranks_.size(); rank++) { // fmt::print(" Rank: {}\n",rank); // Get Rank info for specified rank auto& rank_info = ranks_.at(rank); @@ -593,10 +683,10 @@ struct Info { std::set objects; // Go through all ranks and get all objects at given phase - for (uint64_t rank = 0; rank < this->ranks_.size(); rank++) { + for (uint64_t rank = 0; rank < ranks_.size(); rank++) { // fmt::print("Rank: {}\n",rank); // Get Rank info for specified rank - auto const& rank_info = this->ranks_.at(rank); + auto const& rank_info = ranks_.at(rank); // Get history of phases for this rank auto const& phase_history = rank_info.getPhaseWork(); @@ -611,6 +701,15 @@ struct Info { // fmt::print("| |-> Object Id: {}\n", elm_id); objects.insert(elm_id); } + + for (auto const& [lb_id, lb_iter] : phase_work.getLBIterations()) { + auto const& object_work_at_lb_iter = lb_iter.getObjectWork(); + + for (auto const& [elm_id, obj_work] : object_work_at_lb_iter) { + // fmt::print("| |-> Object Id: {}\n", elm_id); + objects.insert(elm_id); + } + } } } fmt::print("Size of all objects: {}\n", objects.size()); @@ -737,20 +836,21 @@ struct Info { * \brief Compute imbalance across ranks at phase * * \param[in] phase the phase + * \param[in] lb_iter the LB iterations * * \return the imbalance */ - double getImbalance(PhaseType phase) const { + double getImbalance(PhaseType phase, LBIterationType lb_iter) const { double load_sum = 0.; double max_load = 0.; - for (uint64_t rank = 0; rank < this->ranks_.size(); rank++) { - auto rank_max_load = this->getRank(rank).getLoad(phase); - if (rank_max_load > max_load) - max_load = rank_max_load; - load_sum += this->getRank(rank).getLoad(phase); + for (uint64_t rank = 0; rank < ranks_.size(); rank++) { + auto const rank_load = getRank(rank).getLoad(phase, lb_iter); + if (rank_load > max_load) + max_load = rank_load; + load_sum += rank_load; } - double load_avg = load_sum / this->ranks_.size(); + double load_avg = load_sum / ranks_.size(); double imbalance = std::numeric_limits::quiet_NaN(); if (load_avg != 0) { imbalance = (max_load / load_avg) - 1.; @@ -836,7 +936,8 @@ struct Info { * \return the requested attribute or user_defined QOI */ QOIVariantTypes getObjectAttributeOrUserDefined( - ObjectWork object, std::string object_qoi) const { + ObjectWork object, std::string object_qoi + ) const { auto obj_attributes = object.getAttributes(); if (obj_attributes.count(object_qoi) > 0) { return obj_attributes.at(object_qoi); @@ -875,9 +976,15 @@ struct Info { * \return the value for a given user-defined key/value pair */ QOIVariantTypes getRankUserDefined( - Rank const& rank, PhaseType phase, std::string const& key + Rank const& rank, PhaseType phase, LBIterationType lb_iter, + std::string const& key ) const { - return rank.getPhaseWork().at(phase).getUserDefined().at(key); + if (lb_iter == no_lb_iter) { + return rank.getPhaseWork().at(phase).getUserDefined().at(key); + } else { + return rank.getPhaseWork().at(phase).getLBIteration(lb_iter) + .getUserDefined().at(key); + } } /** @@ -895,6 +1002,13 @@ struct Info { if (auto iter = ud.find(key); iter != ud.end()) { return true; } + auto const& lb_iters = rank.getPhaseWork().at(i).getLBIterations(); + for (auto const& [_, lb_iter] : lb_iters) { + auto const& ud2 = lb_iter.getUserDefined(); + if (auto iter = ud2.find(key); iter != ud2.end()) { + return true; + } + } } } return false; @@ -944,11 +1058,14 @@ struct Info { * * \param[in] rank the rank * \param[in] phase the phase + * \param[in] lb_iter the lb iteration * * \return the rank load */ - QOIVariantTypes getRankLoad(Rank rank, PhaseType phase) const { - return rank.getLoad(phase); + QOIVariantTypes getRankLoad( + Rank rank, PhaseType phase, LBIterationType lb_iter + ) const { + return rank.getLoad(phase, lb_iter); } /** @@ -956,12 +1073,17 @@ struct Info { * * \param[in] rank the rank * \param[in] phase the phase + * \param[in] lb_iter the lb iteration * * \return the received volume */ - QOIVariantTypes getRankReceivedVolume(Rank rank, PhaseType phase) const { + QOIVariantTypes getRankReceivedVolume( + Rank rank, PhaseType phase, LBIterationType lb_iter + ) const { auto received_volume = 0.; - auto const& phase_objects = rank.getPhaseWork().at(phase).getObjectWork(); + auto const& phase_objects = lb_iter == no_lb_iter ? + rank.getPhaseWork().at(phase).getObjectWork() : + rank.getPhaseWork().at(phase).getLBIteration(lb_iter).getObjectWork(); for (auto const& [obj_id, obj_work] : phase_objects) { received_volume += obj_work.getReceivedVolume(); } @@ -973,12 +1095,17 @@ struct Info { * * \param[in] rank the rank * \param[in] phase the phase + * \param[in] lb_iter the lb iteration * * \return the sent volume */ - QOIVariantTypes getRankSentVolume(Rank rank, PhaseType phase) const { + QOIVariantTypes getRankSentVolume( + Rank rank, PhaseType phase, LBIterationType lb_iter + ) const { auto sent_volume = 0.; - auto const& phase_objects = rank.getPhaseWork().at(phase).getObjectWork(); + auto const& phase_objects = lb_iter == no_lb_iter ? + rank.getPhaseWork().at(phase).getObjectWork() : + rank.getPhaseWork().at(phase).getLBIteration(lb_iter).getObjectWork(); for (auto const& [obj_id, obj_work] : phase_objects) { sent_volume += obj_work.getSentVolume(); } @@ -990,11 +1117,14 @@ struct Info { * * \param[in] rank the rank * \param[in] phase the phase + * \param[in] lb_iter the lb iteration * * \return the number of objects */ - QOIVariantTypes getRankNumObjects(Rank rank, PhaseType phase) const { - auto num_objects = static_cast(rank.getNumObjects(phase)); + QOIVariantTypes getRankNumObjects( + Rank rank, PhaseType phase, LBIterationType lb_iter + ) const { + auto num_objects = static_cast(rank.getNumObjects(phase, lb_iter)); return num_objects; } @@ -1003,13 +1133,17 @@ struct Info { * * \param[in] rank the rank * \param[in] phase the phase + * \param[in] lb_iter the lb iteration * * \return the number of migratable objects */ - QOIVariantTypes - getRankNumMigratableObjects(Rank rank, PhaseType phase) const { + QOIVariantTypes getRankNumMigratableObjects( + Rank rank, PhaseType phase, LBIterationType lb_iter + ) const { auto num_migratable_objects = 0; - auto const& phase_objects = rank.getPhaseWork().at(phase).getObjectWork(); + auto const& phase_objects = lb_iter == no_lb_iter ? + rank.getPhaseWork().at(phase).getObjectWork() : + rank.getPhaseWork().at(phase).getLBIteration(lb_iter).getObjectWork(); for (auto const& [obj_id, _] : phase_objects) { if (object_info_.at(obj_id).isMigratable()) { num_migratable_objects++; @@ -1023,12 +1157,17 @@ struct Info { * * \param[in] rank the rank * \param[in] phase the phase + * \param[in] lb_iter the lb iteration * * \return the total load of migratable objects */ - QOIVariantTypes getRankMigratableLoad(Rank rank, PhaseType phase) const { + QOIVariantTypes getRankMigratableLoad( + Rank rank, PhaseType phase, LBIterationType lb_iter + ) const { auto migratable_load = 0.; - auto const& phase_objects = rank.getPhaseWork().at(phase).getObjectWork(); + auto const& phase_objects = lb_iter == no_lb_iter ? + rank.getPhaseWork().at(phase).getObjectWork() : + rank.getPhaseWork().at(phase).getLBIteration(lb_iter).getObjectWork(); for (auto const& [obj_id, obj_work] : phase_objects) { if (object_info_.at(obj_id).isMigratable()) { migratable_load += obj_work.getLoad(); @@ -1042,12 +1181,17 @@ struct Info { * * \param[in] rank the rank * \param[in] phase the phase + * \param[in] lb_iter the lb iteration * * \return the total load of sentinel objects */ - QOIVariantTypes getRankSentinelLoad(Rank rank, PhaseType phase) const { + QOIVariantTypes getRankSentinelLoad( + Rank rank, PhaseType phase, LBIterationType lb_iter + ) const { auto sentinel_load = 0.; - auto const& phase_objects = rank.getPhaseWork().at(phase).getObjectWork(); + auto const& phase_objects = lb_iter == no_lb_iter ? + rank.getPhaseWork().at(phase).getObjectWork() : + rank.getPhaseWork().at(phase).getLBIteration(lb_iter).getObjectWork(); for (auto const& [obj_id, obj_work] : phase_objects) { if (object_info_.at(obj_id).isSentinel()) { sentinel_load += obj_work.getLoad(); diff --git a/src/vt-tv/api/phase_work.h b/src/vt-tv/api/phase_work.h index 242662646..581f0b30c 100644 --- a/src/vt-tv/api/phase_work.h +++ b/src/vt-tv/api/phase_work.h @@ -52,21 +52,21 @@ namespace vt::tv { /** - * \struct PhaseWork + * \struct WorkDistribution * - * \brief The work for a given phase + * \brief The work distribution for a phase or iteration */ -struct PhaseWork { - PhaseWork() = default; +struct WorkDistribution { + WorkDistribution() = default; /** - * \brief Construct phase work + * \brief Construct work distribution * - * \param[in] in_phase the phase + * \param[in] in_phase the phase for the work distribution * \param[in] in_objects objects' work for the phase * \param[in] in_user_defined the user-defined fields in json */ - PhaseWork( + WorkDistribution( PhaseType in_phase, std::unordered_map in_objects, std::unordered_map in_user_defined = {} @@ -174,6 +174,133 @@ struct PhaseWork { std::unordered_map user_defined_; }; +/** + * \struct LBIteration + * + * \brief An LB iteration within a phase where the distribution is changing + */ +struct LBIteration : WorkDistribution { + LBIteration() = default; + + /** + * \brief Construct a LB iteration + * + * \param[in] in_phase the phase + * \param[in] in_lb_iteration the LB iteration + * \param[in] in_objects objects' work for the phase + * \param[in] in_user_defined the user-defined fields in json + */ + LBIteration( + PhaseType in_phase, + LBIterationType in_lb_iteration, + std::unordered_map in_objects, + std::unordered_map in_user_defined = {} + ) : WorkDistribution(in_phase, std::move(in_objects), std::move(in_user_defined)), + lb_iteration_(in_lb_iteration) + { } + + /** + * \brief Get the LB iteration ID + * + * \return the LB iteration ID + */ + LBIterationType getLBIterationID() const { return lb_iteration_; } + + /** + * \brief Serializer for data + * + * \param[in] s the serializer + */ + template + void serialize(SerializerT& s) { + WorkDistribution::serialize(s); + s | lb_iteration_; + } + +private: + LBIterationType lb_iteration_ = 0; /**< The LB iteration */ +}; + +/** + * \struct PhaseWork + * + * \brief The work for a phase which may include multiple subsequent LB + * iterations + */ +struct PhaseWork : WorkDistribution { + PhaseWork() = default; + + /** + * \brief Construct a phase work + * + * \param[in] in_phase the phase + * \param[in] in_objects objects' work for the phase + * \param[in] in_user_defined the user-defined fields in json + */ + PhaseWork( + PhaseType in_phase, + std::unordered_map in_objects, + std::unordered_map in_user_defined = {} + ) : WorkDistribution(in_phase, std::move(in_objects), std::move(in_user_defined)) + { } + + /** + * \brief Add an LB iteration + * + * \param[in] lb_iter_id the iteration ID + * \param[in] lb_iter the work distribution for the iteration + */ + void addLBIteration(LBIterationType lb_iter_id, LBIteration lb_iter) { + lb_iters_.emplace(lb_iter_id, std::move(lb_iter)); + } + + /** + * \brief Get all LB iterations + * + * \return the const LB iterations + */ + auto const& getLBIterations() const { return lb_iters_; } + + /** + * \brief Get a LB iteration + * + * \param[in] lb_iter_id the LB iteration ID + * + * \return ref to the lb iteration + */ + LBIteration& getLBIteration(LBIterationType lb_iter_id) { + return lb_iters_.find(lb_iter_id)->second; + } + + /** + * \brief Get a const LB iteration + * + * \param[in] lb_iter_id the LB iteration ID + * + * \return ref to the lb iteration + */ + LBIteration const& getLBIteration(LBIterationType lb_iter_id) const { + return lb_iters_.find(lb_iter_id)->second; + } + + /** + * \brief Serializer for data + * + * \param[in] s the serializer + */ + template + void serialize(SerializerT& s) { + WorkDistribution::serialize(s); + s | lb_iters_; + } + +private: + /// LB iterations for this phase + std::unordered_map lb_iters_; +}; + + + } /* end namespace vt::tv */ #endif /*INCLUDED_VT_TV_API_PHASE_WORK_H*/ diff --git a/src/vt-tv/api/rank.h b/src/vt-tv/api/rank.h index e7a77918e..e9cc12768 100644 --- a/src/vt-tv/api/rank.h +++ b/src/vt-tv/api/rank.h @@ -96,8 +96,12 @@ struct Rank { * * \return the load */ - double getLoad(PhaseType phase) const { - return phase_info_.at(phase).getLoad(); + double getLoad(PhaseType phase, LBIterationType lb_iter) const { + if (lb_iter == no_lb_iter) { + return phase_info_.at(phase).getLoad(); + } else { + return phase_info_.at(phase).getLBIteration(lb_iter).getLoad(); + } } /** @@ -114,8 +118,12 @@ struct Rank { * * \return the number of objects */ - uint64_t getNumObjects(PhaseType phase) const { - return phase_info_.at(phase).getObjectWork().size(); + uint64_t getNumObjects(PhaseType phase, LBIterationType lb_iter) const { + if (lb_iter == no_lb_iter) { + return phase_info_.at(phase).getObjectWork().size(); + } else { + return phase_info_.at(phase).getLBIteration(lb_iter).getObjectWork().size(); + } } /** diff --git a/src/vt-tv/api/types.h b/src/vt-tv/api/types.h index 1c1241a73..86b7f0b3e 100644 --- a/src/vt-tv/api/types.h +++ b/src/vt-tv/api/types.h @@ -51,6 +51,7 @@ namespace vt::tv { using PhaseType = uint64_t; +using LBIterationType = uint64_t; using NodeType = int16_t; using ElementIDType = uint64_t; using SubphaseType = uint16_t; @@ -61,6 +62,8 @@ using CollectionObjGroupIDType = uint64_t; /// Possible QOIs types using QOIVariantTypes = std::variant; +constexpr LBIterationType no_lb_iter = static_cast(-1); + } /* end namespace vt::tv */ #endif /*INCLUDED_VT_TV_API_TYPES_H*/ diff --git a/src/vt-tv/render/render.cc b/src/vt-tv/render/render.cc index 199b11e7f..a1b7dc66b 100644 --- a/src/vt-tv/render/render.cc +++ b/src/vt-tv/render/render.cc @@ -81,14 +81,14 @@ Render::Render(Info in_info) max_o_per_dim_ = 0; // Set the info selected_phase - this->info_.setSelectedPhase(selected_phase_); + info_.setSelectedPhase(selected_phase_); // Normalize communication edges if (selected_phase_ != std::numeric_limits::max()) { - this->info_.normalizeEdges(selected_phase_); + info_.normalizeEdges(selected_phase_); } else { - for (PhaseType phase = 0; phase < this->n_phases_; phase++) { - this->info_.normalizeEdges(phase); + for (PhaseType phase = 0; phase < n_phases_; phase++) { + info_.normalizeEdges(phase); } } @@ -98,7 +98,7 @@ Render::Render(Info in_info) for (auto const& objectID : allObjects) { std::array jitterDims; for (uint64_t d = 0; d < 3; d++) { - if (auto f = this->rank_dims_.find(d); f != this->rank_dims_.end()) { + if (auto f = rank_dims_.find(d); f != rank_dims_.end()) { jitterDims[d] = ((double)std::rand() / RAND_MAX - 0.5) * object_jitter_; } else jitterDims[d] = 0; @@ -106,10 +106,10 @@ Render::Render(Info in_info) jitter_dims_.insert(std::make_pair(objectID, jitterDims)); } - object_qoi_range_ = this->computeObjectQOIRange_(); - rank_qoi_range_ = this->computeRankQOIRange_(); - object_volume_max_ = this->computeMaxObjectVolume_(); - object_load_max_ = this->info_.getMaxLoad(); + object_qoi_range_ = computeObjectQOIRange_(); + rank_qoi_range_ = computeRankQOIRange_(); + object_volume_max_ = computeMaxObjectVolume_(); + object_load_max_ = info_.getMaxLoad(); }; Render::Render( @@ -149,14 +149,14 @@ Render::Render( max_o_per_dim_ = 0; // Set the info selected_phase - this->info_.setSelectedPhase(selected_phase_); + info_.setSelectedPhase(selected_phase_); // Normalize communication edges if (selected_phase_ != std::numeric_limits::max()) { - this->info_.normalizeEdges(selected_phase_); + info_.normalizeEdges(selected_phase_); } else { - for (PhaseType phase = 0; phase < this->n_phases_; phase++) { - this->info_.normalizeEdges(phase); + for (PhaseType phase = 0; phase < n_phases_; phase++) { + info_.normalizeEdges(phase); } } @@ -166,7 +166,7 @@ Render::Render( for (auto const& objectID : allObjects) { std::array jitterDims; for (uint64_t d = 0; d < 3; d++) { - if (auto f = this->rank_dims_.find(d); f != this->rank_dims_.end()) { + if (auto f = rank_dims_.find(d); f != rank_dims_.end()) { jitterDims[d] = ((double)std::rand() / RAND_MAX - 0.5) * object_jitter_; } else jitterDims[d] = 0; @@ -174,14 +174,14 @@ Render::Render( jitter_dims_.insert(std::make_pair(objectID, jitterDims)); } - object_qoi_range_ = this->computeObjectQOIRange_(); - rank_qoi_range_ = this->computeRankQOIRange_(); - object_volume_max_ = this->computeMaxObjectVolume_(); - object_load_max_ = this->info_.getMaxLoad(); + object_qoi_range_ = computeObjectQOIRange_(); + rank_qoi_range_ = computeRankQOIRange_(); + object_volume_max_ = computeMaxObjectVolume_(); + object_load_max_ = info_.getMaxLoad(); }; double Render::computeMaxObjectVolume_() { - double ov_max = this->info_.getMaxVolume(); + double ov_max = info_.getMaxVolume(); return ov_max; } @@ -193,10 +193,14 @@ Render::computeObjectQOIRange_() { std::set> oq_all; // Update the QOI range - auto updateQOIRange = [&](auto const& objects, PhaseType phase) { + auto updateQOIRange = [&]( + auto const& objects, PhaseType phase, LBIterationType lb_iter + ) { for (auto const& [obj_id, obj_work] : objects) { // Update maximum object qoi - auto oq = info_.getObjectQOIAtPhase(obj_id, phase, this->object_qoi_); + auto oq = info_.getObjectQOIAtPhase( + obj_id, phase, lb_iter, object_qoi_ + ); if (!continuous_object_qoi_) { // Allow for integer categorical QOI (i.e. rank_id) if (oq == static_cast(oq)) { @@ -218,17 +222,29 @@ Render::computeObjectQOIRange_() { // Iterate over all ranks if (selected_phase_ != std::numeric_limits::max()) { - auto const& objects = this->info_.getPhaseObjects(selected_phase_); - updateQOIRange(objects, selected_phase_); + auto const& objects = info_.getPhaseObjects(selected_phase_, no_lb_iter); + updateQOIRange(objects, selected_phase_, no_lb_iter); + auto const& lb_iters = + info_.getRank(0).getPhaseWork().at(selected_phase_).getLBIterations(); + for (auto const& [lb_iter_id, _] : lb_iters) { + auto const& objects2 = info_.getPhaseObjects(selected_phase_, lb_iter_id); + updateQOIRange(objects2, selected_phase_, lb_iter_id); + } } else { - for (PhaseType phase = 0; phase < this->n_phases_; phase++) { - auto const& objects = this->info_.getPhaseObjects(phase); - updateQOIRange(objects, phase); + for (PhaseType phase = 0; phase < n_phases_; phase++) { + auto const& objects = info_.getPhaseObjects(phase, no_lb_iter); + updateQOIRange(objects, phase, no_lb_iter); + auto const& lb_iters = + info_.getRank(0).getPhaseWork().at(phase).getLBIterations(); + for (auto const& [lb_iter_id, _] : lb_iters) { + auto const& objects2 = info_.getPhaseObjects(phase, lb_iter_id); + updateQOIRange(objects2, phase, lb_iter_id); + } } } // Update extrema attribute - this->object_qoi_max_ = oq_max; + object_qoi_max_ = oq_max; if (continuous_object_qoi_) { // return range @@ -247,24 +263,20 @@ std::pair Render::computeRankQOIRange_() { double rqmin_for_phase; // Iterate over all ranks - for (uint64_t rank_id = 0; rank_id < this->n_ranks_; rank_id++) { - auto rank_qoi_map = info_.getAllQOIAtRank(rank_id, this->rank_qoi_); + for (uint64_t rank_id = 0; rank_id < n_ranks_; rank_id++) { + auto rank_qoi_vec = info_.getAllQOIAtRank(rank_id, rank_qoi_); // Get max qoi for this rank across all phases auto prmax = std::max_element( - std::begin(rank_qoi_map), - std::end(rank_qoi_map), - [](auto const& p1, auto const& p2) { return p1.second < p2.second; } + std::begin(rank_qoi_vec), std::end(rank_qoi_vec) ); - rqmax_for_phase = prmax->second; + rqmax_for_phase = *prmax; // Get min qoi for this rank across all phases - auto prmin = std::max_element( - std::begin(rank_qoi_map), - std::end(rank_qoi_map), - [](auto const& p1, auto const& p2) { return p1.second > p2.second; } + auto prmin = std::min_element( + std::begin(rank_qoi_vec), std::end(rank_qoi_vec) ); - rqmin_for_phase = prmin->second; + rqmin_for_phase = *prmin; if (rqmax_for_phase > rq_max) rq_max = rqmax_for_phase; @@ -276,32 +288,34 @@ std::pair Render::computeRankQOIRange_() { return std::make_pair(rq_min, rq_max); } -double Render::computeRankQOIAverage_(PhaseType phase, std::string qoi) { - // Initialize rank QOI range attributes - double rq_sum = 0.0; - auto const& rank_loads_at_phase = - this->info_.getAllRankQOIAtPhase(phase, qoi); - for (auto const& [rank, rank_load] : rank_loads_at_phase) { - rq_sum += rank_load; - } - return rq_sum / rank_loads_at_phase.size(); -} +// double Render::computeRankQOIAverage_(PhaseType phase, std::string qoi) { +// // Initialize rank QOI range attributes +// double rq_sum = 0.0; +// auto const& rank_loads_at_phase = +// this->info_.getAllRankQOIAtPhase(phase, qoi); +// for (auto const& [rank, rank_load] : rank_loads_at_phase) { +// rq_sum += rank_load; +// } +// return rq_sum / rank_loads_at_phase.size(); +// } std::map> -Render::createObjectMapping_(PhaseType phase) { +Render::createObjectMapping_(PhaseType phase, LBIterationType lb_iter) { std::map> object_mapping; - // Add each rank and its corresponding objects at the given phase to the object mapping - for (uint64_t rank_id = 0; rank_id < this->n_ranks_; rank_id++) { + // Add each rank and its corresponding objects at the given phase to the + // object mapping + for (uint64_t rank_id = 0; rank_id < n_ranks_; rank_id++) { object_mapping.insert( - std::make_pair(rank_id, this->info_.getRankObjects(rank_id, phase))); + std::make_pair(rank_id, info_.getRankObjects(rank_id, phase, lb_iter)) + ); } return object_mapping; } template vtkNew Render::createRankArrayUserDefined( - PhaseType phase, std::string const& key + PhaseType phase, LBIterationType lb_iter, std::string const& key ) { vtkNew array; std::string array_name = key; @@ -310,7 +324,9 @@ vtkNew Render::createRankArrayUserDefined( for (uint64_t rank_id = 0; rank_id < n_ranks_; rank_id++) { auto const& cur_rank_info = info_.getRanks().at(rank_id); - auto const& value = info_.getRankUserDefined(cur_rank_info, phase, key); + auto const& value = info_.getRankUserDefined( + cur_rank_info, phase, lb_iter, key + ); //fmt::print("phase={}, key={}, rank_id={}\n", phase, key, rank_id); array->SetTuple1(rank_id, std::get(value)); } @@ -319,7 +335,7 @@ vtkNew Render::createRankArrayUserDefined( template vtkNew Render::createRankArrayComputed( - PhaseType phase, std::string const& key + PhaseType phase, LBIterationType lb_iter, std::string const& key ) { vtkNew array; std::string array_name = key; @@ -327,12 +343,16 @@ vtkNew Render::createRankArrayComputed( array->SetNumberOfTuples(n_ranks_); for (uint64_t rank_id = 0; rank_id < n_ranks_; rank_id++) { - array->SetTuple1(rank_id, info_.getRankQOIAtPhase(rank_id, phase, key)); + array->SetTuple1( + rank_id, info_.getRankQOIAtPhase(rank_id, phase, lb_iter, key) + ); } return std::move(array); } -vtkNew Render::createRankMesh_(PhaseType phase) { +vtkNew Render::createRankMesh_( + PhaseType phase, LBIterationType lb_iter +) { fmt::print("\n\n"); fmt::print("----- Creating rank mesh for phase {} -----\n", phase); vtkNew rank_points_; @@ -362,11 +382,13 @@ vtkNew Render::createRankMesh_(PhaseType phase) { auto const& test_value = info_.getFirstRankUserDefined(rank_qoi_); if (std::holds_alternative(test_value)) { pd_mesh->GetPointData()->SetScalars( - createRankArrayUserDefined(phase, rank_qoi_) + createRankArrayUserDefined( + phase, lb_iter, rank_qoi_ + ) ); } else if (std::holds_alternative(test_value)) { pd_mesh->GetPointData()->SetScalars( - createRankArrayUserDefined(phase, rank_qoi_) + createRankArrayUserDefined(phase, lb_iter, rank_qoi_) ); } } else { @@ -377,11 +399,13 @@ vtkNew Render::createRankMesh_(PhaseType phase) { VtkTypeEnum type = iter->second; if (type == VtkTypeEnum::TYPE_DOUBLE) { pd_mesh->GetPointData()->SetScalars( - createRankArrayComputed(phase, rank_qoi_) + createRankArrayComputed( + phase, lb_iter, rank_qoi_ + ) ); } else if (type == VtkTypeEnum::TYPE_INT) { pd_mesh->GetPointData()->SetScalars( - createRankArrayComputed(phase, rank_qoi_) + createRankArrayComputed(phase, lb_iter, rank_qoi_) ); } } @@ -390,14 +414,16 @@ vtkNew Render::createRankMesh_(PhaseType phase) { auto const& rank_info = info_.getRanks().at(0); auto const& keys = info_.getRankUserDefinedKeys(rank_info, phase); for (auto const& key : keys) { - auto const& test_value = info_.getRankUserDefined(rank_info, phase, key); + auto const& test_value = info_.getRankUserDefined( + rank_info, phase, lb_iter, key + ); if (std::holds_alternative(test_value)) { pd_mesh->GetPointData()->AddArray( - createRankArrayUserDefined(phase, key) + createRankArrayUserDefined(phase, lb_iter, key) ); } else if (std::holds_alternative(test_value)) { pd_mesh->GetPointData()->AddArray( - createRankArrayUserDefined(phase, key) + createRankArrayUserDefined(phase, lb_iter, key) ); } } @@ -407,8 +433,9 @@ vtkNew Render::createRankMesh_(PhaseType phase) { } bool compareObjects( - const std::pair& p1, - const std::pair& p2) { + std::pair const& p1, + std::pair const& p2 +) { ElementIDType lhsID = p1.first.getID(); ElementIDType rhsID = p2.first.getID(); uint64_t migratableLhs = p1.second; @@ -419,21 +446,26 @@ bool compareObjects( return lhsID < rhsID; // then sort by ID } -vtkNew Render::createObjectMesh_(PhaseType phase) { +vtkNew Render::createObjectMesh_( + PhaseType phase, LBIterationType lb_iter +) { fmt::print("\n\n"); - fmt::print("----- Creating object mesh for phase {} -----\n", phase); + fmt::print( + "----- Creating object mesh for (phase,lb_iter) ({},{}) -----\n", + phase, lb_iter + ); // Retrieve number of mesh points and bail out early if empty set - uint64_t n_o = this->info_.getPhaseObjects(phase).size(); + uint64_t n_o = info_.getPhaseObjects(phase, lb_iter).size(); fmt::print(" Number of objects in phase: {}\n", n_o); // Create point array for object quantity of interest vtkNew q_arr; - q_arr->SetName(this->object_qoi_.c_str()); + q_arr->SetName(object_qoi_.c_str()); q_arr->SetNumberOfTuples(n_o); // Load array must be added when it is not the object QOI vtkNew l_arr; - if (this->object_qoi_ != "load") { + if (object_qoi_ != "load") { l_arr->SetName("load"); l_arr->SetNumberOfTuples(n_o); } @@ -448,7 +480,7 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { points->SetNumberOfPoints(n_o); // Retrieve elements constant across all ranks - std::string object_qoi = this->object_qoi_; + std::string object_qoi = object_qoi_; // Iterate over ranks and objects to create mesh points uint64_t point_index = 0; @@ -456,33 +488,33 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { // sent_volumes is a vector to store the communications ("from" object id, "sent to" object id, and volume) std::vector> sent_volumes; - auto object_mapping = this->createObjectMapping_(phase); + auto object_mapping = createObjectMapping_(phase, lb_iter); std::map qoi_map; // Iterate through object mapping for (auto const& [rankID, objects] : object_mapping) { std::array ijk = - this->globalIDToCartesian_(rankID, this->grid_size_); + globalIDToCartesian_(rankID, grid_size_); std::array offsets = { - ijk[0] * this->grid_resolution_, - ijk[1] * this->grid_resolution_, - ijk[2] * this->grid_resolution_}; + ijk[0] * grid_resolution_, + ijk[1] * grid_resolution_, + ijk[2] * grid_resolution_}; // Compute local object block parameters uint64_t n_o_rank = objects.size(); - uint64_t n_o_per_dim = ceil(pow(n_o_rank, 1.0 / this->rank_dims_.size())); - if (n_o_per_dim > this->max_o_per_dim_) { - this->max_o_per_dim_ = n_o_per_dim; + uint64_t n_o_per_dim = ceil(pow(n_o_rank, 1.0 / rank_dims_.size())); + if (n_o_per_dim > max_o_per_dim_) { + max_o_per_dim_ = n_o_per_dim; } - double o_resolution = this->grid_resolution_ / (n_o_per_dim + 1.); + double o_resolution = grid_resolution_ / (n_o_per_dim + 1.); // Create point coordinates std::array rank_size = {1, 1, 1}; for (uint64_t d = 0; d < 3; d++) { - if (auto f = this->rank_dims_.find(d); f != this->rank_dims_.end()) { + if (auto f = rank_dims_.find(d); f != rank_dims_.end()) { rank_size[d] = n_o_per_dim; } else rank_size[d] = 1; @@ -490,7 +522,7 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { std::array centering = {0, 0, 0}; for (uint64_t d = 0; d < 3; d++) { - if (auto f = this->rank_dims_.find(d); f != this->rank_dims_.end()) { + if (auto f = rank_dims_.find(d); f != rank_dims_.end()) { centering[d] = 0.5 * o_resolution * (n_o_per_dim - 1.0); } else { centering[d] = 0.0; @@ -500,7 +532,7 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { std::vector> ordered_objects; for (auto const& [objectID, objectWork] : objects) { - bool migratable = this->info_.getObjectInfo().at(objectID).isMigratable(); + bool migratable = info_.getObjectInfo().at(objectID).isMigratable(); ordered_objects.push_back(std::make_pair(objectWork, migratable)); for (auto const& [key, _] : objectWork.getUserDefined()) { auto const& value = objectWork.getUserDefined().at(key); @@ -523,7 +555,7 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { // Insert point using offset and rank coordinates std::array currentPointPosition = {0, 0, 0}; int d = 0; - for (auto c : this->globalIDToCartesian_(i, rank_size)) { + for (auto c : globalIDToCartesian_(i, rank_size)) { currentPointPosition[d] = offsets[d] - centering[d] + (jitter_dims_.at(objectWork.getID())[d] + c) * o_resolution; d++; @@ -537,10 +569,12 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { // Set object attributes ElementIDType obj_id = objectWork.getID(); - auto oq = info_.getObjectQOIAtPhase(obj_id, phase, object_qoi_); + auto oq = info_.getObjectQOIAtPhase( + obj_id, phase, lb_iter, object_qoi_ + ); q_arr->SetTuple1(point_index, oq); b_arr->SetTuple1(point_index, migratable); - if (this->object_qoi_ != "load") { + if (object_qoi_ != "load") { l_arr->SetTuple1(point_index, objectWork.getLoad()); } @@ -601,16 +635,16 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { pd_mesh->SetLines(lines); pd_mesh->GetPointData()->SetScalars(q_arr); pd_mesh->GetPointData()->AddArray(b_arr); - if (this->object_qoi_ != "load") { + if (object_qoi_ != "load") { pd_mesh->GetPointData()->AddArray(l_arr); } pd_mesh->GetCellData()->SetScalars(lineValuesArray); for (auto const& [key, vtk_type] : qoi_map) { if (vtk_type == VtkTypeEnum::TYPE_DOUBLE) { - addObjectArray(pd_mesh, phase, key); + addObjectArray(pd_mesh, phase, lb_iter, key); } else if (vtk_type == VtkTypeEnum::TYPE_INT) { - addObjectArray(pd_mesh, phase, key); + addObjectArray(pd_mesh, phase, lb_iter, key); } } @@ -621,16 +655,18 @@ vtkNew Render::createObjectMesh_(PhaseType phase) { template void Render::addObjectArray( - vtkNew& pd_mesh, PhaseType phase, std::string const& key + vtkNew& pd_mesh, PhaseType phase, LBIterationType lb_iter, + std::string const& key ) { - auto const num_objects = info_.getPhaseObjects(phase).size(); + auto const num_objects = info_.getPhaseObjects(phase, lb_iter).size(); vtkNew array; array->SetName(key.c_str()); array->SetNumberOfTuples(num_objects); int point_index = 0; - auto object_mapping = createObjectMapping_(phase); + auto object_mapping = createObjectMapping_(phase, lb_iter); + for (auto const& [rankID, objects] : object_mapping) { std::vector> ordered_objects; for (auto const& [objectID, objectWork] : objects) { @@ -781,7 +817,8 @@ Render::createColorTransferFunction_( double x, double y, uint64_t font_size, - std::set> values) { + std::set> values) + { vtkSmartPointer scalar_bar_actor = vtkSmartPointer::New(); scalar_bar_actor->SetLookupTable(mapper->GetLookupTable()); @@ -833,7 +870,8 @@ Render::createColorTransferFunction_( } /* static */ std::array Render::globalIDToCartesian_( - uint64_t flat_id, std::array grid_sizes) { + uint64_t flat_id, std::array grid_sizes +) { std::array cartesian = {0, 0, 0}; // Sanity check uint64_t n01 = grid_sizes[0] * grid_sizes[1]; @@ -862,7 +900,8 @@ Render::createColorTransferFunction_( /* static */ vtkSmartPointer Render::createRanksMapper_( vtkPolyData* rank_mesh, std::variant, std::set>> - rank_qoi_range) { + rank_qoi_range +) { // Create square glyphs at ranks vtkSmartPointer rank_glyph = vtkSmartPointer::New(); @@ -920,6 +959,8 @@ Render::createColorTransferFunction_( void Render::renderPNG( PhaseType phase, + LBIterationType lb_iter, + int cur_frame, vtkPolyData* rank_mesh, vtkPolyData* object_mesh, uint64_t edge_width, @@ -927,7 +968,8 @@ void Render::renderPNG( uint64_t win_size, uint64_t font_size, std::string output_dir, - std::string output_file_stem) { + std::string output_file_stem +) { // Setup rendering space vtkSmartPointer renderer = setupRenderer_(); @@ -940,7 +982,7 @@ void Render::renderPNG( rank_actor->SetMapper(rank_mapper); // Create qoi scale legend for ranks - std::string rank_qoi_scale_title = "Rank " + this->rank_qoi_; + std::string rank_qoi_scale_title = "Rank " + rank_qoi_; vtkSmartPointer rank_qoi_scale_actor = createScalarBarActor_( rank_mapper, rank_qoi_scale_title, 0.5, 0.9, font_size); @@ -953,11 +995,11 @@ void Render::renderPNG( renderer->AddActor(rank_actor); renderer->AddActor2D(rank_qoi_scale_actor); - if (this->object_qoi_ != "") { + if (object_qoi_ != "") { // Create white to black lookup table vtkSmartPointer bw_lut = vtkSmartPointer::New(); - bw_lut->SetTableRange(0.0, this->object_volume_max_); + bw_lut->SetTableRange(0.0, object_volume_max_); bw_lut->SetSaturationRange(0, 0); bw_lut->SetHueRange(0, 0); bw_lut->SetValueRange(1, 0); @@ -969,7 +1011,7 @@ void Render::renderPNG( vtkSmartPointer::New(); edge_mapper->SetInputData(object_mesh); edge_mapper->SetScalarModeToUseCellData(); - edge_mapper->SetScalarRange(0.0, this->object_volume_max_); + edge_mapper->SetScalarRange(0.0, object_volume_max_); edge_mapper->SetLookupTable(bw_lut); // Create communication volume and its scalar bar actors @@ -1000,6 +1042,7 @@ void Render::renderPNG( {1.0, vtkSmartPointer::New()}}; std::map glyph_types = { {0.0, "Square"}, {1.0, "Circle"}}; + for (const auto& [k, v] : glyph_types) { vtkSmartPointer thresh = vtkSmartPointer::New(); @@ -1032,8 +1075,7 @@ void Render::renderPNG( glypher->SetScaleModeToScaleByScalar(); glypher->SetScaleFactor(glyph_factor); glypher->Update(); - glypher->GetOutput()->GetPointData()->SetActiveScalars( - this->object_qoi_.c_str()); + glypher->GetOutput()->GetPointData()->SetActiveScalars(object_qoi_.c_str()); vtkSmartPointer zRaise = vtkSmartPointer::New(); @@ -1046,13 +1088,13 @@ void Render::renderPNG( glyph_mappers.at(k)->SetInputConnection(trans->GetOutputPort()); glyph_mappers.at(k)->SetLookupTable( - createColorTransferFunction_(this->object_qoi_range_)); + createColorTransferFunction_(object_qoi_range_)); - if (std::holds_alternative>( - this->object_qoi_range_)) { + if (std::holds_alternative>(object_qoi_range_)) { auto range = - std::get>(this->object_qoi_range_); - // Manually set scalar range so either mapper (migratable vs non-migratable) can be used for the scalar bar range + std::get>(object_qoi_range_); + // Manually set scalar range so either mapper (migratable vs + // non-migratable) can be used for the scalar bar range glyph_mappers.at(k)->SetScalarRange(range.first, range.second); } @@ -1065,16 +1107,18 @@ void Render::renderPNG( } if (glyph_mappers.at(1.0)) { - std::string object_qoi_name = "Object " + this->object_qoi_; + std::string object_qoi_name = "Object " + object_qoi_; std::set> values = {}; // Check continuity of object qoi - if (std::holds_alternative>( - this->object_qoi_range_)) { + if (std::holds_alternative>(object_qoi_range_)) { values = {}; - } else if (std::holds_alternative>>( - this->object_qoi_range_)) { + } else if ( + std::holds_alternative>>( + object_qoi_range_ + ) + ) { values = std::get>>( - this->object_qoi_range_); + object_qoi_range_); } else { throw std::runtime_error( "Unexpected type in object_qoi_range variant."); @@ -1086,7 +1130,8 @@ void Render::renderPNG( 0.52, 0.04, font_size, - values); + values + ); renderer->AddActor2D(object_qoi_scalar_bar_actor); } } @@ -1095,12 +1140,19 @@ void Render::renderPNG( // Create text std::stringstream ss; if (selected_phase_ != std::numeric_limits::max()) { - ss << "Phase: " << phase << "\n"; + ss << "Phase: " << phase; } else { - ss << "Phase: " << phase << "/" << (this->n_phases_ - 1) << "\n"; + ss << "Phase: " << phase << "/" << (n_phases_ - 1); } + if (lb_iter != no_lb_iter) { + auto const n_lb_iters = + info_.getRank(0).getPhaseWork().at(phase).getLBIterations().size(); + ss << ", Iter: " << lb_iter << "/" << n_lb_iters; + } + ss << "\n"; ss << "Load Imbalance: " << std::fixed << std::setprecision(2) - << this->info_.getImbalance(phase); + << info_.getImbalance(phase, lb_iter); + // Setup text actor vtkSmartPointer text_actor = vtkSmartPointer::New(); @@ -1136,7 +1188,7 @@ void Render::renderPNG( vtkNew writer; writer->SetInputConnection(w2i->GetOutputPort()); std::string png_filename = - output_dir + output_file_stem + std::to_string(phase) + ".png"; + output_dir + output_file_stem + std::to_string(cur_frame) + ".png"; writer->SetFileName(png_filename.c_str()); writer->SetCompressionLevel(2); writer->Write(); @@ -1158,35 +1210,43 @@ void Render::generate(uint64_t font_size, uint64_t win_size) { fmt::print("selected phase={}\n", selected_phase_); - auto createMeshAndRender = [&](PhaseType phase) { - vtkNew object_mesh = this->createObjectMesh_(phase); - vtkNew rank_mesh = this->createRankMesh_(phase); + auto createMeshAndRender = [&]( + PhaseType phase, LBIterationType lb_iter, int& cur_frame + ) { + vtkNew object_mesh = createObjectMesh_(phase, lb_iter); + vtkNew rank_mesh = createRankMesh_(phase, lb_iter); if (save_meshes_) { - fmt::print("== Writing object mesh for phase {}\n", phase); + fmt::print( + "== Writing object mesh for (phase,lb_iter)= ({},{})\n", phase, lb_iter + ); vtkNew writer; std::string object_mesh_filename = output_dir_ + output_file_stem_ + - "_object_mesh_" + std::to_string(phase) + ".vtp"; + "_object_mesh_" + std::to_string(cur_frame) + ".vtp"; writer->SetFileName(object_mesh_filename.c_str()); writer->SetInputData(object_mesh); writer->Write(); - fmt::print("== Writing rank mesh for phase {}\n", phase); + fmt::print( + "== Writing rank mesh for (phase,lb_iter)= ({},{})\n", phase, lb_iter + ); vtkNew writer2; std::string rank_mesh_filneame = output_dir_ + output_file_stem_ + - "_rank_mesh_" + std::to_string(phase) + ".vtp"; + "_rank_mesh_" + std::to_string(cur_frame) + ".vtp"; writer2->SetFileName(rank_mesh_filneame.c_str()); writer2->SetInputData(rank_mesh); writer2->Write(); } if (save_pngs_) { - fmt::print("== Rendering visualization PNG for phase {}\n", phase); + fmt::print( + "== Rendering visualization PNG for (phase,lb_iter)= ({},{})\n", + phase, lb_iter + ); std::pair obj_qoi_range; try { - obj_qoi_range = - std::get>(this->object_qoi_range_); + obj_qoi_range = std::get>(object_qoi_range_); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; obj_qoi_range = {0, 1}; @@ -1194,13 +1254,16 @@ void Render::generate(uint64_t font_size, uint64_t win_size) { uint64_t window_size = win_size; uint64_t edge_width = 0.03 * window_size / - *std::max_element(this->grid_size_.begin(), this->grid_size_.end()); - double glyph_factor = 0.8 * this->grid_resolution_ / - ((this->max_o_per_dim_ + 1) * std::sqrt(object_load_max_)); + *std::max_element(grid_size_.begin(), grid_size_.end()); + double glyph_factor = 0.8 * grid_resolution_ / + ((max_o_per_dim_ + 1) * std::sqrt(object_load_max_)); fmt::print(" Image size: {}x{}px\n", win_size, win_size); fmt::print(" Font size: {}pt\n", font_size); - this->renderPNG( + + renderPNG( phase, + lb_iter, + cur_frame, rank_mesh, object_mesh, edge_width, @@ -1208,15 +1271,29 @@ void Render::generate(uint64_t font_size, uint64_t win_size) { window_size, font_size, output_dir_, - output_file_stem_); + output_file_stem_ + ); } + + cur_frame++; }; + int cur_frame = 0; if (selected_phase_ != std::numeric_limits::max()) { - createMeshAndRender(selected_phase_); + createMeshAndRender(selected_phase_, no_lb_iter, cur_frame); + auto const& lb_iters = + info_.getRank(0).getPhaseWork().at(selected_phase_).getLBIterations(); + for (auto const& [id, _] : lb_iters) { + createMeshAndRender(selected_phase_, id, cur_frame); + } } else { - for (PhaseType phase = 0; phase < this->n_phases_; phase++) { - createMeshAndRender(phase); + for (PhaseType phase = 0; phase < n_phases_; phase++) { + createMeshAndRender(phase, no_lb_iter, cur_frame); + auto const& lb_iters = + info_.getRank(0).getPhaseWork().at(phase).getLBIterations(); + for (auto const& [id, _] : lb_iters) { + createMeshAndRender(phase, id, cur_frame); + } } } } diff --git a/src/vt-tv/render/render.h b/src/vt-tv/render/render.h index e331c1e06..5adcd9794 100644 --- a/src/vt-tv/render/render.h +++ b/src/vt-tv/render/render.h @@ -171,75 +171,89 @@ struct Render { */ std::pair computeRankQOIRange_(); - /** - * \brief Compute average of rank qoi. - * - * @param qoi quantity of interest on which to compute average - * - * \return rank qoi range - */ - double computeRankQOIAverage_(PhaseType phase, std::string qoi); + // /** + // * \brief Compute average of rank qoi. + // * + // * @param qoi quantity of interest on which to compute average + // * + // * \return rank qoi range + // */ + // double computeRankQOIAverage_(PhaseType phase, std::string qoi); /** * \brief Create mapping of objects in ranks * * \param[in] phase phase index + * \param[in] lb_iter the LB iteration * * \return mapping */ std::map> - createObjectMapping_(PhaseType phase); + createObjectMapping_(PhaseType phase, LBIterationType lb_iter); /** * \brief Map ranks to polygonal mesh. * * \param[in] iteration phase index + * \param[in] lb_iter LB iteration * * \return rank mesh */ - vtkNew createRankMesh_(PhaseType iteration); + vtkNew createRankMesh_( + PhaseType iteration, LBIterationType lb_iter + ); private: /** * \brief Create rank array from user-defined * * \param[in] phase the phase + * \param[in] lb_iter the LB iteration * \param[in] key the QOI key to add */ template - vtkNew createRankArrayUserDefined(PhaseType phase, std::string const& key); + vtkNew createRankArrayUserDefined( + PhaseType phase, LBIterationType lb_iter, std::string const& key + ); /** * \brief Create rank array from computed QOI * * \param[in] phase the phase + * \param[in] lb_iter the LB iteration * \param[in] key the QOI key to add */ template - vtkNew createRankArrayComputed(PhaseType phase, std::string const& key); + vtkNew createRankArrayComputed( + PhaseType phase, LBIterationType lb_iter, std::string const& key + ); /** * \brief Add object array * * \param[in] pd_mesh the mesh * \param[in] phase the phase + * \param[in] lb_iter the LB iteration * \param[in] key the QOI key to add - */ + */ template void addObjectArray( - vtkNew& pd_mesh, PhaseType phase, std::string const& key + vtkNew& pd_mesh, PhaseType phase, LBIterationType lb_iter, + std::string const& key ); - public: /** * \brief Map objects to polygonal mesh. * * \param[in] phase phase + * \param[in] lb_iter the LB iteration * * \return object mesh */ - vtkNew createObjectMesh_(PhaseType phase); + vtkNew createObjectMesh_( + PhaseType phase, LBIterationType lb_iter + ); static void getRgbFromTab20Colormap_(int index, double& r, double& g, double& b); @@ -314,6 +328,8 @@ struct Render { * @brief Export a visualization PNG from meshes. * * @param phase Phase to render. + * @param lb_iter LB iteration to render + * @param cur_frame the current frame number being rendered * @param rank_mesh Mesh data for the ranks. * @param object_mesh Mesh data for the objects. * @param edge_width Width of the edges in the visualization. @@ -326,6 +342,8 @@ struct Render { */ void renderPNG( PhaseType phase, + LBIterationType lb_iter, + int cur_frame, vtkPolyData* rank_mesh, vtkPolyData* object_mesh, uint64_t edge_width, @@ -333,7 +351,8 @@ struct Render { uint64_t win_size, uint64_t font_size, std::string output_dir, - std::string output_file_stem); + std::string output_file_stem + ); void generate(uint64_t font_size = 50, uint64_t win_size = 2000); }; diff --git a/src/vt-tv/utility/json_reader.cc b/src/vt-tv/utility/json_reader.cc index e83c1dc36..d90a9a6bf 100644 --- a/src/vt-tv/utility/json_reader.cc +++ b/src/vt-tv/utility/json_reader.cc @@ -108,187 +108,221 @@ void JSONReader::readString(std::string const& in_json_string) { json_ = std::make_unique(std::move(j)); } -std::unique_ptr JSONReader::parse() { - using json = nlohmann::json; - - assert(json_ != nullptr && "Must have valid json"); - - std::unordered_map object_info; - std::unordered_map phase_info; - - json j = std::move(*json_); - - auto phases = j["phases"]; - if (phases.is_array()) { - for (auto const& phase : phases) { - std::unordered_map read_phase_user_defined; - auto id = phase["id"]; - if (phase.find("user_defined") != phase.end()) { - auto phase_user_defined = phase["user_defined"]; - if (phase_user_defined.is_object()) { - for (auto& [key, value] : phase_user_defined.items()) { - read_phase_user_defined[key] = value; +std::unique_ptr JSONReader::parsePhaseIter( + PhaseType phase_id, nlohmann::json elm, + std::unordered_map& object_info, bool is_lb_iter +) { + std::unordered_map whole_iter_phase_user_defined; + if (elm.find("user_defined") != elm.end()) { + auto phase_user_defined = elm["user_defined"]; + if (phase_user_defined.is_object()) { + for (auto& [key, value] : phase_user_defined.items()) { + whole_iter_phase_user_defined[key] = value; + } + } + } + auto tasks = elm.value("tasks", elm.array()); + + std::unordered_map objects; + + if (tasks.is_array()) { + for (auto const& task : tasks) { + auto node = task["node"]; + auto time = task["time"]; + auto etype = task["entity"]["type"]; + assert(time.is_number() && "task time must be a number"); + assert(node.is_number() && "task node must be a number"); + + if (etype == "object") { + auto object = task["entity"].value("id", task["entity"]["seq_id"]); + auto home = task["entity"]["home"]; + bool migratable = task["entity"]["migratable"]; + + assert(object.is_number() && "task id or seq_id must be provided and be a number"); + assert(home.is_number() && "task home must be a number"); + + std::vector index_arr; + + if ( + task["entity"].find("collection_id") != task["entity"].end() and + task["entity"].find("index") != task["entity"].end()) { + auto cid = task["entity"]["collection_id"]; + auto idx = task["entity"]["index"]; + if (cid.is_number() && idx.is_array()) { + std::vector arr = idx; + index_arr = std::move(arr); } } - } - auto tasks = phase.value("tasks", j.array()); - - std::unordered_map objects; - - if (tasks.is_array()) { - for (auto const& task : tasks) { - auto node = task["node"]; - auto time = task["time"]; - auto etype = task["entity"]["type"]; - assert(time.is_number() && "task time must be a number"); - assert(node.is_number() && "task node must be a number"); - - if (etype == "object") { - auto object = task["entity"].value("id", task["entity"]["seq_id"]); - auto home = task["entity"]["home"]; - bool migratable = task["entity"]["migratable"]; - - assert(object.is_number() && "task id or seq_id must be provided and be a number"); - assert(home.is_number() && "task home must be a number"); - - std::vector index_arr; - - if ( - task["entity"].find("collection_id") != task["entity"].end() and - task["entity"].find("index") != task["entity"].end()) { - auto cid = task["entity"]["collection_id"]; - auto idx = task["entity"]["index"]; - if (cid.is_number() && idx.is_array()) { - std::vector arr = idx; - index_arr = std::move(arr); - } - } - ObjectInfo oi{object, home, migratable, std::move(index_arr)}; + ObjectInfo oi{object, home, migratable, std::move(index_arr)}; - if (task["entity"].find("collection_id") != task["entity"].end()) { - oi.setIsCollection(true); - oi.setMetaID(task["entity"]["collection_id"]); - } + if (task["entity"].find("collection_id") != task["entity"].end()) { + oi.setIsCollection(true); + oi.setMetaID(task["entity"]["collection_id"]); + } - if (task["entity"].find("objgroup_id") != task["entity"].end()) { - oi.setIsObjGroup(true); - oi.setMetaID(task["entity"]["objgroup_id"]); - } + if (task["entity"].find("objgroup_id") != task["entity"].end()) { + oi.setIsObjGroup(true); + oi.setMetaID(task["entity"]["objgroup_id"]); + } - object_info.try_emplace(object, std::move(oi)); + object_info.try_emplace(object, std::move(oi)); - std::unordered_map subphase_loads; + std::unordered_map subphase_loads; - if (task.find("subphases") != task.end()) { - auto subphases = task["subphases"]; - if (subphases.is_array()) { - for (auto const& s : subphases) { - auto sid = s["id"]; - auto stime = s["time"]; + if (task.find("subphases") != task.end()) { + auto subphases = task["subphases"]; + if (subphases.is_array()) { + for (auto const& s : subphases) { + auto sid = s["id"]; + auto stime = s["time"]; - assert(sid.is_number() && "sid must be a number"); - assert(stime.is_number() && "stime must be a number"); + assert(sid.is_number() && "sid must be a number"); + assert(stime.is_number() && "stime must be a number"); - subphase_loads[sid] = stime; - } - } + subphase_loads[sid] = stime; } + } + } - std::unordered_map - readed_user_defined; - if (task.find("user_defined") != task.end()) { - auto user_defined = task["user_defined"]; - if (user_defined.is_object()) { - for (auto& [key, value] : user_defined.items()) { - readed_user_defined[key] = value; - } - } + std::unordered_map + task_user_defined; + if (task.find("user_defined") != task.end()) { + auto user_defined = task["user_defined"]; + if (user_defined.is_object()) { + for (auto& [key, value] : user_defined.items()) { + task_user_defined[key] = value; } + } + } - std::unordered_map readed_attributes; - if (task.find("attributes") != task.end()) { - auto attributes = task["attributes"]; - if (attributes.is_object()) { - for (auto& [key, value] : attributes.items()) { - readed_attributes[key] = value; - } - } + std::unordered_map task_attributes; + if (task.find("attributes") != task.end()) { + auto attributes = task["attributes"]; + if (attributes.is_object()) { + for (auto& [key, value] : attributes.items()) { + task_attributes[key] = value; } - - // fmt::print(" Add object {}\n", (ElementIDType)object); - objects.try_emplace( - object, - ObjectWork{ - object, - time, - std::move(subphase_loads), - std::move(readed_user_defined), - std::move(readed_attributes)}); } } + + // fmt::print(" Add object {}\n", (ElementIDType)object); + objects.try_emplace( + object, + ObjectWork{ + object, + time, + std::move(subphase_loads), + std::move(task_user_defined), + std::move(task_attributes) + } + ); } + } + } - auto communications = phase.value("communications", j.array()); - if (communications.is_array()) { - for (auto const& comm : communications) { - auto type = comm["type"]; - if (type == "SendRecv") { - auto bytes = comm["bytes"]; - auto messages = comm["messages"]; - - auto from = comm["from"]; - auto to = comm["to"]; - - ElementIDType from_id = from.value("id", from["seq_id"]); - ElementIDType to_id = to.value("id", to["seq_id"]); - - assert(bytes.is_number() && "bytes must be a number"); - // assert(from.is_number()); - // assert(to.is_number()); - - // fmt::print(" From: {}, to: {}\n", from_id, to_id); - - // Object on this rank sent data - auto from_it = objects.find(from_id); - if (from_it != objects.end()) { - from_it->second.addSentCommunications(to_id, bytes); - } else { - auto to_it = objects.find(to_id); - if (to_it != objects.end()) { - to_it->second.addReceivedCommunications(from_id, bytes); - } else { - fmt::print( - "Warning: Communication {} -> {}: neither sender nor " - "recipient was found in objects.\n", - from_id, - to_id); - } - } + auto communications = elm.value("communications", elm.array()); + if (communications.is_array()) { + for (auto const& comm : communications) { + auto type = comm["type"]; + if (type == "SendRecv") { + auto bytes = comm["bytes"]; + auto messages = comm["messages"]; + + auto from = comm["from"]; + auto to = comm["to"]; + + ElementIDType from_id = from.value("id", from["seq_id"]); + ElementIDType to_id = to.value("id", to["seq_id"]); + + assert(bytes.is_number() && "bytes must be a number"); + // assert(from.is_number()); + // assert(to.is_number()); + + // fmt::print(" From: {}, to: {}\n", from_id, to_id); + + // Object on this rank sent data + auto from_it = objects.find(from_id); + if (from_it != objects.end()) { + from_it->second.addSentCommunications(to_id, bytes); + } else { + auto to_it = objects.find(to_id); + if (to_it != objects.end()) { + to_it->second.addReceivedCommunications(from_id, bytes); + } else { + fmt::print( + "Warning: Communication {} -> {}: neither sender nor " + "recipient was found in objects.\n", + from_id, + to_id); } } } + } + } + + if (is_lb_iter) { + auto lb_iter_id = elm["id"]; + return std::make_unique( + phase_id, lb_iter_id, + std::move(objects), std::move(whole_iter_phase_user_defined) + ); + } else { + return std::make_unique( + phase_id, std::move(objects), std::move(whole_iter_phase_user_defined) + ); + } +} + +std::unique_ptr JSONReader::parse() { + using json = nlohmann::json; + + assert(json_ != nullptr && "Must have valid json"); + + std::unordered_map object_info; + std::unordered_map phase_info; + + json j = std::move(*json_); + + auto phases = j["phases"]; + if (phases.is_array()) { + for (auto const& phase : phases) { + auto phase_id = phase["id"]; + auto pw = parsePhaseIter(phase_id, phase, object_info); phase_info.try_emplace( - id, - PhaseWork{id, std::move(objects), std::move(read_phase_user_defined)} + phase_id, + *static_cast(pw.release()) ); + if (phase.find("lb_iterations") != phase.end()) { + auto lb_iters = j["lb_iterations"]; + if (lb_iters.is_array()) { + for (auto const& iter : lb_iters) { + auto lb_iter = parsePhaseIter(phase_id, iter, object_info); + auto lb_id = static_cast(lb_iter.get())->getLBIterationID(); + phase_info[phase_id].addLBIteration( + lb_id, + *static_cast(lb_iter.release()) + ); + } + } + } } } - std::unordered_map readed_metadata; + std::unordered_map read_metadata; if (j.find("metadata") != j.end()) { auto metadata = j["metadata"]; if (metadata.find("attributes") != metadata.end()) { auto attributes = metadata["attributes"]; if (attributes.is_object()) { for (auto& [key, value] : attributes.items()) { - readed_metadata[key] = value; + read_metadata[key] = value; } } } } - Rank r{rank_, std::move(phase_info), std::move(readed_metadata)}; + Rank r{rank_, std::move(phase_info), std::move(read_metadata)}; std::unordered_map rank_info; rank_info.try_emplace(rank_, std::move(r)); diff --git a/src/vt-tv/utility/json_reader.h b/src/vt-tv/utility/json_reader.h index 7c12139da..8bf9bd876 100644 --- a/src/vt-tv/utility/json_reader.h +++ b/src/vt-tv/utility/json_reader.h @@ -100,6 +100,23 @@ struct JSONReader { */ bool validate_datafile(std::string file_path); +private: + /** + * \brief Read in a phase or LB iteration in a JSON data file + * + * \param[in] phase_id the phase ID + * \param[in] j json input + * \param[in] object_info collection of object information + * \param[in] is_lb_iter whether we are parsing an LB iteration + * + * \return the \c WorkDistribution + */ + std::unique_ptr parsePhaseIter( + PhaseType phase_id, nlohmann::json j, + std::unordered_map& object_info, + bool is_lb_iter = false + ); + private: NodeType rank_ = 0; std::unique_ptr json_ = nullptr; diff --git a/tests/unit/api/test_info.cc b/tests/unit/api/test_info.cc index cdd3d6a4a..4338a7b70 100644 --- a/tests/unit/api/test_info.cc +++ b/tests/unit/api/test_info.cc @@ -243,7 +243,7 @@ TEST_F(InfoTest, test_get_phase_objects) { Info info = Info(objects_info, {{0, rank_0}, {1, rank_1}, {2, rank_2}}); - auto phase_0_objects = info.getPhaseObjects(0); + auto phase_0_objects = info.getPhaseObjects(0, no_lb_iter); for (auto& [key, val] : phase_0_objects) { fmt::print( "Phase 0 has object {} at key {} with load {}\n", @@ -263,7 +263,7 @@ TEST_F(InfoTest, test_get_phase_objects) { ASSERT_EQ(phase_0_objects.at(4).getLoad(), 2.0); ASSERT_EQ(phase_0_objects.at(5).getLoad(), 2.0); - auto phase_1_objects = info.getPhaseObjects(1); + auto phase_1_objects = info.getPhaseObjects(1, no_lb_iter); for (auto& [key, val] : phase_1_objects) { fmt::print( "Phase 1 has object {} at key {} with load {}\n", @@ -313,7 +313,8 @@ TEST_F(InfoTest, test_get_max_load_after_changing_selected_phase) { info.getMaxLoad(); } catch (std::exception& e) { fmt::print( - "Authorized exception (unitialized selected phase): {}\n", e.what()); + "Authorized exception (uninitialized selected phase): {}\n", e.what() + ); } info.setSelectedPhase(1); @@ -409,9 +410,14 @@ TEST_F(InfoTest, test_get_object_qoi) { "non-existent"}); for (auto const& qoi : qoi_list) { if (qoi == "non-existent") { - EXPECT_THROW(info.getObjectQOIAtPhase(0, 0, qoi), std::runtime_error); + EXPECT_THROW( + info.getObjectQOIAtPhase(0, 0, no_lb_iter, qoi), + std::runtime_error + ); } else { - ASSERT_NO_THROW(info.getObjectQOIAtPhase(0, 0, qoi)); + ASSERT_NO_THROW( + info.getObjectQOIAtPhase(0, 0, no_lb_iter, qoi) + ); } } } @@ -465,14 +471,14 @@ TEST_F(InfoTest, test_get_rank_qoi) { auto qoi_getter = info.getRankQOIGetter(qoi); if (qoi == "id") { - ASSERT_EQ(qoi_getter(rank_0, 0), 0); - ASSERT_EQ(qoi_getter(rank_1, 0), 1); + ASSERT_EQ(qoi_getter(rank_0, 0, no_lb_iter), 0); + ASSERT_EQ(qoi_getter(rank_1, 0, no_lb_iter), 1); } else if (qoi == "sent_volume") { - ASSERT_EQ(qoi_getter(rank_0, 0), 2.0); - ASSERT_EQ(qoi_getter(rank_1, 1), 3.6); + ASSERT_EQ(qoi_getter(rank_0, 0, no_lb_iter), 2.0); + ASSERT_EQ(qoi_getter(rank_1, 1, no_lb_iter), 3.6); } else if (qoi == "received_volume") { - ASSERT_EQ(qoi_getter(rank_0, 0), 0.0); - ASSERT_EQ(qoi_getter(rank_1, 1), 2.0); + ASSERT_EQ(qoi_getter(rank_0, 0, no_lb_iter), 0.0); + ASSERT_EQ(qoi_getter(rank_1, 1, no_lb_iter), 2.0); } } @@ -485,16 +491,16 @@ TEST_F(InfoTest, test_get_rank_qoi) { std::get(info.getRankAttribute(rank_1, "attr2")), "cd"); // Test getRankQOIAtPhase method - ASSERT_EQ(info.getRankQOIAtPhase(0, 0, "sent_volume"), 2.0); - ASSERT_EQ(info.getRankQOIAtPhase(0, 0, "received_volume"), 0.0); - ASSERT_EQ(info.getRankQOIAtPhase(1, 1, "received_volume"), 2.0); - ASSERT_EQ(info.getRankQOIAtPhase(0, 0, "number_of_objects"), 2.0); - ASSERT_EQ(info.getRankQOIAtPhase(1, 0, "number_of_objects"), 0.0); - ASSERT_EQ(info.getRankQOIAtPhase(0, 0, "number_of_migratable_objects"), 2.0); - ASSERT_EQ(info.getRankQOIAtPhase(0, 0, "migratable_load"), 3.0); - ASSERT_EQ(info.getRankQOIAtPhase(1, 1, "sentinel_load"), 3.6); - ASSERT_EQ(info.getRankQOIAtPhase(1, 0, "id"), 1.0); - ASSERT_EQ(info.getRankQOIAtPhase(0, 0, "attr1"), 12.0); + ASSERT_EQ(info.getRankQOIAtPhase(0, 0, no_lb_iter, "sent_volume"), 2.0); + ASSERT_EQ(info.getRankQOIAtPhase(0, 0, no_lb_iter, "received_volume"), 0.0); + ASSERT_EQ(info.getRankQOIAtPhase(1, 1, no_lb_iter, "received_volume"), 2.0); + ASSERT_EQ(info.getRankQOIAtPhase(0, 0, no_lb_iter, "number_of_objects"), 2.0); + ASSERT_EQ(info.getRankQOIAtPhase(1, 0, no_lb_iter, "number_of_objects"), 0.0); + ASSERT_EQ(info.getRankQOIAtPhase(0, 0, no_lb_iter, "number_of_migratable_objects"), 2.0); + ASSERT_EQ(info.getRankQOIAtPhase(0, 0, no_lb_iter, "migratable_load"), 3.0); + ASSERT_EQ(info.getRankQOIAtPhase(1, 1, no_lb_iter, "sentinel_load"), 3.6); + ASSERT_EQ(info.getRankQOIAtPhase(1, 0, no_lb_iter, "id"), 1.0); + ASSERT_EQ(info.getRankQOIAtPhase(0, 0, no_lb_iter, "attr1"), 12.0); } TEST_F( diff --git a/tests/unit/api/test_rank.cc b/tests/unit/api/test_rank.cc index 25b3abcb0..b00f7a30b 100644 --- a/tests/unit/api/test_rank.cc +++ b/tests/unit/api/test_rank.cc @@ -83,12 +83,12 @@ TEST_F(RankTest, test_initial_state) { // Assertions for rank_0 EXPECT_EQ(rank_0.getRankID(), 2); EXPECT_EQ(rank_0.getNumPhases(), 3); - EXPECT_EQ(rank_0.getLoad(0), phase_0.getLoad()); - EXPECT_EQ(rank_0.getLoad(1), phase_1.getLoad()); - EXPECT_EQ(rank_0.getLoad(2), phase_2.getLoad()); - EXPECT_EQ(rank_0.getNumObjects(0), phase_0.getObjectWork().size()); - EXPECT_EQ(rank_0.getNumObjects(1), phase_1.getObjectWork().size()); - EXPECT_EQ(rank_0.getNumObjects(2), phase_2.getObjectWork().size()); + EXPECT_EQ(rank_0.getLoad(0, no_lb_iter), phase_0.getLoad()); + EXPECT_EQ(rank_0.getLoad(1, no_lb_iter), phase_1.getLoad()); + EXPECT_EQ(rank_0.getLoad(2, no_lb_iter), phase_2.getLoad()); + EXPECT_EQ(rank_0.getNumObjects(0, no_lb_iter), phase_0.getObjectWork().size()); + EXPECT_EQ(rank_0.getNumObjects(1, no_lb_iter), phase_1.getObjectWork().size()); + EXPECT_EQ(rank_0.getNumObjects(2, no_lb_iter), phase_2.getObjectWork().size()); EXPECT_EQ(rank_0.getPhaseWork().size(), phase_info_0.size()); EXPECT_EQ(rank_0.getPhaseWork().at(0).getPhase(), 0); EXPECT_EQ(rank_0.getPhaseWork().at(1).getPhase(), 1); diff --git a/tests/unit/utility/test_json_reader.cc b/tests/unit/utility/test_json_reader.cc index d33dd2828..43edd9b0b 100644 --- a/tests/unit/utility/test_json_reader.cc +++ b/tests/unit/utility/test_json_reader.cc @@ -160,7 +160,7 @@ TEST_F(JSONReaderTest, test_json_reader_object_info_attributes) { auto& rank_info = info->getRank(rank); EXPECT_EQ(rank_info.getRankID(), rank); - auto const& objects = info->getRankObjects(0, 0); + auto const& objects = info->getRankObjects(0, 0, no_lb_iter); auto const& object_work = objects.at(3407875); auto& object_attributes = object_work.getAttributes(); @@ -211,7 +211,7 @@ TEST_F(JSONReaderTest, test_json_reader_object_work_user_defined) { auto& rank_info = info->getRank(rank); EXPECT_EQ(rank_info.getRankID(), rank); - auto const& objects = info->getRankObjects(0, 0); + auto const& objects = info->getRankObjects(0, 0, no_lb_iter); auto const& object_info = objects.at(3407875); auto& user_defined = object_info.getUserDefined();