bool ImportPressure::Configure( const Configuration * inputJson ) { LOG_DEBUG_F( "%s\n", __FUNCTION__ ); // TODO: specification for rate, seasonality, and age-biting function initConfigTypeMap( "Durations", &durations, IP_Durations_DESC_TEXT, 0, INT_MAX, 1 ); initConfigTypeMap( "Daily_Import_Pressures", &daily_import_pressures, IP_Daily_Import_Pressures_DESC_TEXT , 0, FLT_MAX, 0 ); bool configured = Outbreak::Configure( inputJson ); if( configured && !JsonConfigurable::_dryrun ) { if( durations.size() != (daily_import_pressures.size()) ) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "ImportPressure intervention requires Durations must be the same size as Daily_Import_Pressures" ); } if( durations.size() <= 0 ) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "Empty Durations parameter in ImportPressure intervention." ); } ConstructDistributionCalendar(); } return configured; }
void CalendarEventCoordinator::BuildDistributionCalendar( std::vector<int> distribution_times, std::vector<float> distribution_coverages ) { NaturalNumber last_time = 0; while (!distribution_times.empty()) { NaturalNumber time = distribution_times.front(); if( time == last_time ) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, ( std::string( "Duplicate distribution time entries: " ) + std::to_string( time ) ).c_str() ); } else if( time < last_time ) { std::stringstream msg; msg << "Distribution time mis-ordered: " << (int) last_time << " > " << (int) time << std::endl; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, msg.str().c_str() ); } Fraction coverage = distribution_coverages.front(); distribution_times.erase( distribution_times.begin() ); distribution_coverages.erase( distribution_coverages.begin() ); times_and_coverages.insert( std::make_pair( time, coverage ) ); last_time = time; } }
void DiagnosticTreatNeg::onPatientDefault() { LOG_DEBUG_F( "Individual %d got the test but defaulted, receiving Defaulters intervention without waiting for days_to_diagnosis (actually means days_to_intervention) \n", parent->GetSuid().data ); // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary IGlobalContext *pGC = nullptr; const IInterventionFactory* ifobj = nullptr; if (s_OK == parent->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC)) { ifobj = pGC->GetInterventionFactory(); } if (!ifobj) { throw NullPointerException( __FILE__, __LINE__, __FUNCTION__, "parent->GetInterventionFactoryObj()" ); } if( !defaulters_event.IsUninitialized() ) { if( defaulters_event != NO_TRIGGER_STR ) { INodeTriggeredInterventionConsumer* broadcaster = nullptr; if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster)) { throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext" ); } broadcaster->TriggerNodeEventObserversByString( parent->GetEventContext(), defaulters_event ); } } else if( defaulters_config._json.Type() != ElementType::NULL_ELEMENT ) { auto tmp_config = Configuration::CopyFromElement(defaulters_config._json); // Distribute the defaulters intervention, right away (do not use the days_to_diagnosis IDistributableIntervention *di = const_cast<IInterventionFactory*>(ifobj)->CreateIntervention( tmp_config ); delete tmp_config; tmp_config = nullptr; ICampaignCostObserver* pICCO; // Now make sure cost of the test-positive intervention is reported back to node if (s_OK == parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(ICampaignCostObserver), (void**)&pICCO) ) { di->Distribute( parent->GetInterventionsContext(), pICCO ); pICCO->notifyCampaignEventOccurred( (IBaseIntervention*)di, (IBaseIntervention*)this, parent ); } else { throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "ICampaignCostObserver", "INodeEventContext" ); } } else { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "neither event or config defined" ); } }
void CheckSimType( const std::string& rSimTypeStr, const json::Object& rJsonObject, const char* pClassName ) { json::QuickInterpreter sim_type_qi( rJsonObject[ "Sim_Types" ] ); json::QuickInterpreter sim_type_array( sim_type_qi.As<json::Array>() ); int num_types = sim_type_qi.As<json::Array>().Size() ; assert( num_types > 0 ); if( std::string(sim_type_array[0].As<json::String>()) == std::string("*") ) { // wild card means it is valid for any simulation type return ; } std::string supported ; for( int i = 0 ; i < num_types ; i++ ) { std::string supported_sim_type = std::string(sim_type_array[i].As<json::String>()) ; if( rSimTypeStr == supported_sim_type ) { return ; } supported += "'"+supported_sim_type + "', " ; } supported = supported.substr( 0, supported.length() - 2 ); std::stringstream ss ; ss << "The '" << pClassName << "' intervention is not valid with the current 'Simulation_Type' (='" << rSimTypeStr << "'). " ; ss << "This intervention is only supported for the following simulation types: " << supported ; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, ss.str().c_str() ); }
bool MultiInterventionEventCoordinator::HasNodeLevelIntervention() const { bool has_node_level_intervention = false; bool has_individual_level_intervention = false; const json::Array & interventions_array = json::QuickInterpreter( intervention_config._json ).As<json::Array>(); LOG_DEBUG_F("interventions array size = %d\n", interventions_array.Size()); for( int idx = 0; idx < interventions_array.Size(); idx++ ) { const json::Object& actualIntervention = json_cast<const json::Object&>(interventions_array[idx]); auto config = Configuration::CopyFromElement( actualIntervention, "campaign" ); INodeDistributableIntervention *ndi = InterventionFactory::getInstance()->CreateNDIIntervention(config); if( ndi != nullptr ) { has_node_level_intervention = true; ndi->Release(); } else { has_individual_level_intervention = true; } delete config; config = nullptr; } if( has_node_level_intervention && has_individual_level_intervention ) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "You cannot mix individual and nodel-level interventions." ); } return has_node_level_intervention; }
void NodeLevelHealthTriggeredIVScaleUpSwitch::onDisqualifiedByCoverage( IIndividualHumanEventContext *pIndiv ) { //if qualify by everything except demographic coverage, give the not_covered_individualintervention_config // this intervention is the one phased out as the actual_individualintervention_config is phased in // if the not_covered_individualintervention_config is NULL, then there is no intervention to phase out LOG_DEBUG("The person qualified by everything except demographic coverage, give the not_covered_individualintervention_config \n"); // Query for campaign cost observer interface from INodeEventContext *parent ICampaignCostObserver *iCCO; if (s_OK != parent->QueryInterface(GET_IID(ICampaignCostObserver), (void**)&iCCO)) { throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent", "ICampaignCostObserver", "INodeEventContext" ); } // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary //const IInterventionFactory* ifobj = dynamic_cast<NodeEventContextHost *>(parent)->GetInterventionFactoryObj(); IGlobalContext *pGC = nullptr; const IInterventionFactory* ifobj = nullptr; if (s_OK == parent->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC)) { ifobj = pGC->GetInterventionFactory(); } if (!ifobj) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "The pointer to IInterventionFactory object is not valid (could be DLL specific)" ); } const json::Array & interventions_array = json::QuickInterpreter(not_covered_intervention_configs._json).As<json::Array>(); LOG_DEBUG_F("not_covered_intervention_configs array size = %d\n", interventions_array.Size()); if (interventions_array.Size() == 0 ) { LOG_DEBUG("nothing to phase out \n"); } else { for( int idx=0; idx<interventions_array.Size(); idx++ ) { const json::Object& notcoveredIntervention = json_cast<const json::Object&>(interventions_array[idx]); Configuration * tmpConfig = Configuration::CopyFromElement( notcoveredIntervention, "campaign" ); assert( tmpConfig ); LOG_DEBUG_F("NodeHTIScaleUpSwitch will distribute notcoveredintervention #%d\n", idx); IDistributableIntervention *di = const_cast<IInterventionFactory*>(ifobj)->CreateIntervention(tmpConfig); delete tmpConfig; tmpConfig = nullptr; if( di ) { di->Distribute( pIndiv->GetInterventionsContext(), iCCO ); LOG_DEBUG("A Node level health-triggered intervention was successfully distributed, gave the not_covered_intervention_configs\n"); // It's not at all clear to me that we would incur cost at this point, but we could. //iCCO->notifyCampaignExpenseIncurred( interventionCost, pIndiv ); } } } }
bool DiagnosticTreatNeg::Configure( const Configuration * inputJson ) { EventOrConfig::Enum use_event_or_config; initConfig( "Event_Or_Config", use_event_or_config, inputJson, MetadataDescriptor::Enum("EventOrConfig", Event_Or_Config_DESC_TEXT, MDD_ENUM_ARGS( EventOrConfig ) ) ); if( use_event_or_config == EventOrConfig::Event || JsonConfigurable::_dryrun ) { initConfigTypeMap( "Negative_Diagnosis_Event", &negative_diagnosis_event, DTN_Negative_Diagnosis_Config_Event_DESC_TEXT ); initConfigTypeMap( "Defaulters_Event", &defaulters_event, DTN_Defaulters_Diagnosis_Config_Event_DESC_TEXT ); } if( use_event_or_config == EventOrConfig::Config || JsonConfigurable::_dryrun ) { initConfigComplexType("Negative_Diagnosis_Config", &negative_diagnosis_config, DTN_Negative_Diagnosis_Config_DESC_TEXT, "Event_Or_Config", "Config" ); initConfigComplexType("Defaulters_Config", &defaulters_config, DTN_Defaulters_Diagnosis_Config_DESC_TEXT, "Event_Or_Config", "Config" ); } bool ret = SimpleDiagnostic::Configure( inputJson ); if( ret ) { if( use_event_or_config == EventOrConfig::Config || JsonConfigurable::_dryrun ) { InterventionValidator::ValidateIntervention( negative_diagnosis_config._json ); InterventionValidator::ValidateIntervention( defaulters_config._json ); } if( !JsonConfigurable::_dryrun && negative_diagnosis_event.IsUninitialized() && (negative_diagnosis_config._json.Type() == ElementType::NULL_ELEMENT) ) { const char* msg = "You must define either Negative_Diagnosis_Event or Negative_Diagnosis_Config"; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, msg ); } if( !JsonConfigurable::_dryrun && defaulters_event.IsUninitialized() && (defaulters_config._json.Type() == ElementType::NULL_ELEMENT) ) { const char* msg = "You must define either Defaulters_Event or Defaulters_Config"; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, msg ); } } return ret ; }
void DiagnosticTreatNeg::negativeTestDistribute() { LOG_DEBUG_F( "Individual %d tested 'negative', receiving negative intervention.\n", parent->GetSuid().data ); // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary IGlobalContext *pGC = nullptr; const IInterventionFactory* ifobj = nullptr; if (s_OK == parent->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC)) { ifobj = pGC->GetInterventionFactory(); } if (!ifobj) { throw NullPointerException( __FILE__, __LINE__, __FUNCTION__, "parent->GetInterventionFactoryObj()" ); } if( use_event_or_config == EventOrConfig::Event ) { INodeTriggeredInterventionConsumer* broadcaster = nullptr; if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster)) { throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext" ); } broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), negative_diagnosis_event ); } else if( negative_diagnosis_config._json.Type() != ElementType::NULL_ELEMENT ) { auto tmp_config = Configuration::CopyFromElement( negative_diagnosis_config._json, "campaign" ); // Distribute the test-negative intervention IDistributableIntervention *di = const_cast<IInterventionFactory*>(ifobj)->CreateIntervention( tmp_config ); delete tmp_config; tmp_config = nullptr; ICampaignCostObserver* pICCO; // Now make sure cost of the test-positive intervention is reported back to node if (s_OK == parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(ICampaignCostObserver), (void**)&pICCO) ) { di->Distribute( parent->GetInterventionsContext(), pICCO ); pICCO->notifyCampaignEventOccurred( (IBaseIntervention*)di, (IBaseIntervention*)this, parent ); } else { throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "ICampaignCostObserver", "INodeEventContext" ); } } else { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "neither event or config defined" ); } expired = true; }
void GenericDrug::ResetForNextDose(float dt) { dosing_timer = time_between_doses; remaining_doses--; if (remaining_doses != 0 && time_between_doses < dt) { std::ostringstream oss; oss << "Time to next dose (" << time_between_doses << ") is shorter than the time-step, dt (" << dt << ")" << std::endl; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, oss.str().c_str() ); } }
void SimpleTransmissionGroups::AddPropertyValueListToPropertyToValueMap( const string& route, const string& property, const PropertyValueList_t& values ) { if (transmissionRouteName.length() == 0) { transmissionRouteName = string(route); } else if (route != transmissionRouteName) { throw GeneralConfigurationException(__FILE__, __LINE__, __FUNCTION__, "SimpleTransmissionGroups only supports one route."); } propertyNameToValuesMap[property] = values; LOG_DEBUG_F( "Adding list of %d values to propertyNameToValuesMap for key/property %s\n", values.size(), property.c_str() ); }
Kernel::StrainIdentity* Outbreak::GetNewStrainIdentity(INodeEventContext *context) { StrainIdentity *outbreak_strainID = nullptr; // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary // NO usage of GET_CONFIGURABLE(SimulationConfig)->number_substrains in DLL IGlobalContext *pGC = nullptr; const SimulationConfig* simConfigObj = nullptr; if (s_OK == context->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC)) { simConfigObj = pGC->GetSimulationConfigObj(); } if (!simConfigObj) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "The pointer to IInterventionFactory object is not valid (could be DLL specific)" ); } if (( antigen < 0 ) || ( antigen >= simConfigObj->number_basestrains )) { throw IncoherentConfigurationException( __FILE__, __LINE__, __FUNCTION__, "antigen", antigen, "number_basestrains", simConfigObj->number_basestrains ); } if ( genome < 0 ) { int ss = simConfigObj->number_substrains; if (ss & (ss-1)) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "Only supporting random genome generation for Number_Substrains as factor of two." ); } unsigned int BARCODE_BITS = 0; while(ss >>= 1) ++BARCODE_BITS; uint32_t genome = context->GetRng()->ul() & ((1 << BARCODE_BITS)-1); //genome = context->GetRng()->i(simConfigObj->number_substrains); outbreak_strainID = _new_ StrainIdentity(antigen, genome); LOG_DEBUG_F("random genome generation... antigen: %d\t genome: %d\n", antigen, genome); }
bool StandardInterventionDistributionEventCoordinator::Configure( const Configuration * inputJson ) { initializeInterventionConfig( inputJson ); //initConfigTypeMap("Number_Distributions", &num_distributions, Number_Distributions_DESC_TEXT, -1, 1e6, -1 ); // by convention, -1 means no limit initConfigTypeMap("Number_Repetitions", &num_repetitions, Number_Repetitions_DESC_TEXT, -1, 1000, -1 ); //if( num_repetitions > 1 ) // -1 = repeat without end, 0 is meaningless. want to think this one through more { initConfigTypeMap("Timesteps_Between_Repetitions", &tsteps_between_reps, Timesteps_Between_Repetitions_DESC_TEXT, -1, 10000 /*undefined*/, -1 /*off*/, "Number_Repetitions", "<>0" ); } //initConfigTypeMap("Include_Departures", &include_emigrants, Include_Departures_DESC_TEXT, false ); //initConfigTypeMap("Include_Arrivals", &include_immigrants, Include_Arrivals_DESC_TEXT, false ); demographic_restrictions.ConfigureRestrictions( this, inputJson ); bool retValue = JsonConfigurable::Configure( inputJson ); if( retValue && !JsonConfigurable::_dryrun) { demographic_restrictions.CheckConfiguration(); validateInterventionConfig( intervention_config._json ); if( HasNodeLevelIntervention() ) { // --------------------------------------------------------------------------- // --- If the user is attempting to define demographic restrictions when they // --- are using a node level intervention, then we need to error because these // --- restrictions are not doing anything. // --------------------------------------------------------------------------- if( !demographic_restrictions.HasDefaultRestrictions() ) { std::ostringstream msg ; msg << "In StandardInterventionDistributionEventCoordinator, demographic restrictions such as 'Demographic_Coverage'\n"; msg << "and 'Target_Gender' do not apply when distributing node level interventions such as "; msg << std::string( json::QuickInterpreter(intervention_config._json)["class"].As<json::String>() ); msg << ".\nThe node level intervention must handle the demographic restrictions."; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, msg.str().c_str() ); } } } return retValue; }
void CoverageByNodeJson::ConfigureFromJsonAndKey( const Configuration* inputJson, const std::string& key ) { try { json::QuickInterpreter json_array( (*inputJson)[key].As<json::Array>() ); for( unsigned int idx = 0; idx < (*inputJson)[key].As<json::Array>().Size(); idx++ ) { json::QuickInterpreter node_coverage_pair( json_array[idx] ); if (node_coverage_pair.As<json::Array>().Size() != 2) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "Coverage_By_Node parameter needs to be an array of [nodeID,coverage] pairs." ); } uint32_t nodeid = 0; float coverage = 0.0f; try { nodeid = (uint32_t) node_coverage_pair[0].As<json::Number>(); } catch( const json::Exception & ) { throw Kernel::JsonTypeConfigurationException( __FILE__, __LINE__, __FUNCTION__, "0", node_coverage_pair, "Expected NUMBER" ); } try { coverage = (float) node_coverage_pair[1].As<json::Number>(); } catch( const json::Exception & ) { throw Kernel::JsonTypeConfigurationException( __FILE__, __LINE__, __FUNCTION__, "1", node_coverage_pair, "Expected NUMBER" ); } LOG_DEBUG_F("Parsing Coverage_By_Node property: nodeid=%d, coverage=%0.2f.\n", nodeid, coverage); auto ret = node_coverage_map.insert(std::pair<uint32_t,float>(nodeid,coverage)); if (ret.second == false) { LOG_WARN_F("Duplicate coverage specified for node with ID=%d. Using first coverage value specified.\n", nodeid); } } } catch( const json::Exception & ) { throw Kernel::JsonTypeConfigurationException( __FILE__, __LINE__, __FUNCTION__, key.c_str(), (*inputJson), "Expected ARRAY" ); } }
bool CoverageByNodeEventCoordinator::Configure( const Configuration * inputJson ) { initConfigComplexType("Coverage_By_Node", &coverage_by_node, Coverage_By_Node_DESC_TEXT ); bool configured = StandardInterventionDistributionEventCoordinator::Configure(inputJson); try { if( !JsonConfigurable::_dryrun ) { } } catch( const json::Exception &e ) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, e.what() ); // ( "Coverage_By_Node json problem: Coverage_By_Node is valid json but needs to be an array of (nodeID,coverage) pairs." ); } return configured; }
bool CalendarEventCoordinator::Configure( const Configuration * inputJson ) { std::vector<int> distribution_times; std::vector<float> distribution_coverages; initConfigTypeMap("Distribution_Times", &distribution_times, Distribution_Times_DESC_TEXT, 1, INT_MAX, 0 ); initConfigTypeMap("Distribution_Coverages", &distribution_coverages, Distribution_Coverages_DESC_TEXT, 0.0f, 1.0f, 0.0f ); bool retValue = StandardInterventionDistributionEventCoordinator::Configure( inputJson ); if(distribution_times.size() != distribution_coverages.size()) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "In a Calendar Event Coordinator, vector of distribution coverages must match vector of distribution times" ); } BuildDistributionCalendar(distribution_times, distribution_coverages); return retValue; }
void NodeLevelHealthTriggeredIV::SetContextTo(INodeEventContext *context) { BaseNodeIntervention::SetContextTo( context ); // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary //const IInterventionFactory* ifobj = dynamic_cast<NodeEventContextHost *>(parent)->GetInterventionFactoryObj(); IGlobalContext *pGC = nullptr; const IInterventionFactory* ifobj = nullptr; if (s_OK == parent->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC)) { ifobj = pGC->GetInterventionFactory(); } if (!ifobj) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "The pointer to IInterventionFactory object is not valid (could be DLL specific)" ); } if( (_di == nullptr) && (_ndi == nullptr) ) { Configuration* config = nullptr; if( using_individual_config ) { config = Configuration::CopyFromElement( (actual_individual_intervention_config._json), "campaign" ); } else { config = Configuration::CopyFromElement( (actual_node_intervention_config._json), "campaign" ); } _di = const_cast<IInterventionFactory*>(ifobj)->CreateIntervention( config ); if( _di == nullptr ) { _ndi = const_cast<IInterventionFactory*>(ifobj)->CreateNDIIntervention( config ); } release_assert( (_di !=nullptr) || (_ndi != nullptr) ); delete config; config = nullptr; } }
bool InputEIR::Configure( const Configuration * inputJson ) { initConfig( "Age_Dependence", age_dependence, inputJson, MetadataDescriptor::Enum("Age_Dependence", IE_Age_Dependence_DESC_TEXT, MDD_ENUM_ARGS(AgeDependentBitingRisk)) ); initConfigComplexType( "Monthly_EIR", &monthly_EIR, IE_Config_Type_DESC_TEXT); switch(age_dependence) { case AgeDependentBitingRisk::OFF: risk_function=nullptr; break; case AgeDependentBitingRisk::LINEAR: risk_function = SusceptibilityVector::LinearBitingFunction; break; case AgeDependentBitingRisk::SURFACE_AREA_DEPENDENT: risk_function = SusceptibilityVector::SurfaceAreaBitingFunction; break; default: if( !JsonConfigurable::_dryrun ) { throw BadEnumInSwitchStatementException( __FILE__, __LINE__, __FUNCTION__, "age_dependence", age_dependence, AgeDependentBitingRisk::pairs::lookup_key(age_dependence) ); } } bool configured = BaseNodeIntervention::Configure( inputJson ); if(monthly_EIR.size() != MONTHSPERYEAR && JsonConfigurable::_dryrun == false ) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "'Monthly_EIR' parameterizes the mean number of infectious bites experienced by an individual for each month of the year. As such, it must be an array of EXACTLY length 12." ); } return configured; }
void NodeEventCoordinator::UpdateNodes( float dt ) { // Intervention class names for informative logging std::ostringstream intervention_name; intervention_name << std::string( json::QuickInterpreter(intervention_config._json)["class"].As<json::String>() ); // Simplest NDI distribution without repetition INodeDistributableIntervention *ndi = nullptr; for(auto *nec : cached_nodes) { auto tmp_config = Configuration::CopyFromElement( intervention_config._json ); ndi = InterventionFactory::getInstance()->CreateNDIIntervention( tmp_config ); delete tmp_config; tmp_config = nullptr; if(ndi) { if (!ndi->Distribute( nec, this ) ) { ndi->Release(); // a bit wasteful for now, could cache it for the next fellow } else { LOG_INFO_F("UpdateNodes() distributed '%s' intervention to node %d\n", intervention_name.str().c_str(), nec->GetId().data ); } } else { // add NDI-only exception std::string err = "Unable to create an instance of " + intervention_name.str() + " as an INodeDistributableIntervention."; throw GeneralConfigurationException(__FILE__, __LINE__, __FUNCTION__, err.c_str()); } } // Dispose if(ndi) ndi->Release(); distribution_complete = true; }
// copy/paste to remove single-intervention-specific logging // TODO: could be handled more gracefully // also not robust in case of array containing NTI not just ITI void MultiInterventionEventCoordinator::UpdateNodes( float dt ) { // Only call VisitNodes on first call and if countdown == 0 if( tsteps_since_last != tsteps_between_reps ) { return; } int grandTotal = 0; int limitPerNode = -1; LOG_DEBUG_F("[UpdateNodes] limitPerNode = %d\n", limitPerNode); for (auto nec : cached_nodes) { try { // For now, distribute evenly across nodes. int totalIndivGivenIntervention = nec->VisitIndividuals( this, limitPerNode ); grandTotal += totalIndivGivenIntervention; LOG_INFO_F( "UpdateNodes() gave out %d interventions at node %d\n", totalIndivGivenIntervention, nec->GetId().data ); } catch(json::Exception &e) { // ERROR: not ITI??? // ERROR: ::cerr << "exception casting intervention_config to array! " << e.what() << std::endl; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, e.what() ); // ( "Intervention_Configs json problem: intervention_config is valid json but needs to be an array." ); } } tsteps_since_last = 0; num_repetitions--; if( num_repetitions == 0 ) { distribution_complete = true; // we're done, signal disposal ok } }
bool MultiInterventionDistributor::Distribute(IIndividualHumanInterventionsContext *context, ICampaignCostObserver * const pICCO ) { // ---------------------------------------------------------------------------------- // --- Putting this here because we don't want anything to happen if we are aborting // ---------------------------------------------------------------------------------- if( AbortDueToDisqualifyingInterventionStatus( context->GetParent() ) ) { return false; } // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary IGlobalContext *pGC = nullptr; const IInterventionFactory* ifobj = nullptr; release_assert(context->GetParent()); if (s_OK == context->GetParent()->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC)) { ifobj = pGC->GetInterventionFactory(); } if (!ifobj) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "The pointer to IInterventionFactory object is not valid (could be DLL specific)" ); } try { // Parse intervention_list const json::Array & interventions_array = json::QuickInterpreter(intervention_list._json).As<json::Array>(); LOG_DEBUG_F("interventions array size = %d\n", interventions_array.Size()); for( int idx=0; idx<interventions_array.Size(); idx++ ) { const json::Object& actualIntervention = json_cast<const json::Object&>(interventions_array[idx]); Configuration * tmpConfig = Configuration::CopyFromElement( actualIntervention, "campaign" ); assert( tmpConfig ); // Instantiate and distribute interventions LOG_DEBUG_F( "Attempting to instantiate intervention of class %s\n", std::string((*tmpConfig)["class"].As<json::String>()).c_str() ); IDistributableIntervention *di = const_cast<IInterventionFactory*>(ifobj)->CreateIntervention(tmpConfig); if (di) { if (!di->Distribute( context, pICCO ) ) { di->Release(); } } else { INodeDistributableIntervention* ndi = const_cast<IInterventionFactory*>(ifobj)->CreateNDIIntervention( tmpConfig ); release_assert(ndi); if( !ndi->Distribute( context->GetParent()->GetEventContext()->GetNodeEventContext(), nullptr ) ) { ndi->Release(); } } delete tmpConfig; tmpConfig = nullptr; } } catch(json::Exception &e) { // ERROR: ::cerr << "exception casting intervention_config to array! " << e.what() << std::endl; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, e.what() ); // ( "Intervention_List json problem: intervention_list is valid json but needs to be an array." ); } // Nothing more for this class to do... Expire(); return true; }
bool MultiInterventionEventCoordinator::visitIndividualCallback( IIndividualHumanEventContext *ihec, float & incrementalCostOut, ICampaignCostObserver * pICCO ) { // Less of this would need to be copied from the base class with a more thoughtful encapsulation of functions // In particular, only the give-intervention(s)-to-individual stuff inside the try statement is different. if( !demographic_restrictions.HasDefaultRestrictions() ) // don't waste any more time with checks if we're giving to everyone { if( qualifiesDemographically( ihec ) == false ) { LOG_DEBUG("Individual not given intervention because not in target demographic\n"); return false; } } LOG_DEBUG("Individual meets demographic targeting criteria\n"); if (!TargetedIndividualIsCovered(ihec)) { incrementalCostOut = 0; return false; } else { incrementalCostOut = 0; try { const json::Array & interventions_array = json::QuickInterpreter( intervention_config._json ).As<json::Array>(); LOG_DEBUG_F("interventions array size = %d\n", interventions_array.Size()); for( int idx=0; idx<interventions_array.Size(); idx++ ) { const json::Object& actualIntervention = json_cast<const json::Object&>(interventions_array[idx]); Configuration * tmpConfig = Configuration::CopyFromElement(actualIntervention); assert( tmpConfig ); // instantiate and distribute intervention LOG_DEBUG_F( "Attempting to instantiate intervention of class %s\n", std::string((*tmpConfig)["class"].As<json::String>()).c_str() ); IDistributableIntervention *di = InterventionFactory::getInstance()->CreateIntervention(tmpConfig); assert(di); delete tmpConfig; tmpConfig = nullptr; if (di) { if (!di->Distribute( ihec->GetInterventionsContext(), pICCO ) ) { di->Release(); // a bit wasteful for now, could cache it for the next fellow } LOG_DEBUG_F("Distributed an intervention %p to individual %d at a cost of %f\n", di, ihec->GetSuid().data, incrementalCostOut); } } } catch(json::Exception &e) { // ERROR: ::cerr << "exception casting intervention_config to array! " << e.what() << std::endl; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, e.what() ); // ( "InterventionConfigs json problem: intervention_config is valid json but needs to be an array." ); } } return true; }
void CalendarEventCoordinator::UpdateNodes( float dt ) { // Now issue events as they come up, including anything currently in the past or present while( parent->GetSimulationTime().time >= times_and_coverages.begin()->first) { int grandTotal = 0; int limitPerNode = -1; // intervention class names for informative logging std::ostringstream intervention_name; intervention_name << std::string( json::QuickInterpreter(intervention_config._json)["class"].As<json::String>() ); auto qi_as_config = Configuration::CopyFromElement( (intervention_config._json), "campaign" ); _di = InterventionFactory::getInstance()->CreateIntervention(qi_as_config); // including deeper information for "distributing" interventions (e.g. calendars) formatInterventionClassNames( intervention_name, &json::QuickInterpreter(intervention_config._json) ); // Only visit individuals if this is NOT an NTI. Check... // Check to see if intervention is an INodeDistributable... INodeDistributableIntervention *ndi = InterventionFactory::getInstance()->CreateNDIIntervention(qi_as_config); INodeDistributableIntervention *ndi2 = nullptr; LOG_DEBUG_F("[UpdateNodes] limitPerNode = %d\n", limitPerNode); for (auto nec : cached_nodes) { if (ndi) { throw NotYetImplementedException( __FILE__, __LINE__, __FUNCTION__ ); #if 0 ndi2 = InterventionFactory::getInstance()->CreateNDIIntervention( qi_as_config ); if(ndi2) { float duration = -1; if (times_and_coverages.size() > 1) { auto iter = times_and_coverages.end(); //A node-targeted intervention issued through the calender coordinator lasts until the next NDI. //Is there an overlap of one day here? Should there be a -1? duration = (float)(prev(iter,2)->first - prev(iter, 1)->first); } INodeDistributableInterventionParameterSetterInterface* pNDIPSI = nullptr; if (s_OK == ndi2->QueryInterface(GET_IID(INodeDistributableInterventionParameterSetterInterface), (void**)&pNDIPSI) ) { pNDIPSI->SetDemographicCoverage(times_and_coverages.begin()->second); pNDIPSI->SetMaxDuration(duration); } if (!ndi2->Distribute( nec, this ) ) { LOG_INFO_F("UpdateNodes() distributed '%s' intervention to node %d\n", intervention_name.str().c_str(), nec->GetId().data ); } ndi2->Release(); } #endif } else { try { // For now, distribute evenly across nodes. int totalIndivGivenIntervention = nec->VisitIndividuals( this, limitPerNode ); grandTotal += totalIndivGivenIntervention; LOG_INFO_F( "UpdateNodes() gave out %d interventions at node %d\n", totalIndivGivenIntervention, nec->GetId().data ); } catch( const json::Exception &e ) { throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, e.what() ); } } } delete qi_as_config; qi_as_config = nullptr; times_and_coverages.erase(times_and_coverages.begin()); LOG_DEBUG_F("%d Distributions remaining from CalendarEventCoordinator\n", times_and_coverages.size()); if( times_and_coverages.empty() ) { LOG_DEBUG_F("Signaling for disposal of CalendarEventCoordinator\n"); distribution_complete = true; // we're done, signal disposal ok break; } } return; }
bool CommunityHealthWorkerEventCoordinator::Configure( const Configuration * inputJson ) { initConfigTypeMap("Duration", &m_CoordinatorDaysRemaining, CHW_Duration_DESC_TEXT, 0.0f, FLT_MAX, FLT_MAX ); initConfigTypeMap("Max_Distributed_Per_Day", &m_MaxDistributedPerDay, CHW_Max_Distributed_Per_Day_DESC_TEXT, 1, INT_MAX, INT_MAX ); initConfigTypeMap("Waiting_Period", &m_QueueWaitingPeriodDays, CHW_Waiting_Period_DESC_TEXT, 0.0f, FLT_MAX, FLT_MAX ); initConfigTypeMap("Days_Between_Shipments", &m_DaysBetweenShipments, CHW_Days_Between_Shipments_DESC_TEXT, 1.0f, FLT_MAX, FLT_MAX ); initConfigTypeMap("Amount_In_Shipment", &m_AmountInShipment, CHW_Amount_In_Shipment_DESC_TEXT, 0, INT_MAX, INT_MAX ); initConfigTypeMap("Max_Stock", &m_MaxStock, CHW_Max_Stock_DESC_TEXT, 0, INT_MAX, INT_MAX ); DurationDistribution initial_amount; initial_amount.SetTypeNameDesc( "Initial_Amount_Distribution_Type", CHW_Initial_Amount_Distribution_Type_DESC_TEXT ); initial_amount.AddSupportedType( DistributionFunction::FIXED_DURATION, "Initial_Amount", CHW_Initial_Amount_DESC_TEXT, "", "" ); initial_amount.AddSupportedType( DistributionFunction::UNIFORM_DURATION, "Initial_Amount_Min", CHW_Initial_Amount_Min_DESC_TEXT, "Initial_Amount_Max", CHW_Initial_Amount_Max_DESC_TEXT ); initial_amount.AddSupportedType( DistributionFunction::GAUSSIAN_DURATION, "Initial_Amount_Mean", CHW_Initial_Amount_Mean_DESC_TEXT, "Initial_Amount_Std_Dev", CHW_Initial_Amount_Std_Dev_DESC_TEXT ); initial_amount.AddSupportedType( DistributionFunction::EXPONENTIAL_DURATION, "Initial_Amount", CHW_Initial_Amount_DESC_TEXT, "", "" ); initial_amount.Configure( this, inputJson ); m_DemographicRestrictions.ConfigureRestrictions( this, inputJson ); initConfigTypeMap("Trigger_Condition_List", &m_TriggerConditionList, CHW_Trigger_Condition_List_DESC_TEXT ); initConfigComplexType( "Node_Property_Restrictions", &m_NodePropertyRestrictions, CHW_Node_Property_Restriction_DESC_TEXT ); initConfigComplexType( "Intervention_Config", &m_InterventionConfig, CHW_Intervention_Config_DESC_TEXT ); bool retValue = JsonConfigurable::Configure( inputJson ); if( retValue && !JsonConfigurable::_dryrun) { // ------------------------------------------------------ // --- Check that the intervention exists and initialize // ------------------------------------------------------ InterventionValidator::ValidateIntervention( m_InterventionConfig._json, inputJson->GetDataLocation() ); m_DemographicRestrictions.CheckConfiguration(); Configuration* qi_as_config = Configuration::CopyFromElement( m_InterventionConfig._json ); m_InterventionName = std::string( json::QuickInterpreter(m_InterventionConfig._json)["class"].As<json::String>() ); m_pInterventionIndividual = InterventionFactory::getInstance()->CreateIntervention( qi_as_config ); if( m_pInterventionIndividual == nullptr ) { m_pInterventionNode = InterventionFactory::getInstance()->CreateNDIIntervention( qi_as_config ); } delete qi_as_config; qi_as_config = nullptr; // --------------------------------------------------------------------------- // --- If the user is attempting to define demographic restrictions when they // --- are using a node level intervention, then we need to error because these // --- restrictions are not doing anything. // --------------------------------------------------------------------------- if( (m_pInterventionNode != nullptr) && !m_DemographicRestrictions.HasDefaultRestrictions() ) { std::ostringstream msg ; msg << "In CommunityHealthWorkerEventCoordinator, demographic restrictions such as 'Demographic_Coverage'\n"; msg << "and 'Target_Gender' do not apply when distributing nodel level interventions such as "; msg << m_InterventionName; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, msg.str().c_str() ); } else if( (m_pInterventionNode == nullptr) && (m_NodePropertyRestrictions.Size() > 0) ) { std::ostringstream msg; msg << "In CommunityHealthWorkerEventCoordinator, Node_Property_Restrictions only works when distributing node-level interventions.\n"; msg << m_InterventionName << " is a individual-targeted intervention.\n"; throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, msg.str().c_str() ); } for( auto& event_name : m_RemoveIndividualEventList ) { m_TriggerConditionList.push_back( event_name ); } // -------------------- // --- Initialize Stock // -------------------- initial_amount.CheckConfiguration(); m_CurrentStock = int( initial_amount.CalculateDuration() + 0.5); // round up if( m_CurrentStock > m_MaxStock ) { m_CurrentStock = m_MaxStock; } m_DaysToNextShipment = int( float(m_CurrentStock) / float(m_MaxDistributedPerDay) ); if( m_DaysToNextShipment > m_DaysBetweenShipments ) { m_DaysToNextShipment = m_DaysBetweenShipments; } } return retValue; }