The signature has to be defined between the BEGIN_SIGNATURE_HOOK and END_SIGNATURE_HOOK macros.
To declare read variables :
To declare produced or modified variables :
These variables can be scalar (no dimension) or vector (1 dimension). When they are declared in the signature, the variables names suffixed by the [] symbol are vectors, the ones not suffixed are scalars ( myvariable is scalar and myothervariable[] is vector).
To declare the use of discrete events:
To declare extra files:
BEGIN_SIGNATURE_HOOK DECLARE_SIGNATURE_ID("water.surf-uz.runoff-infiltration.mseytoux"); DECLARE_SIGNATURE_NAME("Morel-Seytoux production on surface units"); DECLARE_SIGNATURE_DESCRIPTION("Production function computing infiltration and runoff at the surface of a unit using the Morel-Seytoux method, based on the Green and Ampt method."); DECLARE_SIGNATURE_DOMAIN("hydrology"); DECLARE_SIGNATURE_STATUS(openfluid::base::BETA); DECLARE_SIGNATURE_SDKVERSION; DECLARE_SIGNATURE_AUTHORNAME("Moussa R., Fabre J.-C."); DECLARE_SIGNATURE_AUTHOREMAIL("moussa@supagro.inra.fr, fabrejc@supagro.inra.fr"); DECLARE_REQUIRED_VAR("water.atm-surf.H.rain","SU","rainfall height on SU","m"); DECLARE_PRODUCED_VAR("water.surf.H.runoff","SU","runoff on the surface of the unit","m"); DECLARE_PRODUCED_VAR("water.surf.H.infiltration","SU","infiltration through the surface of the unit","m"); DECLARE_USED_PREVVAR("water.surf.Q.downstream-su","SU","output volume at the outlet of the upstream SUs","m3/s"); DECLARE_REQUIRED_INPUTDATA("ks","SU","hydraulic conductivity when saturated","m/s"); DECLARE_REQUIRED_INPUTDATA("thetares","SU","","m3/m3"); DECLARE_REQUIRED_INPUTDATA("thetasat","SU","","m3/m3"); DECLARE_REQUIRED_INPUTDATA("betaMS","SU","",""); DECLARE_REQUIRED_INPUTDATA("hc","SU","","m"); DECLARE_REQUIRED_INPUTDATA("thetaisurf","SU","","m3/m3"); DECLARE_FUNCTION_PARAM("resstep","numerical resolution step for ponding time",""); END_SIGNATURE_HOOK
The initParams method should be used to retreive function parameters, read from the model.xml file (See Access to function parameters). Once read, the values should be stored into class attributes to be accessed by other methods.
The prepareData method should be used to do data pre-processing before the consistency checking.
The checkConsistency method is called during the global consistency checking phase. It should be used to add function own consistency checking.
The initializeRun method should be used to do data initialization, or to compute values that do not change during simulation.
The runStep method is called at each exchange time step. it should contain the computation code.
The finalizeRun method should be used to do post-processing after simulation. it is the last method ran.
The spatial domain can be accessed using in different ways: using the access method of the CoreRepository, or use the macros intended for handling spatial entities. The first way is for experimented functions developpers only, so only the second way will be developped here.
To parse all units of a specific class, you can use:
To parse a specific list of units of a specific class, you can use:
bool MyFunc::runStep(const openfluid::base::SimulationStatus* SimStatus) { openfluid::core::Unit* SU; openfluid::core::Unit* UpSU; openfluid::core::UnitsPtrList_t* UpSUsList; DECLARE_UNITS_ORDERED_LOOP(1); DECLARE_UNITS_LIST_LOOP(25); BEGIN_UNITS_ORDERED_LOOP(1,"SU",SU) UpSUsList = SU->getFromUnits("SU"); BEGIN_UNITS_LIST_LOOP(25,UpSUsList,UpSU) OPENFLUID_GetVariable(UpSU,"water.surf.Q.downstream-su",CurrentStep-1,&TmpValue); END_LOOP END_LOOP; }
bool MyFunc::runStep(const openfluid::base::SimulationStatus* SimStatus) { int CurrentStep; openfluid::core::ScalarValue TmpValue; openfluid::core::Unit* SU; DECLARE_UNITS_ORDERED_LOOP(17); CurrentStep = SimStatus->getCurrentStep(); BEGIN_UNITS_ORDERED_LOOP(17,"SU",SU) OPENFLUID_GetVariable(SU,"the.requested.var",CurrentStep-1,&TmpValue); END_LOOP; }
The real time information is given through the openfluid::core::DateTime class.
<?xml version="1.0" standalone="yes"?> <openfluid> <model> <function fileID="myfunction"> <param name="myparam" value="2" /> </function> </model> </openfluid>
bool MyFunction::initParams(openfluid::core::FuncParamsMap_t Params) { m_MyParam = 0; //default value OPENFLUID_GetFunctionParameter(Params,"myparam",&m_MyParam); return true; }
To be reused in other part of the simulation function, the variable to store the value of function parameters should be declared as class variables. The parameters type can be string, double, integer, boolean, vector of string, vector of double, ... (see definitions of OPENFLUID_GetFunctionParameter to get more information about available types).
The scalar type is a double precision floating point type. The corresponding plain old type in C++ is double.
They can be accessed only from the runStep method.
bool MyFunction::runStep(const openfluid::base::SimulationStatus* SimStatus) { int CurrentStep; openfluid::core::ScalarValue TmpValue; openfluid::core::Unit* SU; DECLARE_UNITS_ORDERED_LOOP(12); CurrentStep = SimStatus->getCurrentStep(); BEGIN_UNITS_ORDERED_LOOP(12,"SU",SU) OPENFLUID_GetVariable(SU,"MyVar",CurrentStep,&TmpValue); TmpValue = TmpValue * 2; OPENFLUID_AppendVariable(SU,"MyVarX2",QOutput); END_LOOP return true; }
The name of the accessed input data must match the name given in the standard input files.
Example of process of events occurring on the current time step:
bool MyFunction::runStep(const openfluid::base::SimulationStatus* SimStatus) { openfluid::core::Unit* SU; openfluid::core::EventCollection EvColl; openfluid::core::Event* Ev; std::list<openfluid::core::Event* > *EvList; openfluid::core::DateTime BTime, ETime; DECLARE_EVENT_COLLECTION_LOOP; DECLARE_UNITS_ORDERED_LOOP(1); BTime = SimStatus->getCurrentTime(); ETime = BTime + SimStatus->getTimeStep()-1; BEGIN_UNITS_ORDERED_LOOP(1,"SU",SU) EvColl.clear(); OPENFLUID_GetEvents(SU,BTime,ETime,&EvColl); EvList = EvColl.getEventsList(); BEGIN_EVENT_COLLECTION_LOOP(EvColl.getEventsList(),Ev) if (Ev->isInfoEquals("molecule","glyphosate")) { // process the event } END_LOOP; END_LOOP; return true; }
declaration of the ID-map structure in the .h file :
class MyFunction : public openfluid::base::PluggableFunction { private: openfluid::core::IDScalarValueMap m_LastValue; public: // rest of the declaration of the class }
usage of the ID-map structure in the .cpp file :
bool MyFunction::runStep(const openfluid::base::SimulationStatus* SimStatus) { int ID; openfluid::core::ScalarValue TmpValue; openfluid::core::Unit* SU; DECLARE_UNITS_ORDERED_LOOP(7); BEGIN_UNITS_ORDERED_LOOP(7,"SU",SU) ID = SU->getID(); TmpValue = TmpValue + m_LastValue[ID] OPENFLUID_AppendVariable(SU,"MyVar",TmpValue); m_LastValue[ID] = TmpValue; END_LOOP return true; }
To raise a warning you can use OPENFLUID_RaiseWarning , to raise an error you can use OPENFLUID_RaiseError
As already said, an error stops the simulation the next time the kernel takes control of the simulation. It is strongly advised to force the control taking by the kernel by using a return false instruction justr after the raise of the error.
bool Myfunction::checkConsistency() { openfluid::core::ScalarValue TmpValue; openfluid::core::Unit* SU; DECLARE_SU_ORDERED_LOOP(1); BEGIN_SU_ORDERED_LOOP(1,"SU",SU) OPENFLUID_GetInputData(SU,"MyVar",&TmpValue); if (TmpValue <= 0) { OPENFLUID_RaiseError("my.function","Wrong value for the MyProp distributed property on SU"); return false; } END_LOOP return true; }
They are accessible from simulation functions using the OPENFLUID_OPENFLUID_GetRunEnvironment method.
bool MyFunction::initializeRun(const openfluid::base::SimulationInfo* SimInfo) { std::string InputDir; OPENFLUID_GetRunEnvironment("dir.input",&InputDir); // the current input directory is now available through the InputDir local variable return true; }
The keys for requesting runtime environment information are:
Fortran source code (FSubr.f90):
subroutine displayvector(Fsize,vect) implicit none integer Fsize,ifrom real*8 vect(Fsize) write(*,*) 'size',Fsize write(*,*) (Vect(i),i=1,Fsize) return end
BEGIN_EXTERN_FORTRAN EXTERN_FSUBROUTINE(toto)(FINT *Size, FREAL8 *Vect); END_EXTERN_FORTRAN
bool MyFunction::initializeRun(const openfluid::base::SimulationInfo* SimInfo) { openfluid::core::VectorValue* MyVect; MyVect = new openfluid::core::VectorValue(15,9); int Size = MyVect->getSize(); CALL_FSUBROUTINE(toto)(&Size,(MyVect->getData())); return true; }
The compilation and link of fortran source code is automatically done when adding fortran source files to the FUNC_FORTRAN variable in the CMake.in.config file (See Complete example).