blob: 1d856a9b4ecdb6c80039c137de664743a8a7eed8 [file] [log] [blame]
/* Copyright (c) 2012 Massachusetts Institute of Technology
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "model/Model.h"
#include <vector>
#include "util/Result.h"
namespace DSENT
{
using std::vector;
using LibUtil::deletePtrMap;
using LibUtil::clonePtrMap;
Model::SubModel::SubModel(Model* model_, double num_models_)
: m_model_(model_), m_num_models_(num_models_)
{}
Model::SubModel::~SubModel()
{
delete m_model_;
}
Model* Model::SubModel::getModel()
{
return m_model_;
}
const Model* Model::SubModel::getModel() const
{
return m_model_;
}
double Model::SubModel::getNumModels() const
{
return m_num_models_;
}
Model::SubModel* Model::SubModel::clone() const
{
return new SubModel(*this);
}
Model::SubModel::SubModel(const SubModel& sub_model_)
{
m_model_ = sub_model_.m_model_->clone();
m_num_models_ = sub_model_.m_num_models_;
}
const char Model::TYPE_SEPARATOR[] = ">>";
const char Model::HIERARCHY_SEPARATOR[] = "->";
const char Model::SUBFIELD_SEPARATOR[] = ":";
const char Model::DETAIL_SEPARATOR[] = "@";
Model::Model(const String& instance_name_, const TechModel* tech_model_)
: m_instance_name_(instance_name_), m_tech_model_(tech_model_),
m_constructed_(false), m_updated_(false), m_evaluated_(false)
{
m_property_names_ = new vector<String>;
m_parameter_names_ = new vector<String>;
m_parameters_ = new ParameterMap();
m_properties_ = new PropertyMap();
m_generated_properties_ = new PropertyMap();
m_sub_instances_ = new Map<SubModel*>();
m_event_map_ = new Map<Result*>();
m_area_map_ = new Map<Result*>();
m_ndd_power_map_ = new Map<Result*>();
}
Model::~Model()
{
// Clear parameter names
delete m_parameter_names_;
// Clear property name
delete m_property_names_;
// Clear parameters
delete m_parameters_;
m_parameters_ = NULL;
// Clear input properties
delete m_properties_;
m_properties_ = NULL;
// Clear generated properties
delete m_generated_properties_;
m_generated_properties_ = NULL;
// Clear sub models
deletePtrMap<SubModel>(m_sub_instances_);
m_sub_instances_ = NULL;
// Clear all results
deletePtrMap<Result>(m_event_map_);
m_event_map_ = NULL;
deletePtrMap<Result>(m_area_map_);
m_area_map_ = NULL;
deletePtrMap<Result>(m_ndd_power_map_);
m_ndd_power_map_ = NULL;
}
void Model::setInstanceName(const String& instance_name_)
{
m_instance_name_ = instance_name_;
return;
}
const String& Model::getInstanceName() const
{
return m_instance_name_;
}
void Model::setIsTopModel(bool is_top_model_)
{
m_is_top_model_ = is_top_model_;
return;
}
bool Model::getIsTopModel() const
{
return m_is_top_model_;
}
//-------------------------------------------------------------------------
// Parameters and properties checks
//-------------------------------------------------------------------------
void Model::addParameterName(const String& parameter_name_)
{
ASSERT(!m_constructed_, "[Error] " + getInstanceName() +
" -> Cannot add additional parameters names after model is constructed!");
m_parameter_names_->push_back(parameter_name_);
return;
}
void Model::addParameterName(const String& parameter_name_, const String& parameter_default_)
{
ASSERT(!m_constructed_, "[Error] " + getInstanceName() +
" -> Cannot add additional parameters names after model is constructed!");
m_parameter_names_->push_back(parameter_name_);
setParameter(parameter_name_, parameter_default_);
return;
}
const vector<String>* Model::getParameterNames() const
{
return m_parameter_names_;
}
void Model::addPropertyName(const String& property_name_)
{
ASSERT(!m_constructed_, "[Error] " + getInstanceName() +
" -> Cannot add additional property names after model is constructed!");
m_property_names_->push_back(property_name_);
return;
}
void Model::addPropertyName(const String& property_name_, const String& property_default_)
{
ASSERT(!m_constructed_, "[Error] " + getInstanceName() +
" -> Cannot add additional property names after model is constructed!");
m_property_names_->push_back(property_name_);
setProperty(property_name_, property_default_);
return;
}
const vector<String>* Model::getPropertyNames() const
{
return m_property_names_;
}
void Model::checkParameters() const
{
String missing_parameters = "";
for(int i = 0; i < (int)m_parameter_names_->size(); ++i)
{
const String& parameter_name = m_parameter_names_->at(i);
if (!m_parameters_->keyExist(parameter_name))
missing_parameters += " " + parameter_name + "\n";
}
ASSERT(missing_parameters.size() == 0, "[Error] " + m_instance_name_ +
" -> Missing parameters:\n" + missing_parameters);
return;
}
void Model::checkProperties() const
{
String missing_properties = "";
for(int i = 0; i < (int)m_property_names_->size(); ++i)
{
const String& property_name = m_property_names_->at(i);
if (!m_properties_->keyExist(property_name))
missing_properties += " " + property_name + "\n";
}
ASSERT(missing_properties.size() == 0, "[Error] " + m_instance_name_ +
" -> Missing properties:\n" + missing_properties);
return;
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// Parameters Manipulation
//-------------------------------------------------------------------------
const ParameterMap* Model::getParameters() const
{
return m_parameters_;
}
const String Model::getParameter(const String& parameter_name_) const
{
return m_parameters_->get(parameter_name_);
}
void Model::setParameter(const String& parameter_name_, const String& parameter_value_)
{
ASSERT(!m_constructed_, "[Error] " + getInstanceName() +
" -> Cannot set parameters after model is constructed!");
m_parameters_->set(parameter_name_, parameter_value_);
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// Properties Manipulation
//-------------------------------------------------------------------------
const PropertyMap* Model::getProperties() const
{
return m_properties_;
}
const String Model::getProperty(const String& property_name_) const
{
return m_properties_->get(property_name_);
}
void Model::setProperty(const String& property_name_, const String& property_value_)
{
// If any properties changed, reset updated and evaluated flags
m_updated_ = false;
m_evaluated_ = false;
m_properties_->set(property_name_, property_value_);
}
//-------------------------------------------------------------------------
PropertyMap* Model::getGenProperties()
{
return m_generated_properties_;
}
const PropertyMap* Model::getGenProperties() const
{
return m_generated_properties_;
}
void Model::addSubInstances(Model* sub_instance_, double num_sub_instances_)
{
// Get instance name
const String& sub_instance_name = sub_instance_->getInstanceName();
// Check if the instance exists
if(m_sub_instances_->keyExist(sub_instance_name))
{
const String& error_msg = "[Error] " + m_instance_name_ +
" -> Instance exists (" + sub_instance_name + ")";
throw Exception(error_msg);
}
// Check if the num_sub_instances_ is a positive number
ASSERT((num_sub_instances_ >= 0), "[Error] " + m_instance_name_ +
" -> Invalid number of instance (" + String(num_sub_instances_) + ")");
// Add the instance
m_sub_instances_->set(sub_instance_name, new SubModel(sub_instance_, num_sub_instances_));
return;
}
Model* Model::getSubInstance(const String& sub_instance_name_)
{
// Throw an Exception if the instance already exists
if(!m_sub_instances_->keyExist(sub_instance_name_))
{
const String& error_msg = "[Error] " + m_instance_name_ +
" -> Instance not exists (" + sub_instance_name_ + ")";
throw Exception(error_msg);
}
return m_sub_instances_->get(sub_instance_name_)->getModel();
}
const Model* Model::getSubInstance(const String& sub_instance_name_) const
{
// Throw an Exception if the instance does not exist
if(!m_sub_instances_->keyExist(sub_instance_name_))
{
const String& error_msg = "[Error] " + m_instance_name_ +
" -> Instance not exists (" + sub_instance_name_ + ")";
throw Exception(error_msg);
}
return m_sub_instances_->get(sub_instance_name_)->getModel();
}
bool Model::hasSubInstance(const String& sub_instance_name_) const
{
return m_sub_instances_->keyExist(sub_instance_name_);
}
void Model::addAreaResult(Result* area_)
{
const String& area_name = area_->getName();
// Throw an Exception if the area already exists
if(m_area_map_->keyExist(area_name))
{
const String& error_msg = "Internal error: area (" + area_name +
") exists";
throw Exception(error_msg);
}
// Add the area
m_area_map_->set(area_name, area_);
return;
}
Result* Model::getAreaResult(const String& area_name_)
{
return m_area_map_->get(area_name_);
}
const Result* Model::getAreaResult(const String& area_name_) const
{
return m_area_map_->get(area_name_);
}
bool Model::hasAreaResult(const String& area_name_) const
{
return m_area_map_->keyExist(area_name_);
}
void Model::addNddPowerResult(Result* ndd_power_)
{
const String& ndd_power_name = ndd_power_->getName();
// Throw an Exception if the ndd_power already exists
if(m_ndd_power_map_->keyExist(ndd_power_name))
{
const String& error_msg = "Internal error: ndd_power (" + ndd_power_name +
") exists";
throw Exception(error_msg);
}
// Add the ndd_power
m_ndd_power_map_->set(ndd_power_name, ndd_power_);
return;
}
Result* Model::getNddPowerResult(const String& ndd_power_name_)
{
return m_ndd_power_map_->get(ndd_power_name_);
}
const Result* Model::getNddPowerResult(const String& ndd_power_name_) const
{
return m_ndd_power_map_->get(ndd_power_name_);
}
bool Model::hasNddPowerResult(const String& ndd_power_name_) const
{
return m_ndd_power_map_->keyExist(ndd_power_name_);
}
void Model::addEventResult(Result* event_)
{
const String& event_name = event_->getName();
// Throw an Exception if the event already exists
if(m_event_map_->keyExist(event_name))
{
const String& error_msg = "Internal error: event (" + event_name +
") exists";
throw Exception(error_msg);
}
// Add the event
m_event_map_->set(event_name, event_);
return;
}
Result* Model::getEventResult(const String& event_name_)
{
return m_event_map_->get(event_name_);
}
const Result* Model::getEventResult(const String& event_name_) const
{
return m_event_map_->get(event_name_);
}
bool Model::hasEventResult(const String& event_name_) const
{
return m_event_map_->keyExist(event_name_);
}
const TechModel* Model::getTechModel() const
{
return m_tech_model_;
}
const void* Model::parseQuery(const String& query_type_, const String& query_hier_, const String& query_sub_field_)
{
// Break query by hierarchy separator
vector<String> hier_split = query_hier_.splitByString(HIERARCHY_SEPARATOR);
// Check if the query_hier matches the instance name
ASSERT((hier_split[0] == m_instance_name_), "[Error] " +
m_instance_name_ + " -> Mismatch in instance name (" +
hier_split[0] + ")");
// If there is no more hierarchy separator, this process the query
if(hier_split.size() == 1)
{
// Query the model
return processQuery(query_type_, query_sub_field_);
}
else
{
// Reconstruct the query
String temp_query_hier = hier_split[1];
for(int i = 2; i < (int)hier_split.size(); ++i)
{
temp_query_hier += HIERARCHY_SEPARATOR + hier_split[i];
}
// Get sub instance's name
const String& temp_sub_instance_name = hier_split[1];
ASSERT(m_sub_instances_->keyExist(temp_sub_instance_name), "[Error] " +
m_instance_name_ + " -> No sub-instances queried (" +
temp_sub_instance_name + ")");
return m_sub_instances_->get(temp_sub_instance_name)->getModel()->parseQuery(query_type_, temp_query_hier, query_sub_field_);
}
}
const void* Model::processQuery(const String& query_type_, const String& query_sub_field_)
{
if(query_type_ == "Property")
{
return getProperties();
}
else if(query_type_ == "Parameter")
{
return getParameters();
}
else if(query_type_.contain("Hier"))
{
return this;
}
else if(query_type_ == "Area")
{
return queryArea(query_sub_field_);
}
else if(query_type_ == "NddPower")
{
return queryNddPower(query_sub_field_);
}
else if(query_type_ == "Energy")
{
return queryEventEnergyCost(query_sub_field_);
}
else
{
const String& error_msg = "[Error] " + m_instance_name_ + " -> Unknown query type (" + query_type_ + ")";
throw Exception(error_msg);
return NULL;
}
}
const Result* Model::queryArea(const String& area_name_) const
{
ASSERT(m_area_map_->keyExist(area_name_), "[Error] " + m_instance_name_ +
" -> Unknown queried area name (" + area_name_ + ")");
return m_area_map_->get(area_name_);
}
const Result* Model::queryNddPower(const String& ndd_power_name_)
{
ASSERT(m_ndd_power_map_->keyExist(ndd_power_name_), "[Error] " + m_instance_name_ +
" -> Unknown queried ndd power name (" + ndd_power_name_ + ")");
use("Idle");
return m_ndd_power_map_->get(ndd_power_name_);
}
const Result* Model::queryEventEnergyCost(const String& event_name_)
{
ASSERT(m_event_map_->keyExist(event_name_), "[Error] " + m_instance_name_ +
" -> Unknown queried event name (" + event_name_ + ")");
use(event_name_);
return m_event_map_->get(event_name_);
}
// Update checks whether the model needs updating, whether all properties have been specified,
// and calls updateModel if update is necessary
void Model::construct()
{
// Model should not be constructed yet
ASSERT(!m_constructed_, "[Error] " + getInstanceName() + " -> Cannot construct an already contructed model!");
// Check if whether all needed parameters are defined
checkParameters();
constructModel();
m_constructed_ = true;
m_updated_ = false;
m_evaluated_ = false;
return;
}
// Update checks whether the model needs updating, whether all properties have been specified,
// and calls updateModel if update is necessary
void Model::update()
{
// Model should be constructed
ASSERT(m_constructed_, "[Error] " + getInstanceName() + " -> Cannot update an unconstructed model!");
// If the model needs updating (due to property change)
// an update is necessary
if (!m_updated_)
{
// Check if all properties needed exist
checkProperties();
updateModel();
m_updated_ = true;
m_evaluated_ = false;
}
return;
}
// Evaluate checks whether the model needs to be evaluated.
void Model::evaluate()
{
// Model should be constructed
ASSERT(m_constructed_, "[Error] " + getInstanceName() + " -> Cannot evaluate an unconstructed model!");
// Model should be updated
ASSERT(m_updated_, "[Error] " + getInstanceName() + " -> Cannot evaluate without first updating!");
// If the model needs evaluating
if (!m_evaluated_)
{
evaluateModel();
m_evaluated_ = true;
}
return;
}
void Model::use(const String& event_name_)
{
useModel(event_name_);
return;
}
void Model::use()
{
useModel();
return;
}
// By default, update model will iterate through all sub-instances and do updateModel on them
void Model::updateModel()
{
Map<SubModel*>::Iterator iter = m_sub_instances_->begin();
Map<SubModel*>::Iterator end = m_sub_instances_->end();
while (iter != end)
{
iter->second->getModel()->update();
iter++;
}
return;
}
// By default, update model will iterate through all sub-instances and do updateModel on them
void Model::evaluateModel()
{
Map<SubModel*>::Iterator iter = m_sub_instances_->begin();
Map<SubModel*>::Iterator end = m_sub_instances_->end();
while (iter != end)
{
iter->second->getModel()->evaluate();
iter++;
}
return;
}
void Model::useModel(const String& /* event_name_ */)
{}
void Model::useModel()
{}
void Model::printHierarchy(const String& query_type_, const String& query_sub_field_, const String& prepend_str_, int detail_level_, ostream& ost_) const
{
if(query_type_ == "InstHier")
{
ost_ << prepend_str_ << getInstanceName() << endl;
printInstHierarchy(prepend_str_, detail_level_, ost_);
//if(detail_level_ > 0)
//{
//for(Map<SubModel*>::ConstIterator it = m_sub_instances_->begin(); it != m_sub_instances_->end(); ++it)
//{
//const Model* sub_model = (it->second)->getModel();
//String temp_prepend_str = prepend_str_ + " ";
//sub_model->printHierarchy(query_type_, query_sub_field_, temp_prepend_str, detail_level_ - 1, ost_);
//}
//}
}
else
{
const Map<Result*>* result_map;
if(query_type_ == "AreaHier")
{
result_map = m_area_map_;
}
else if(query_type_ == "NddPowerHier")
{
result_map = m_ndd_power_map_;
}
else if(query_type_ == "EventHier")
{
result_map = m_event_map_;
}
else
{
const String& error_msg = "[Error] " + m_instance_name_ + " -> Unknown query type (" + query_type_ + ")";
throw Exception(error_msg);
return;
}
if(query_sub_field_ == "")
{
for(Map<Result*>::ConstIterator it = result_map->begin(); it != result_map->end(); ++it)
{
const Result* result = it->second;
ost_ << prepend_str_ << getInstanceName() << "->" << result->getName() << endl;
result->printHierarchy(prepend_str_, detail_level_, ost_);
}
}
else
{
const Result* result = result_map->get(query_sub_field_);
ost_ << prepend_str_ << getInstanceName() << "->" << result->getName() << endl;
result->printHierarchy(prepend_str_, detail_level_, ost_);
}
}
return;
}
void Model::printInstHierarchy(const String& prepend_str_, int detail_level_, ostream& ost_) const
{
if(detail_level_ > 0)
{
for(Map<SubModel*>::ConstIterator it = m_sub_instances_->begin(); it != m_sub_instances_->end(); ++it)
{
const Model* sub_model = it->second->getModel();
String temp_prepend_str = prepend_str_ + " ";
ost_ << prepend_str_ << " |--" << sub_model->getInstanceName() << endl;
sub_model->printInstHierarchy(temp_prepend_str, detail_level_ - 1, ost_);
}
}
return;
}
Model* Model::clone() const
{
throw Exception(getInstanceName() + " -> Cannot be cloned!");
}
Model::Model(const Model& model_)
{
// Copy instance's name
m_instance_name_ = model_.m_instance_name_;
// Clone properties
m_properties_ = model_.m_properties_->clone();
// Clone instances
m_sub_instances_ = clonePtrMap(model_.m_sub_instances_);
// Clone events, area, ndd_power
m_event_map_ = clonePtrMap(model_.m_event_map_);
m_area_map_ = clonePtrMap(model_.m_area_map_);
m_ndd_power_map_ = clonePtrMap(model_.m_ndd_power_map_);
// Copy tech model pointer
m_tech_model_ = model_.m_tech_model_;
}
} // namespace DSENT