Introduction
Un modèle J2K est un agencement de modules associé à un état initial et à des valeurs de paramètres. Le fonctionnement des modules est codé en java mais la plupart des utilisateurs ne programment pas eux-mêmes les modules, ils utilisent des modules existant qu'ils agencent et paramétrisent en utilisant un logiciel d'édition de modèle appelé Juice.
Pour organiser la communication avec des modèles J2K depuis l'extérieur, nous avons programmé et compilé des modules dédiés en Java. Reste à l'utilisateur d'intégrer correctement ces modules dans ses modèles. Ce chapitre décrit le fonctionnement interne de nos modules et la marche qu'un utilisateur doit suivre pour les utiliser.
Les développeurs qui souhaiteraient modifier nos modules ou créer de nouveaux modules de communication spécifiques sont invités à consulter le chapitre sur le Développement des modules.
Principe général
Un modèle J2K est spécifié dans un fichier JAMS (extension .jams") qui s'édite avec le logiciel "Juice".
Pour lancer Juice, il faut aller dans la racine de JAMS et lancer Juice avec java :
cd /ou/est/donc/jams
ls # verifier que juice-starter.jar est bien là
java -jar juice-starter.jar
L'ordonnancement des modèles J2K est classiquement organisés en au moins trois boucles principales: Une boucle dîte "temporelle" qui décrit la succession des opérations à effectuer à chaque pas de temps, et qui contient elle même une boucle dite "spatiale" (qui décrit la succession des opérations à effectuer sur les entités spatiales (HRU) classées de l'amont vers l'aval) et une boucles sur les brins de rivières (ReachLoop) classées également de l'amont vers l'aval.
L'enjeu du couplage est de pouvoir intervenir sur le système à tout moment de la simulation mais sans perturber la logique des calculs hydrologiques prévus pour être ordonnancée de l'amont vers l'aval. Pour cette raison les modules de communications sont organisés de la manière suivante: * le module CouplingCommunication, situé en début de boucle temporel reçoit toutes perturbations que subit le modèle à un pas de temps donné. * ces perturbations sont enregistrées avant l'exécution du pas de temps dans J2K mais ne sont pas répercutées immédiatement sur les variables d'état du système * lors du parcours des boucles spatiales (HRULoop) et de brins de rivières (ReachLoop) des modules de communication secondaires (par exemple "Drip irrigation") vont être exécutés à chaque fois qu'une modification a été prévu pour ce pas de temps au fur et à mesure du parcours de la boucle et donc en respectant l’ordonnancement amont / aval et la logique des calculs hydrologiques. La section ci-dessous présente un exemple type d’ordonnancement de modèle communicant.
Agencement des modules d'un modèle J2K communicant
Cet agencement n'est pas à considérer comme un exemple fonctionnel applicable dans une étude. C'est le modèle qui a été créé et utilisé pendant le développement des modules.
Les modules que nous avons créé sont en gras.
- timeloop
- CouplingCommunication
- ...
- ...
- TSInput
- HRULoop
- GetETP
- CouplingHruVariableChanger (lié à precip par exemple)
- Aspersion irrigation
- GetRainSnow
- VegetationInterception
- Drip irrigation
- Surface irrigation
- SnowProcess
- J2KProcessLumpedSoilWater_cowat
- GroundWater
- HRU2HRURouting
- HRU2ReachRounting
- HRU weight aggregator
- total area aggregator
- WatershedsInputAndState
- ReachLoop
- BuechReachInOut
- REACH2REACHRouting
- ChannelStorageAggregator
- ReachesOutput
- Converters
Fonctionnement et implémentation des modules
Pour communiquer avec les modules, voir la page sur le protocole de communication.
CouplingCommunication
C'est le module qui gère la communication avec l'extérieur du modèle. On peut dire que c'est le module principal, c'est celui qui permet effectivement le couplage. Il comprend le protocole (on dit qu'il "implémente" le protocole) que nous avons défini et dont nous parlons dans la page dédiée.
Ce module bloque l'exécution du modèle en début de pas de temps et attend des commandes.
- On a une commande pour le débloquer pendant un ou plusieurs pas de temps.
- On a des commandes pour définir les valeurs d'irrigation, de prélèvement/rejet, d'infiltration par les canaux.
- Une commande pour tout arrêter
- Des commandes pour récupérer des valeurs associées aux reachs et aux HRUs
Les commandes sont aussi détaillées dans la page sur le protocole.
Architecture du module de communication
Ce module de communication exécute ses deux tâches en parallèle (multithread).
- L'ordonnancement : le contrôle de l'exécution du modèle
- La communication : l'écoute sur le réseau pour recevoir et répondre aux commandes venant de l'extérieur
Cela signifie qu'il peut recevoir une commande même quand ce n'est pas à son tour de s'exécuter dans le modèle.
Ordonnancement
L'ordonnancement est géré dans la méthode principale présente dans tous les modules J2K : la méthode run()
. C'est celle qui est appelée par JAMS pour dire à un module de s'exécuter.
On utilise ici des sémaphores pour bloquer et ensuite libérer l'exécution. Les sémaphores sont des sortes de verrous logiciels. C'est avec ceux-ci que l'on gère habituellement la synchronisation ou l'ordonnancement entre les exécutions parallèles d'un programme (programmation concurrente).
Communication
En informatique, les communications sur les réseaux respectent l'architecture "client-serveur". Le serveur est en permanence en attente de connexion. c'est le client qui initie la communication en se connectant au serveur. Dans notre cas, le modèle J2K est le serveur, l'entité extérieure qui pilote le modèle J2K joue le rôle de client.
La gestion de la communication est très basique. Le module crée un socket (partie serveur) qui se met en attente d'une connexion (requête HTTP). Lorsqu'une requête est reçue, le message qu'elle contient est analysé et en fonction de la commande qu'elle contient, le module de communication va :
- effectuer une action (débloquer le modèle, stopper le modèle, renvoyer des informations sur l'état du modèle, etc...)
- ou bien stocker des informations (les valeurs d'irrigation que l'on veut modifier par exemple)
Le stockage d'information est effectué dans des attributs statiques (des dictionnaires) pour faciliter leur accès depuis les autres modules. On fournit aussi des méthodes statiques pour simplifier ces accès et les rendre plus lisibles.
Interaction avec les autres modules
Quand les autres modules de couplage s'exécutent (Aspersion, Drip, BuechReachInOut etc...), ils accèdent aux informations stockées par le module de communication pour connaitre les valeurs qui viennent de l'extérieur. On peut dire qu'ils "demandent" au module de communication ce qu'ils doivent faire.
Ils utilisent tous la méthode statique getTableValue()
:
Double extValue = CouplingCommunication.getTableValue(moduleName, hruID);
CouplingVariableChanger
Les modules CouplingHruVariableChanger et CouplingReachVariableChanger peuvent être insérés n'importe où dans la HRU loop ou la REACH loop. On leur donne un nom et on fait le lien avec un attribut (une variable J2K propre à chaque HRU/REACH). On choisit si on veut forcer la valeur de l'attribut ou juste y ajouter une valeur avec le paramètre setOrAdd.
Pour communiquer les valeurs à ces modules, on envoie la commande "set" au module de communication en lui précisant quel module on veut atteindre.
Par exemple, si on veut influer sur l'attribut "precip" des HRU, disons qu'on veut ajouter des précipitations, on ajoute où on veut une instance du module CouplingHruVariableChanger dans le modèle J2K (avec juice), on définit son nom en mettant l'attribut moduleName à "couplingPrecip" (par exemple) et on met la valeur de l'attribut setOrAdd à 1. On fait le lien entre l'attribut attribute du module avec l'attribut de notre choix, ici c'est precip.
Ensuite, dans le programme de pilotage du couplage (Rcoupler.R
pour nous), on appelle la fonction j2kSet()
en lui disant qu'on veut ajouter 10, 20 et 30 de précip pour les HRU 1, 2 et 3 :
j2kSet('couplingPrecip', c(1, 2, 3), c(10, 20, 30))
Donc, à l'intérieur du modèle J2K, lorsque c'est au tour du module CouplingVariableChanger
nommé "couplingPrecip" de s'exécuter, ce dernier va demander au module de communication quelles entités il doit modifier et quelles valeurs il doit définir pour chacun d'entre elles. Cela est possible avec un simple appel à une méthode statique :
Double extValue = CouplingCommunication.getTableValue(moduleName, hruID);
Cette ligne signifie : Je suis le module moduleName
(qui vaut "couplingPrecip" dans notre exemple) et je veux savoir quelle valeur je dois mettre dans l'attribut dont je suis responsable pour la HRU dont l'identifiant est "HruId". Dans ce contexte, on connait l'identifiant de la HRU puisqu'on est dans la boucle spatiale (HRU loop).
!! ATTENTION !! Il faut bien faire attention aux unités. Dans notre exemple, on modifie precip qui est en MM. Comme ces modules sont génériques, ils ne peuvent pas faire de conversion comme les autres modules. Ces modules génériques ne savent pas ce qu'ils manipulent.
Aspersion
Ce module est très spécifique, il permet d'ajouter de l'irrigation par aspersion depuis l'extérieur. Il doit être placé au bon endroit dans le modèle car il faut que les modifications qu'il apportent aient un effet sur les calculs impliquant les précipitations.
Pseudo code de l'irrigation par aspersion :
precip = precip + aspersionIrrig
irrigationTotal = irrigationTotal + aspersionIrrig
precip
est la variable interne (attribut des HRUs) contenant la quantité de précipitations du pas de temps.
Son fonctionnement est très simple, il ajoute de l'eau dans les précipitations.
Il met aussi à jour une variable contenant la quantité totale d'irrigation du pas de temps.
Drip
Ce module ajoute de l'irrigation au goutte à goutte.
Pseudo code de l'irrigation par drip :
actDrip = min(maxMPS - actMPS, dripIrrig)
actMPS = actMPS + actDrip
remainingAfterDrip = drip - actDrip
irrigationTotal = irrigationTotal + actDrip
Ce qui reste de drip, une fois qu'on a excédé la capacité d'absorption du sol, sera considéré comme de l'irrigation de surface. On met aussi à jour l'irrigation totale dans ce module.
Surface
Ce module ajoute de l'irrigation de surface.
Pseudo code de l'irrigation de surface :
surfaceIrrig = surfaceIrrig + remainingAfterDrip
netRain = netRain + surfaceIrrig
irrigationTotal = irrigationTotal + surfaceIrrig
On ajoute l'irrigation de surface dans la pluie (pas les précipitations).
Soil infiltration (J2KProcessLumpedSoilWater_cowat)
Ce module applique l'infiltration par les canaux. Sont effet est de rajouter de l'eau dans un des réservoirs des HRUs. La quantité d'infiltration n'est pas calculée dans ce module, elle provient de l'extérieur donc potentiellement d'une autre modèle qui prend en compte les canaux et connait la quantité d'eau qui s'infiltre dans les HRUs.
Ce module est assez complexe, son pseudo code serait trop long. on peut expliquer son fonctionnement en disant qu'on est parti du module J2KProcessLumpedSoilWater (disponible dans les modules J2K_Irstea) et qu'on l'a adapté à notre cas de figure.
Il a une influence sur l'attribut actLPS
des HRUs.
BuechReachInOut
Ce module applique simplement les prélèvements et rejets dans les reachs. On prélève et rejette dans RD1 (attribut actRD1
des reachs).
Unités
Pour les 3 modules d'irrigation, on passe une valeur en litres (convertie en interne en mm).
- aspersion modifie precip (L)
- drip modifie actMPS (L)
- surface modifie netRain (L)
Pour l'infiltration, c'est aussi en litres.
- ça modifie actLPS (L).
Pour les prélèvements et rejets, c'est en litres.
- ça modifie actRD1 (L)
Tout est en litres pour les modules spécifiques (donc tous les modules sauf CouplingHruVariableChanger et CouplingReachVariableChanger).