... | ... | @@ -11,18 +11,18 @@ La quantité initiale pour chacun de ces produits est définie aléatoirement en |
|
|
# Baisse des stocks
|
|
|
|
|
|
Les stocks baissent à intervalle régulier. Le moment où les stocks baissent est déterminée par :
|
|
|
- le paramètre `nbStepsbetweenDS` présent dans le fichier `Parameters.gaml`. Ce paramètre agit sur la fréquence et est fixé à 24. Ainsi, les stocks baissent une fois par jour.
|
|
|
- le paramètre `timeShifting`. Ce paramètre est initialisé aléatoirement (pour chaque agent destinataire) entre 0 et 23 inclus au début de la simulation. Il permet de garantir que les agents ne vont pas tous voir leurs stocks baisser au même moment. Cela permet d'éviter des phénomènes de synchronisation entre les agents.
|
|
|
- le paramètre `🅿 nbStepsbetweenDS` présent dans le fichier `🅶 Parameters.gaml`. Ce paramètre agit sur la fréquence et est fixé à 24. Ainsi, les stocks baissent une fois par jour.
|
|
|
- la variable `🆅 timeShifting`. Ce paramètre est initialisé aléatoirement (pour chaque agent destinataire) entre 0 et 23 inclus au début de la simulation. Il permet de garantir que les agents ne vont pas tous voir leurs stocks baisser au même moment. Cela permet d'éviter des phénomènes de synchronisation entre les agents.
|
|
|
|
|
|
Pour chaque stock présent, la quantité baisse aléatoirement entre 0 et (la quantité maximale du stock / le taux de consommation).
|
|
|
|
|
|
Le taux de consommation (variable `decreasingRateOfStocks `dans le code) est défini à l'initialisation pour chaque agent destinataire. La valeur de ce paramètre est déterminée à partir de l'indice de Huff (pré-calculé et stocké directement dans le Shapefile associé aux `FinalConsignee`) et de deux autres paramètres renseignés par l'utilisateur. En effet, ce dernier doit indiquer les taux de consommation maximaux et minimaux (`valForMaxHuff` et `valForMinHuff` dans le code). On associe ainsi linéairement l'indice de Huff d'un agent à un taux de consommation : plus l'indice de Huff est élevé (plus l'agent est censé avoir de clients), plus le taux est bas (impliquant une baisse de stock importante car "quantité maximale / taux de consommation" => tend vers "quantité maximale" lorsque "taux de consommation" tend vers 1).
|
|
|
Le taux de consommation (variable `🆅 decreasingRateOfStocks `dans le code) est défini à l'initialisation pour chaque agent destinataire. La valeur de ce paramètre est déterminée à partir de l'indice de Huff (pré-calculé et stocké directement dans le Shapefile associé aux `🆃 FinalConsignee`) et de deux autres paramètres renseignés par l'utilisateur. En effet, ce dernier doit indiquer les taux de consommation maximaux et minimaux (`🅿 valForMaxHuff` et `🅿 valForMinHuff` dans le code). On associe ainsi linéairement l'indice de Huff d'un agent à un taux de consommation : plus l'indice de Huff est élevé (plus l'agent est censé avoir de clients), plus le taux est bas (impliquant une baisse de stock importante car "quantité maximale / taux de consommation" => tend vers "quantité maximale" lorsque "taux de consommation" tend vers 1).
|
|
|
|
|
|
En général, je fixe `valForMinHuff` à 6 et `valForMaxHuff` à 2.
|
|
|
En général, je fixe `🅿 valForMinHuff` à 6 et `🅿 valForMaxHuff` à 2.
|
|
|
|
|
|
# Choix d'un prestataire logistique
|
|
|
|
|
|
Le choix d'un prestataire logistique s'effectue via la fonction `chooseLogisticProvider`.
|
|
|
Le choix d'un prestataire logistique s'effectue via la fonction `🅵 chooseLogisticProvider`.
|
|
|
|
|
|
Cette fonction trie une liste de l'ensemble des prestataires en fonction de la distance euclidienne à partir du destinataire. Puis elle tire un nombre aléatoire entre 0 et 1, et biaise celui-ci à l'aide d'une fonction f(x)=x^4. Le nombre obtenu permet de tirer l'un des prestataires de la liste. Ainsi, plus le prestataire est proche du destinataire plus il aura de chance d'être sélectionné (on part du principe qu'un acteur a plus de facilité de traiter avec un acteur géographiquement proche de lui).
|
|
|
|
... | ... | @@ -30,54 +30,66 @@ Cette fonction trie une liste de l'ensemble des prestataires en fonction de la d |
|
|
|
|
|
Un prestataire logistique s'occupe du réapprovisionnement d'un destinataire final. Par conséquent, ce dernier doit établir un contrat avec l'un d'entre eux. Chaque destinataire doit avoir exactement un contrat avec un prestataire, mais un prestataire peut avoir des contrats avec plusieurs destinataires. Un contrat est établi lorsqu'un destinataire choisit son prestataire. Le prestataire ne peut pas refuser l'établissement d'un contrat.
|
|
|
|
|
|
Le paramètre global `allowLSPSwitch` est un booléen indiquant si les agents destinataires ont le droit de changer de prestataire au cours de la simulation. S'ils n'ont pas le droit, alors la gestion du contrat s'arrête ici, sinon, d'autres règles, décrites ci-dessous, s'appliquent.
|
|
|
Le paramètre global `🅿 allowLSPSwitch` est un booléen indiquant si les agents destinataires ont le droit de changer de prestataire au cours de la simulation. S'ils n'ont pas le droit, alors la gestion du contrat s'arrête ici, sinon, d'autres règles, décrites ci-dessous, s'appliquent.
|
|
|
|
|
|
Le paramètre `numberOfHoursOfContract` détermine le nombre d'heure écoulée depuis la création du contrat. Et les contrats ont une durée minimale définie globalement au sein de `Parameters.gaml` par le paramètre `minimalNumberOfHoursOfContract`.
|
|
|
Le paramètre `🅿 numberOfHoursOfContract` détermine le nombre d'heure écoulée depuis la création du contrat. Et les contrats ont une durée minimale définie globalement au sein de `🅶 Parameters.gaml` par le paramètre `🅿 minimalNumberOfHoursOfContract`.
|
|
|
|
|
|
À l'initialisation des agents, on attribue à la variable `numberOfHoursOfContract` de chaque agent une valeur aléatoire comprise entre 100 et `minimalNumberOfHoursOfContract`. Ainsi, on contraint les agents à conserver leur prestataire pendant au moins les 100 premières étapes de la simulation, mais on évite également des phénomènes de synchronisation entre eux (qu'ils ne changent pas tous de prestataire au même moment).
|
|
|
À l'initialisation des agents, on attribue à la variable `🆅 numberOfHoursOfContract` de chaque agent une valeur aléatoire comprise entre 100 et `🅿 minimalNumberOfHoursOfContract`. Ainsi, on contraint les agents à conserver leur prestataire pendant au moins les 100 premières étapes de la simulation, mais on évite également des phénomènes de synchronisation entre eux (qu'ils ne changent pas tous de prestataire au même moment).
|
|
|
|
|
|
Un agent destinataire peut tester à intervalle régulier si son prestataire est suffisamment efficace (voir ci-après pour les mesures d'efficacité). La durée de l'intervalle entre deux tests est égale à la valeur de `minimalNumberOfHoursOfContract`. Si le prestataire est considéré assez efficace, alors on le garde au moins jusqu'au prochain test. Par contre, si ce n'est pas le cas, alors on rompt le contrat avec lui et on choisit un autre prestataire. Les stocks gérés par l'ancien prestataire sont transférés au nouveau.
|
|
|
Un agent destinataire peut tester à intervalle régulier si son prestataire est suffisamment efficace (voir ci-après pour les mesures d'efficacité). La durée de l'intervalle entre deux tests est égale à la valeur de `🅿 minimalNumberOfHoursOfContract`. Si le prestataire est considéré assez efficace, alors on le garde au moins jusqu'au prochain test. Par contre, si ce n'est pas le cas, alors on rompt le contrat avec lui et on choisit un autre prestataire. Les stocks gérés par l'ancien prestataire sont transférés au nouveau.
|
|
|
|
|
|
# Mesures d'efficacité des prestataires
|
|
|
|
|
|
C'est la fonction `shouldISwitchMyLSP` qui est appelée pour déterminer si un prestataire est considéré assez efficace ou non. Elle retourne donc un booléen : `vrai` si on doit changer, `faux` sinon.
|
|
|
C'est la fonction `🅵 shouldISwitchMyLSP` qui est appelée pour déterminer si un prestataire est considéré assez efficace ou non. Elle retourne donc un booléen : `vrai` si on doit changer, `faux` sinon.
|
|
|
|
|
|
Un agent destinataire dispose de 3 stratégies différentes pour déterminer le besoin de changement. La stratégie qui sera effectivement utilisé par les agents est déterminée par la variable `stratMeasureLSPEfficiency`. La valeur de cette variable est déterminé par plusieurs paramètres définis par l'utilisateur. Ce dernier peut en effet décider si l'ensemble des destinataires finaux doivent utiliser la même stratégie ou pas. Cela se fait grâce au paramètre `isLocalLSPSwitcStrat`.
|
|
|
Un agent destinataire dispose de 3 stratégies différentes pour déterminer le besoin de changement. La stratégie qui sera effectivement utilisé par les agents est déterminée par la variable `🆅 stratMeasureLSPEfficiency`. La valeur de cette variable est déterminé par plusieurs paramètres définis par l'utilisateur. Ce dernier peut en effet décider si l'ensemble des destinataires finaux doivent utiliser la même stratégie ou pas. Cela se fait grâce au paramètre `🅿 isLocalLSPSwitcStrat`.
|
|
|
|
|
|
Si `isLocalLSPSwitcStrat` est faux, alors la valeur de `stratMeasureLSPEfficiency` pour chaque agent sera égale à la valeur défini par `globalLSPSwitchStrat` (dans `Parameters.gaml`).
|
|
|
Si `🅿 isLocalLSPSwitcStrat` est faux, alors la valeur de `🆅 stratMeasureLSPEfficiency` pour chaque agent sera égale à la valeur défini par `🅿 globalLSPSwitchStrat` (dans `🅶 Parameters.gaml`).
|
|
|
|
|
|
Si `isLocalLSPSwitcStrat` est vrai, alors la valeur de `stratMeasureLSPEfficiency` est aléatoirement sélectionnée parmi l'ensemble des stratégies mis à disposition par l'utilisateur (via le paramètre global `possibleLSPSwitcStrats` présent dans `Parameters.gaml`).
|
|
|
Si `🅿 isLocalLSPSwitcStrat` est vrai, alors la valeur de `🆅 stratMeasureLSPEfficiency` est aléatoirement sélectionnée parmi l'ensemble des stratégies mis à disposition par l'utilisateur (via le paramètre global `🅿 possibleLSPSwitcStrats` présent dans `🅶 Parameters.gaml`).
|
|
|
|
|
|
Voici, ci-dessous, un descriptif des différentes stratégies implémentées.
|
|
|
|
|
|
## Stratégie minimisant le nombre de produits en rupture de stock
|
|
|
|
|
|
Un prestataire est considéré inefficace si la valeur de `localAverageNbStockShortagesLastSteps` est plus grande que celle de `averageNbStockShortages`. Ces deux variables sont mis à jour à chaque pas de temps par le reflex `updateAverageLPEfficiency` présent dans le fichier `Observer.gaml`.
|
|
|
Un prestataire est considéré inefficace si la valeur de `🆅 localAverageNbStockShortagesLastSteps` est plus grande que celle de `🆅 averageNbStockShortages`. Ces deux variables sont mis à jour à chaque pas de temps par le reflex `🅵 updateAverageLPEfficiency` présent dans le fichier `🅶 Observer.gaml`.
|
|
|
|
|
|
`averageNbStockShortages` correspond au nombre moyen de produits en rupture de stock au sein de tous les destinataires sur les `nbStepsConsideredForLPEfficiency` dernières étapes. `nbStepsConsideredForLPEfficiency` étant un paramètre global défini dans `Parameters.gaml` et valant `96` (correspondant à 4 jours simulés). Bien sûr, puisqu'il s'agit d'un paramètre, il peut être modifier par l'utilisateur.
|
|
|
`🆅 averageNbStockShortages` correspond au nombre moyen de produits en rupture de stock au sein de tous les destinataires sur les `🅿 nbStepsConsideredForLPEfficiency` dernières étapes. `🅿 nbStepsConsideredForLPEfficiency` étant un paramètre global défini dans `🅶 Parameters.gaml` et valant `96` (correspondant à 4 jours simulés). Bien sûr, puisqu'il s'agit d'un paramètre, il peut être modifier par l'utilisateur.
|
|
|
|
|
|
`localAverageNbStockShortagesLastSteps` correspond au nombre moyen de produit en rupture de stock au sein du destinataire courant et depuis le début du contrat avec le prestataire.
|
|
|
`🆅 localAverageNbStockShortagesLastSteps` correspond au nombre moyen de produit en rupture de stock au sein du destinataire courant et depuis le début du contrat avec le prestataire.
|
|
|
|
|
|
## Stratégie minimisant le temps nécessaire à la livraison
|
|
|
|
|
|
Un prestataire est considéré inefficace si la valeur de `localTimeToBeDelivered` est plus grande que celle de `averageTimeToBeDelivered`. Ces deux variables sont mis à jour à chaque pas de temps par le reflex `updateAverageTimeToBeDelivered` présent dans le fichier `Observer.gaml`.
|
|
|
Un prestataire est considéré inefficace si la valeur de `🆅 localTimeToBeDelivered` est plus grande que celle de `🆅 averageTimeToBeDelivered`. Ces deux variables sont mis à jour à chaque pas de temps par le reflex `🅵 updateAverageTimeToBeDelivered` présent dans le fichier `🅶 Observer.gaml`.
|
|
|
|
|
|
`averageTimeToBeDelivered` correspond au nombre moyen d'étapes nécessaires à la marchandise pour être livrée auprès des destinataires suite aux commandes de réapprovisionnement, et sachant que seules les `nbDeliveriesConsideredForTimeToDelivered` dernières livraisons sont prises en compte. `nbDeliveriesConsideredForTimeToDelivered` étant un paramètre global défini dans `Parameters.gaml` et valant `50`. Bien sûr, puisqu'il s'agit d'un paramètre, il peut être modifier par l'utilisateur.
|
|
|
`🆅 averageTimeToBeDelivered` correspond au nombre moyen d'étapes nécessaires à la marchandise pour être livrée auprès des destinataires suite aux commandes de réapprovisionnement, et sachant que seules les `🅿 nbDeliveriesConsideredForTimeToDelivered` dernières livraisons sont prises en compte. `🅿 nbDeliveriesConsideredForTimeToDelivered` étant un paramètre global défini dans `🅶 Parameters.gaml` et valant `50`. Bien sûr, puisqu'il s'agit d'un paramètre, il peut être modifier par l'utilisateur.
|
|
|
|
|
|
`localTimeToBeDelivered` correspond au nombre moyen d'étapes nécessaires à la marchandise pour être livrée auprès du destinataire courant suite aux commandes de réapprovisionnement, et sachant que seules les `nbDeliveriesConsideredForTimeToDelivered` dernières livraisons sont prises en compte.
|
|
|
`🆅 localTimeToBeDelivered` correspond au nombre moyen d'étapes nécessaires à la marchandise pour être livrée auprès du destinataire courant suite aux commandes de réapprovisionnement, et sachant que seules les `🅿 nbDeliveriesConsideredForTimeToDelivered` dernières livraisons sont prises en compte.
|
|
|
|
|
|
## Stratégie minimisant les coûts financiers
|
|
|
|
|
|
Un prestataire est considéré inefficace si la valeur de `localVolumeNormalizedAverageCosts` est plus grande que celle de `averageCostsOfNeighbors`. Ces deux variables sont mis à jour à chaque pas de temps par le reflex `update_average_costs` présent dans le fichier `Observer.gaml`.
|
|
|
Un prestataire est considéré inefficace si la valeur de `🆅 localVolumeNormalizedAverageCosts` est plus grande que celle de `averageCostsOfNeighbors`. Ces deux variables sont mis à jour à chaque pas de temps par le reflex `🅵 update_average_costs` présent dans le fichier `🅶 Observer.gaml`.
|
|
|
|
|
|
Le reflex `update_average_costs` calcul, pour chaque agent destinataire, le coût moyen par livraison sur les `costsMemory` dernières livraison (`costsMemory` étant un paramètre défini globalement dans `Parameters.gaml`). Le coût d'une seule livraison est calculé ainsi :
|
|
|
Le reflex `🅵 update_average_costs` calcul, pour chaque agent destinataire, le coût moyen par livraison sur les `🅿 costsMemory` dernières livraison (`🅿 costsMemory` étant un paramètre défini globalement dans `🅶 Parameters.gaml`). Le coût d'une seule livraison est calculé ainsi :
|
|
|
|
|
|
`volume * (km_route * coûtUnitaire_route + km_fluvial * coûtUnitaire_fluvial + km_maritime * coûtUnitaire_maritime)`
|
|
|
|
|
|
Les variables `km_mode` correspondent au nombre de kilomètres parcourus par la marchandise dans le mode de transport spécifié. Les variables `coûtUnitaire_mode` correspondent au coût de transport d'une seule unité de marchandise sur un seul kilomètre et dans le mode de transport spécifié.
|
|
|
Les variables `🆅 km_mode` correspondent au nombre de kilomètres parcourus par la marchandise dans le mode de transport spécifié. Les variables `🅿 coûtUnitaire_mode` correspondent au coût de transport d'une seule unité de marchandise sur un seul kilomètre et dans le mode de transport spécifié.
|
|
|
|
|
|
Néanmoins, deux destinataires différents ne peuvent comparer leur prestataire respectif en comparant directement ce coût moyen car les situations de ces deux destinataires peuvent être totalement différentes : par exemple, l'un peut traiter des volumes beaucoup plus important; et ils peuvent aussi se trouver dans des secteurs géographiques très différents (avec des distances de transport également différentes). Cela fausserait donc la comparaison.
|
|
|
|
|
|
Pour résoudre ce problème, on a donc décidé en premier point de normaliser le coût moyen en le divisant par la somme des volumes de marchandises transportées pour un client : il s'agit de la variable `localVolumeNormalizedAverageCosts`.
|
|
|
Pour résoudre ce problème, on a donc décidé en premier point de normaliser le coût moyen en le divisant par la somme des volumes de marchandises transportées pour un client : il s'agit de la variable `🆅 localVolumeNormalizedAverageCosts`.
|
|
|
|
|
|
Et ensuite, on ne compare `localVolumeNormalizedAverageCosts` qu'avec les coûts moyens normalisés des "voisins" du destinataire. La liste de ces voisins est déterminée par la fonction `buildNeighborsList` présente dans `FinalConsignee.gaml`. On se contente de récupérer l'ensemble des destinataires présents à moins de `neighborsDistance` km de l'agent courant. `neighborsDistance` est un paramètre défini globalement dans `Parameters.gaml`. Si la fonction `buildNeighborsList` ne trouve pas d'agent dans ce périmètre, alors elle double la taille du périmètre (puis triple, et ainsi de suite jusqu'à trouver au moins un voisin). Ce coût moyen des voisins correspond donc à la variable `averageCostsOfNeighbors`. |
|
|
Et ensuite, on ne compare `🆅 localVolumeNormalizedAverageCosts` qu'avec les coûts moyens normalisés des "voisins" du destinataire. La liste de ces voisins est déterminée par la fonction `🅵 buildNeighborsList` présente dans `🆃 FinalConsignee.gaml`. On se contente de récupérer l'ensemble des destinataires présents à moins de `🅿 neighborsDistance` km de l'agent courant. `🅿 neighborsDistance` est un paramètre défini globalement dans `🅶 Parameters.gaml`. Si la fonction `🅵 buildNeighborsList` ne trouve pas d'agent dans ce périmètre, alors elle double la taille du périmètre (puis triple, et ainsi de suite jusqu'à trouver au moins un voisin). Ce coût moyen des voisins correspond donc à la variable `🆅 averageCostsOfNeighbors`.
|
|
|
|
|
|
# Légende
|
|
|
|
|
|
`🅵 fonctions`
|
|
|
|
|
|
`🅿 paramètre du modèle`
|
|
|
|
|
|
`🆃 type d'agent`
|
|
|
|
|
|
`🆅 variable`
|
|
|
|
|
|
`🅶 Fichier contenant des variables et/ou des fonctions globales` |
|
|
\ No newline at end of file |