Program Listing for File KModel.h#
↰ Return to documentation for file (include/Karana/SOADyn/KModel.h)
/*
*Copyright(c)2024-2025KaranaDynamicsPtyLtd.Allrightsreserved.
*
*NOTICETOUSER:
*
*Thissourcecodeand/ordocumentation(the"LicensedMaterials")is
*theconfidentialandproprietaryinformationofKaranaDynamicsInc.
*UseoftheseLicensedMaterialsisgovernedbythetermsandconditions
*ofaseparatesoftwarelicenseagreementbetweenKaranaDynamicsandthe
*Licensee("LicenseAgreement").Unlessexpresslypermittedunderthat
*agreement,anyreproduction,modification,distribution,ordisclosure
*oftheLicensedMaterials,inwholeorinpart,toanythirdparty
*withoutthepriorwrittenconsentofKaranaDynamicsisstrictlyprohibited.
*
*THELICENSEDMATERIALSAREPROVIDED"ASIS"WITHOUTWARRANTYOFANYKIND.
*KARANADYNAMICSDISCLAIMSALLWARRANTIES,EXPRESSORIMPLIED,INCLUDING
*BUTNOTLIMITEDTOWARRANTIESOFMERCHANTABILITY,NON-INFRINGEMENT,AND
*FITNESSFORAPARTICULARPURPOSE.
*
*INNOEVENTSHALLKARANADYNAMICSBELIABLEFORANYDAMAGESWHATSOEVER,
*INCLUDINGBUTNOTLIMITEDTOLOSSOFPROFITS,DATA,ORUSE,EVENIF
*ADVISEDOFTHEPOSSIBILITYOFSUCHDAMAGES,WHETHERINCONTRACT,TORT,
*OROTHERWISEARISINGOUTOFORINCONNECTIONWITHTHELICENSEDMATERIALS.
*
*U.S.GovernmentEndUsers:TheLicensedMaterialsarea"commercialitem"
*asdefinedat48C.F.R.2.101,andareprovidedtotheU.S.Government
*onlyasacommercialenditemunderthetermsofthislicense.
*
*AnyuseoftheLicensedMaterialsinindividualorcommercialsoftwaremust
*include,intheuserdocumentationandinternalsourcecodecomments,
*thisNotice,Disclaimer,andU.S.GovernmentUseProvision.
*/
#pragmaonce
#include"Karana/KCore/LockingBase.h"
#include"Karana/KCore/MsgLogger.h"
#include"Karana/SOADyn/StatePropagator.h"
namespaceKarana::Models{
namespacekm=Karana::Math;
namespacekc=Karana::Core;
namespacekd=Karana::Dynamics;
classBaseKModel:publickc::LockingBase{
//Foraccessto_registerModeland_unregisterModel
friendclasskd::StatePropagator;
public:
//TODO:Hidethisinprotected
BaseKModel(std::string_viewname,constkc::ks_ptr<kd::StatePropagator>&sp);
virtualstd::optional<km::Ktime>getNextModelStepTime(constkm::Ktime&t)=0;
virtualkm::KtimegetPeriod()=0;
virtualvoidsetPeriod(constkm::Ktime&t)=0;
kc::ks_ptr<kd::StatePropagator>state_propagator;
booldebug_model=false;
virtualvoidstdDebugMsg(std::string_viewmsg);
protected:
virtualvoid_registerModel()=0;
virtualvoid_unregisterModel()=0;
km::Ktimeperiod{0};
bool_registered=false;
bool_has_continuous_states=false;
/*
*@briefGetmodelcontinuousstates.OverriddenbyKModelifappropriate.
*@returnsThemodel'scontinuousstates.
*/
virtualconstEigen::Ref<constkm::Vec>_getContinuousStates()const=0;
virtualconstEigen::Ref<constkm::Vec>_getContinuousStatesDeriv()=0;
virtualvoid_setContinuousStates(constEigen::Ref<constkm::Vec>x)=0;
};
classKModelParams:publickc::Base{
public:
KModelParams(std::string_viewname)
:Base(name){};
boolisFinalized()constoverride{
kc::warn("YourmodelparamsclassdoesnotoverrideisFinalized.Itshoulddothisto"
"ensureallyour"
"parametersarefinalized.");
returntrue;
};
};
classKModelDiscreteStates:publickc::Base{
public:
KModelDiscreteStates(std::string_viewname)
:Base(name){};
boolisFinalized()constoverride{
kc::warn("YourdiscretemodelstatesclassdoesnotoverrideisFinalized.Itshoulddo"
"thisto"
"ensureitisfinalized.");
returntrue;
}
};
classKModelContinuousStates:publickc::Base{
public:
KModelContinuousStates(std::string_viewname)
:Base(name){};
boolisFinalized()constoverride{
kc::warn("YourcontinuousmodelstatesclassdoesnotoverrideisFinalized.Itshould"
"dothisto"
"ensureitisfinalized.");
returntrue;
}
virtualconstEigen::Ref<constkm::Vec>getX()const=0;
virtualconstEigen::Ref<constkm::Vec>getdX()=0;
virtualvoidsetX(constEigen::Ref<constkm::Vec>x)=0;
};
classKModelScratch:publickc::Base{
public:
KModelScratch(std::string_viewname)
:Base(name){};
};
#ifndefPYBIND11_MKDOC_SKIP
template<typenameT>
conceptHasPreDeriv=requires(Tt,constkm::Ktime&ti,constkm::Vec&x){
{t.preDeriv(ti,x)}->std::same_as<void>;
};
template<typenameT>
conceptHasPostDeriv=requires(Tt,constkm::Ktime&ti,constkm::Vec&x){
{t.postDeriv(ti,x)}->std::same_as<void>;
};
template<typenameT>
conceptHasPreModelStep=requires(Tt,constkm::Ktime&ti,constkm::Vec&x){
{t.preModelStep(ti,x)}->std::same_as<void>;
};
template<typenameT>
conceptHasPostModelStep=requires(Tt,constkm::Ktime&ti,constkm::Vec&x){
{t.postModelStep(ti,x)}->std::same_as<void>;
};
template<typenameT>
conceptHasNextModelStepTime=requires(Tt,constkm::Ktime&ti){
{t.nextModelStepTime(ti)}->std::same_as<std::optional<km::Ktime>>;
};
template<typenameT>
conceptHasPreHop=requires(Tt,constkm::Ktime&ti,constkm::Vec&x){
{t.preHop(ti,x)}->std::same_as<void>;
};
template<typenameT>
conceptHasPostHop=requires(Tt,constkm::Ktime&ti,constkm::Vec&x){
{t.postHop(ti,x)}->std::same_as<void>;
};
#endif
classNoParams:publicKModelParams{};
classNoScratch:publicKModelScratch{};
classNoDiscreteStates:publicKModelDiscreteStates{};
classNoContinuousStates:publicKModelContinuousStates{};
template<classT,
classP=NoParams,
classSc=NoScratch,
classS=NoDiscreteStates,
classC=NoContinuousStates>
classKModel:publicBaseKModel{
#ifndefPYBIND11_MKDOC_SKIP
static_assert(
std::is_base_of_v<KModelParams,P>andnotstd::is_same_v<KModelParams,P>,
"Thesecondtemplateparameter(theparamclass),mustbederivedfromKModelParams,"
"butmustnotbeKModelParamsitself(useNoParamsifthemodelhasnoparams).");
static_assert(std::is_base_of_v<KModelScratch,Sc>and
notstd::is_same_v<KModelScratch,Sc>,
"Thethirdtemplateparameter(themodelscratchclass),mustbederived"
"fromKModelScratch,butmustnotbeKModelScratchitself(useNoScratchif"
"themodelhasnoscratch).");
static_assert(
std::is_base_of_v<KModelDiscreteStates,S>and
notstd::is_same_v<KModelDiscreteStates,S>,
"Thefourthtemplateparameter(thediscretemodelstatesclass),mustbederivedfrom"
"KModelDiscreteStates,butmustnotbeKModelDiscreteStatesitself(use"
"NoDiscreteStates"
"ifthemodelhasnodiscretestates).");
static_assert(std::is_base_of_v<KModelContinuousStates,C>and
notstd::is_same_v<KModelContinuousStates,C>,
"Thefifthtemplateparameter(thecontinuousmodelstatesclass),mustbe"
"derivedfromKModelContinuousStates,butmustnotbeKModelContinuousStates"
"itself(useNoContinuousStatesifthemodelhasnocontinuousstates).");
#endif
public:
//TODO:Hideconstructor
usingBaseKModel::BaseKModel;
kc::ks_ptr<P>params=nullptr;
kc::ks_ptr<Sc>scratch=nullptr;
kc::ks_ptr<S>discrete_states=nullptr;
kc::ks_ptr<C>continuous_states=nullptr;
boolisFinalized()constoverride{
boolfinalized=true;
//IfPisnotKModelParams,thencallisFinalizedonit.Otherwise,justreturntrue.
ifconstexpr(notstd::is_same_v<NoParams,P>){
if(params){
if(notparams->isFinalized()){
finalized=false;
}
}else{
kc::warn("Theparamsclassformodel{}is{}(soparamsshouldbedefined),"
"butitiscurrentlyanullptr.",
name(),
typeid(P).name());
finalized=false;
}
}
//IfScisnotKModelScratch,thencallisFinalizedonit.Otherwise,justreturntrue.
ifconstexpr(notstd::is_same_v<NoScratch,Sc>){
if(scratch){
if(notscratch->isFinalized()){
finalized=false;
}
}else{
kc::warn("Thescratchclassformodel{}is{}(soscratchshouldbedefined),"
"butitiscurrentlyanullptr.",
name(),
typeid(Sc).name());
finalized=false;
}
}
//IfSisnotKModelDiscreteStates,thencallisFinalizedonit.Otherwise,justreturn
//true.
ifconstexpr(notstd::is_same_v<NoDiscreteStates,S>){
if(discrete_states){
if(notdiscrete_states->isFinalized()){
finalized=false;
}
}else{
kc::warn("Thediscretestatesclassformodel{}is{}(sodiscretestates"
"shouldbedefined),"
"butitiscurrentlyanullptr.",
name(),
typeid(S).name());
finalized=false;
}
}
//IfCisnotKModelContinuousStates,thencallisFinalizedonit.Otherwise,just
//returntrue.
ifconstexpr(notstd::is_same_v<NoContinuousStates,C>){
if(continuous_states){
if(notcontinuous_states->isFinalized()){
finalized=false;
}
}else{
kc::warn("Thecontinuousmodelstatesclassformodel{}is{}(socontinuous"
"statesshouldbedefined),butitiscurrentlyanullptr.",
name(),
typeid(C).name());
finalized=false;
}
}
//Checkifweregisteredapostmodelstepeventifappropriate
ifconstexpr(HasPostModelStep<T>){
if(notstate_propagator->hasRegisteredTimedEvent(
std::format("{}_post_model_step_{}",name(),id()),false)){
kc::warn("ApostModelStepmethodisdefined,buttheperiodis0nordoesthe"
"modelhaveanextModelStepTimemethodthatreturnsaKtime."
"Therefore,thismethodwill"
"neverberun.");
finalized=false;
}
}
ifconstexpr(HasPreModelStep<T>){
if(notstate_propagator->hasRegisteredTimedEvent(
std::format("{}_pre_model_step_{}",name(),id()),true)){
kc::warn("ApreModelStepmethodisdefined,buttheperiodis0notdoesthe"
"modelhaveanextModelStepTimemethodthatreturnsaKtime."
"Therefore,thismethodwill"
"neverberun.");
finalized=false;
}
}
returnfinalized;
};
std::optional<km::Ktime>getNextModelStepTime(constkm::Ktime&t)final{
#ifndefPYBIND11_MKDOC_SKIP
ifconstexpr(HasNextModelStepTime<T>){
returnstatic_cast<T*>(this)->nextModelStepTime(t);
}else{
returnt+period;
}
#endif
}
km::KtimegetPeriod()final{
#ifndefPYBIND11_MKDOC_SKIP
ifconstexpr(HasNextModelStepTime<T>){
kc::warn("AnextModelStepTimeisdefinedformodel{}.Returning-1.",name());
returnkm::Ktime{-1};
}else{
returnperiod;
}
#endif
}
voidsetPeriod(constkm::Ktime&t)override{
#ifndefPYBIND11_MKDOC_SKIP
ifconstexpr(HasNextModelStepTime<T>){
throwstd::invalid_argument("Cannotsetthemodelperiodformodel{},asithasa"
"nextModelStepTimemethod.");
}else{
if(t<=km::Ktime{0})[[unlikely]]{
throwstd::invalid_argument(
std::format("Cannotsettheperiodtosomething<=0.Got{}",period));
}
if(period<=km::Ktime{0}){
//Iftheperiodis<=0currently,thenweneedtore-registerthemodel,
//asthismaychangehowitisregistered.
period=t;
//Ifthemodelisalreadyregistered,thenunregisteritandre-registerit.
if(_registered){
kc::ks_ptr<BaseKModel>this_mod=kc::static_pointer_cast<BaseKModel>(
kc::ks_ptr<Base>(shared_from_this()));
state_propagator->unregisterModel(this_mod);
state_propagator->registerModel(this_mod);
}
}else{
//Otherwise,justmodifytheperiod.Thiswillaffectthealready-registered
//timedeventsifthismodelisregistered,soneedtore-register.
period=t;
}
}
#endif
}
protected:
void_registerModel()override{
#ifndefPYBIND11_MKDOC_SKIP
if(_registered){
kc::warn("Model{}hasalreadybeenregistered.Doingnothing.",name());
return;
}
//Ifwehavecontinuousstates,thanmarkthismodelassuch.
ifconstexpr(notstd::is_same_v<NoContinuousStates,C>){
_has_continuous_states=true;
}
//any_methods_registeredisusedtodecideifweshouldtriggeranerrorifatleast
//oneofthemethodsaffectingthesimisdefined.
boolany_methods_registered=false;
ifconstexpr(HasPreDeriv<T>){
kc::trace("RegisteringpreDerivformodel{}.",name());
any_methods_registered=true;
kc::ks_ptr<Base>dark=shared_from_this();
kc::ks_ptr<T>t=kc::static_pointer_cast<T>(dark);
state_propagator->_getOptions()
.fns.pre_deriv_fns[std::format("{}_pre_deriv_{}",name(),id())]=
[t](constkm::Ktime&ti,constkm::Vec&x){returnt->preDeriv(ti,x);};
};
ifconstexpr(HasPostDeriv<T>){
kc::trace("RegisteringpostDerivformodel{}.",name());
any_methods_registered=true;
kc::ks_ptr<Base>dark=shared_from_this();
kc::ks_ptr<T>t=kc::static_pointer_cast<T>(dark);
state_propagator->_getOptions()
.fns.post_deriv_fns[std::format("{}_post_deriv_{}",name(),id())]=
[t](constkm::Ktime&ti,constkm::Vec&x){returnt->postDeriv(ti,x);};
};
ifconstexpr(HasPreHop<T>){
kc::trace("RegisteringpreHopformodel{}.",name());
any_methods_registered=true;
kc::ks_ptr<Base>dark=shared_from_this();
kc::ks_ptr<T>t=kc::static_pointer_cast<T>(dark);
state_propagator->_getOptions()
.fns.pre_hop_fns[std::format("{}_pre_hop_{}",name(),id())]=
[t](constkm::Ktime&ti,constkm::Vec&x){returnt->preHop(ti,x);};
}
ifconstexpr(HasPostHop<T>){
kc::trace("RegisteringpostHopformodel{}.",name());
any_methods_registered=true;
kc::ks_ptr<Base>dark=shared_from_this();
kc::ks_ptr<T>t=kc::static_pointer_cast<T>(dark);
state_propagator->_getOptions()
.fns.post_hop_fns[std::format("{}_post_hop_{}",name(),id())]=
[t](constkm::Ktime&ti,constkm::Vec&x){returnt->postHop(ti,x);};
}
ifconstexpr(HasNextModelStepTime<T>){
kc::trace("nextHopEndTimeexistsformodel{}andwillbeusedovertheperiod.",
name());
}
ifconstexpr(HasPreModelStep<T>){
kc::trace("RegisteringpreModelStepformodel{}.",name());
any_methods_registered=true;
kc::ks_ptr<Base>dark=shared_from_this();
kc::ks_ptr<T>t=kc::static_pointer_cast<T>(dark);
ifconstexpr(HasNextModelStepTime<T>){
//RegisteratimedeventthatusesthenextModelStepTimeasthereschedule
//method.
autote=kd::TimedEvent::create(
std::format("{}_pre_model_step_{}",name(),id()),
state_propagator->_time_keeper->getTime(),
[t](constkm::Ktime&){
returnt->preModelStep(t->state_propagator->_time_keeper->getTime(),
t->state_propagator->_curr_X);
},
true);
te->reschedule_fn=[t](constkm::Ktime&ti){
returnt->nextModelStepTime(ti);
};
state_propagator->registerTimedEvent(te);
}elseif(period>km::Ktime{0}){
//Registeratimedeventthatusestheperiod.
autote=kd::TimedEvent::create(
std::format("{}_pre_model_step_{}",name(),id()),
state_propagator->_time_keeper->getTime(),
[t](constkm::Ktime&){
returnt->preModelStep(t->state_propagator->_time_keeper->getTime(),
t->state_propagator->_curr_X);
},
true);
te->reschedule_fn=[t](constkm::Ktime&ti){returnti+t->period;};
state_propagator->registerTimedEvent(te);
}
};
ifconstexpr(HasPostModelStep<T>){
kc::trace("RegisteringpostModelStepformodel{}.",name());
any_methods_registered=true;
kc::ks_ptr<Base>dark=shared_from_this();
kc::ks_ptr<T>t=kc::static_pointer_cast<T>(dark);
ifconstexpr(HasNextModelStepTime<T>){
if(autonext_time=t->nextModelStepTime(state_propagator->getTime());
next_time){
//RegisteratimedeventthatusesthenextModelStepTimeasthereschedule
//method.
autote=kd::TimedEvent::create(
std::format("{}_post_model_step_{}",name(),id()),
next_time.value(),
[t](constkm::Ktime&){
returnt->postModelStep(
t->state_propagator->_time_keeper->getTime(),
t->state_propagator->_curr_X);
},
false);
te->reschedule_fn=[t](constkm::Ktime&ti){
returnt->nextModelStepTime(ti);
};
state_propagator->registerTimedEvent(te);
}
}elseif(period>km::Ktime{0}){
//Registeratimedeventthatusestheperiod.
autote=kd::TimedEvent::create(
std::format("{}_post_model_step_{}",name(),id()),
state_propagator->getTime()+period,
[t](constkm::Ktime&){
returnt->postModelStep(t->state_propagator->_time_keeper->getTime(),
t->state_propagator->_curr_X);
},
false);
te->reschedule_fn=[t](constkm::Ktime&ti){returnti+t->period;};
state_propagator->registerTimedEvent(te);
}
};
if(notany_methods_registered){
throwstd::invalid_argument(std::format(
"Model\"{}\"didnotdefineanyof:preDeriv,postDeriv,preHop,postHop,"
"preModelStep,orpostModelStep.Mustdefineoneofthosemethods.",
name()));
}
//Markthismodelasregistered
_registered=true;
#endif
}
void_unregisterModel()override{
#ifndefPYBIND11_MKDOC_SKIP
if(not_registered){
kc::warn("Model{}isnotregistered.Doingnothing.",name());
return;
}
ifconstexpr(HasPreDeriv<T>){
kc::trace("UnregisteringpreDerivformodel{}.",name());
state_propagator->_getOptions().fns.pre_deriv_fns.erase(
std::format("{}_pre_deriv_{}",name(),id()));
}
ifconstexpr(HasPostDeriv<T>){
kc::trace("UnregisteringpostDerivformodel{}.",name());
state_propagator->_getOptions().fns.post_deriv_fns.erase(
std::format("{}_post_deriv_{}",name(),id()));
}
ifconstexpr(HasPreHop<T>){
kc::trace("UnregisteringpreHopformodel{}.",name());
state_propagator->_getOptions().fns.pre_hop_fns.erase(
std::format("{}_pre_hop_{}",name(),id()));
}
ifconstexpr(HasPostHop<T>){
kc::trace("UnregisteringpostHopformodel{}.",name());
state_propagator->_getOptions().fns.post_hop_fns.erase(
std::format("{}_post_hop_{}",name(),id()));
}
ifconstexpr(HasPreModelStep<T>){
kc::trace("UnregisteringpreModelStepformodel{}.",name());
ifconstexpr(HasNextModelStepTime<T>){
//It'spossiblethiswillhavealreadybeenremovedifthenextModelStepTime
//everreturned0.
state_propagator->unregisterTimedEvent(
std::format("{}_pre_model_step_{}",name(),id()),true,true);
}elseif(period>km::Ktime{0}){
//It'spossiblethiswillhavealreadybeenremovediftheuserchangedthe
//periodatsomepoint.
state_propagator->unregisterTimedEvent(
std::format("{}_pre_model_step_{}",name(),id()),true,true);
}
}
ifconstexpr(HasPostModelStep<T>){
kc::trace("UnregisteringpostModelStepformodel{}.",name());
ifconstexpr(HasNextModelStepTime<T>){
state_propagator->unregisterTimedEvent(
std::format("{}_post_model_step_{}",name(),id()),false,true);
}elseif(period>km::Ktime{0}){
//It'spossiblethiswillhavealreadybeenremovediftheuserchangedthe
//periodatsomepoint.
state_propagator->unregisterTimedEvent(
std::format("{}_post_model_step_{}",name(),id()),false,true);
}
}
//Markthismodelasnotregistered
_registered=false;
#endif
}
constEigen::Ref<constkm::Vec>_getContinuousStates()constoverride{
ifconstexpr(notstd::is_same_v<KModelContinuousStates,C>){
returncontinuous_states->getX();
}else{
throwstd::runtime_error("_getContinuousStatescalledonanon-continuousmodel.");
}
}
constEigen::Ref<constkm::Vec>_getContinuousStatesDeriv()override{
ifconstexpr(notstd::is_same_v<KModelContinuousStates,C>){
returncontinuous_states->getdX();
}else{
throwstd::runtime_error(
"_getContinuousStatesDerivcalledonanon-continuousmodel.");
}
}
void_setContinuousStates(constEigen::Ref<constkm::Vec>x)override{
ifconstexpr(notstd::is_same_v<KModelContinuousStates,C>){
returncontinuous_states->setX(x);
}else{
throwstd::runtime_error("_setContinuousStatescalledonanon-continuousmodel.");
}
}
};
}//namespaceKarana::Models