mirror of https://gitlab.com/pamhyr/pamhyr2
doc: dev: Add note about scenarios.
parent
40d90424a3
commit
5598464081
|
|
@ -0,0 +1,661 @@
|
|||
# senario.org -- Pamhyr developers documentation
|
||||
# Copyright (C) 2023-2024 INRAE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#+STARTUP: indent show2levels
|
||||
|
||||
#+INCLUDE: ../tools/macro.org
|
||||
#+INCLUDE: ../tools/latex.org
|
||||
|
||||
#+TITLE: Scenario implementation in Pamhyr2: \textbf{Preparatory work}
|
||||
#+SUBTITLE: Version: {{{version}}}
|
||||
#+AUTHOR: {{{INRAE}}}
|
||||
|
||||
#+OPTIONS: toc:t
|
||||
#+LANGUAGE: UKenglish
|
||||
|
||||
#+latex: \newpage
|
||||
|
||||
* COMMENT tools
|
||||
|
||||
#+begin_src emacs-lisp :export none
|
||||
(add-to-list 'org-structure-template-alist
|
||||
'("p" "#+begin_src python :python python3 :results output\n\n#+end_src"))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
#+begin_example
|
||||
((p #+begin_src python :python python3 :results output
|
||||
|
||||
,#+end_src) (d . #+name: graph-XXX
|
||||
,#+header: :results drawer
|
||||
,#+header: :exports results
|
||||
,#+header: :post attr_wrap(width="12cm", data=*this*, name="graph-XXX", caption="Graph XXX", float="t")
|
||||
,#+begin_src dot :file "images/graph-XXX.png" :cache no
|
||||
digraph {
|
||||
bgcolor="transparent";
|
||||
node[colorscheme=set19,shape=box,style="filled",fillcolor=9];
|
||||
}
|
||||
,#+end_src) (p . src python :python python3 :results output :noweb yes
|
||||
|
||||
src) (t . EXPORT latex
|
||||
\begin{table}
|
||||
\centering
|
||||
\resizebox{0.6\linewidth}{!}{
|
||||
|
||||
}
|
||||
%\vspace{0.5pt}
|
||||
%\caption{Number of tests by errors types}
|
||||
\end{table}
|
||||
,#+E) (B . src shell :session *shell* :results output :exports both) (a . export ascii) (c . center) (C . comment) (e . example) (E . export) (h . export html) (l . export latex) (q . quote) (s . src) (v . verse))
|
||||
#+end_example
|
||||
|
||||
#+name: dbg-execute
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def execute(db, query):
|
||||
print(query)
|
||||
return db.execute(query)
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: dbg-execute
|
||||
|
||||
#+name: execute
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def execute(db, query):
|
||||
return db.execute(query)
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: execute
|
||||
|
||||
* Principe
|
||||
|
||||
** Scenario
|
||||
|
||||
A scenario is defined by a name, a description and a parent
|
||||
scenario. If a scenario as no parent, parent value is =None=. So, in
|
||||
Pamhyr2 the list of scenario must by a tree with a root defined by a
|
||||
default scenarios with no parent.
|
||||
|
||||
#+begin_src sql
|
||||
CREATE TABLE scenario(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL, -- Name of the scenario
|
||||
description TEXT NOT NULL, -- Rich text description
|
||||
parent_id INTEGER REFERENCES scenario(id) -- Recursive references
|
||||
-- for parent scenario
|
||||
)
|
||||
#+end_src
|
||||
|
||||
** Data
|
||||
|
||||
Each data is associated with one and only one scenario. This senario
|
||||
is defined by the =id=. So, each data structure who can saved in DB
|
||||
must add the following lines in table définition:
|
||||
|
||||
#+begin_src sql
|
||||
scenario_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
||||
#+end_src
|
||||
|
||||
To avoid, at least, memory usage exploitation, by default, a scenario
|
||||
must saved only the modified data from its parent. This contrains
|
||||
require to wrap any data modification to update the data scenario id
|
||||
to current scenario id in source code. In addition, we need to modify
|
||||
the data loading méthode to take in considération this recursive
|
||||
dependencies.
|
||||
|
||||
#+CAPTION: Recursive loading method
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def rec_load(cls, db, current_scenario):
|
||||
# Default case, end of recursion
|
||||
if current_scenario is None:
|
||||
return [] # Default value
|
||||
|
||||
table = db.execute(f"<MY SELECT> WHERE scenario_id = {current_scenario}")
|
||||
|
||||
# If no data for this scenario, recursion
|
||||
if len(table) == 0:
|
||||
parent_id = db.get_parent_id(current_scenario)
|
||||
return cls.rec_load(db, parent_id)
|
||||
|
||||
# Otherelse, parse data and return...
|
||||
#+end_src
|
||||
|
||||
With this method, modify a data in parent scenario, modify this data
|
||||
in child scenario too. Unless is value has been previously modified in
|
||||
the child scenario. Deny tree node modification, except for leaf.
|
||||
|
||||
This method work, but it dont allow to delete data un child scenarios,
|
||||
because if we dont found data for this scenario we search for parent
|
||||
scenario. So, we need a condition to stop recursion in case of data
|
||||
deletion in child scenario. To stop the recurtion, we can use a dummy
|
||||
data flag. By default, this flags is =false=, but if this flags is
|
||||
=true= the data is ignored at loading.
|
||||
|
||||
So, we can use this flags for each data structure who can saved in DB:
|
||||
|
||||
#+begin_src sql
|
||||
is_dummy BOOLEAN, -- True if this data as been deleted
|
||||
#+end_src
|
||||
|
||||
Modify the loading method:
|
||||
|
||||
#+CAPTION: Recursive loading method with possible dummy data
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def rec_load_2(cls, db, current_scenario):
|
||||
# Default case, end of recursion
|
||||
if current_scenario is None:
|
||||
return [] # Default value
|
||||
|
||||
table = db.execute(f"<MY SELECT> WHERE scenario_id = {current_scenario}")
|
||||
|
||||
# If no data for this scenario, recursion
|
||||
if len(table) == 0:
|
||||
parent_id = db.get_parent_id(current_scenario)
|
||||
return cls.rec_load(db, parent_id)
|
||||
|
||||
for data in table:
|
||||
if is_dummy(data):
|
||||
continue
|
||||
|
||||
# Otherelse, parse data ...
|
||||
|
||||
# return ...
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
|
||||
And add a data reduction method:
|
||||
|
||||
#+caption: Data reduction method for data subtree
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def reduce_change(self):
|
||||
# Get the greater scenario
|
||||
scenar = reduce(
|
||||
lambda acc, x: max(acc, x._scenario),
|
||||
self._values,
|
||||
self._scenario
|
||||
)
|
||||
|
||||
# Apply change on this subtree
|
||||
self._scenario = scenar
|
||||
|
||||
if is_dummy(self): # There are no more subtree
|
||||
self.values = []
|
||||
else: # Update data subtree scenario id
|
||||
for value in self._values:
|
||||
value.set_scenario(self._scenario)
|
||||
# Dummy data is no more necessary in this subtree
|
||||
self._values = list(
|
||||
filter(
|
||||
lambda v: not is_dummy(v),
|
||||
self._values
|
||||
)
|
||||
)
|
||||
|
||||
# Keep data id unique for all subtree
|
||||
self.get_new_db_id()
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
|
||||
#+latex: \newpage
|
||||
|
||||
* Prototype
|
||||
|
||||
** Scenario
|
||||
|
||||
#+name: scenario
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
class Scenario():
|
||||
def __init__(self, id, name, parent):
|
||||
self._id = id
|
||||
self._name = name
|
||||
self._parent = parent
|
||||
|
||||
@classmethod
|
||||
def create_db(cls, db):
|
||||
execute(
|
||||
db, """
|
||||
CREATE TABLE scenario(
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
parent_id INTEGER REFERENCES scenario(id)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def load_db(cls, db):
|
||||
cur = execute(
|
||||
db, f"SELECT * FROM scenario"
|
||||
)
|
||||
table = cur.fetchall()
|
||||
|
||||
new = []
|
||||
for values in table:
|
||||
new.append(
|
||||
cls(
|
||||
values[0], values[1], values[2],
|
||||
)
|
||||
)
|
||||
|
||||
return new
|
||||
|
||||
def save(self, db):
|
||||
sid = 'NULL' if self._parent is None else self._parent
|
||||
|
||||
execute(
|
||||
db,
|
||||
"INSERT INTO scenario (id, name, parent_id) " +
|
||||
f"VALUES ({self._id}, '{self._name}', {sid})"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Scenario {{ {self._id}, {self._name}, parent {self._parent} }}"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self._name}({self._id})"
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: scenario
|
||||
|
||||
** Data
|
||||
|
||||
#+name: data-b
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
class B():
|
||||
def __init__(self, value, scenario):
|
||||
self._value = value
|
||||
self._scenario = scenario
|
||||
self._dummy = False
|
||||
|
||||
@classmethod
|
||||
def create_db(cls, db):
|
||||
execute(db, """
|
||||
CREATE TABLE b(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
dummy_data BOOLEAN,
|
||||
x INTEGER,
|
||||
a_id INTEGER NOT NULL,
|
||||
scenario_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(a_id) REFERENCES a(id),
|
||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
||||
)
|
||||
""")
|
||||
|
||||
def save(self, db, a_id):
|
||||
execute(
|
||||
db,
|
||||
"INSERT INTO b (x, dummy_data, a_id, scenario_id) " +
|
||||
f"VALUES ({self._value}, {self._dummy}, {a_id}, {self._scenario})"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def load_db(cls, db, a_id, senar):
|
||||
if senar is None:
|
||||
return []
|
||||
|
||||
cur = execute(
|
||||
db,
|
||||
"SELECT x, scenario_id, dummy_data FROM b " +
|
||||
f"WHERE scenario_id = {senar} AND a_id = {a_id}"
|
||||
)
|
||||
table = cur.fetchall()
|
||||
|
||||
if len(table) == 0:
|
||||
parent = execute(
|
||||
db,
|
||||
f"SELECT parent_id FROM scenario WHERE id = {senar}"
|
||||
).fetchone()
|
||||
|
||||
return cls.load_db(db, parent[0])
|
||||
|
||||
new = []
|
||||
for values in table:
|
||||
if values[2]: # Is dummy
|
||||
continue
|
||||
|
||||
new.append(cls(values[0], values[1]))
|
||||
|
||||
return new
|
||||
|
||||
def set_value(self, value, scenario):
|
||||
self._value = value
|
||||
self._scenario = scenario
|
||||
|
||||
def set_dummy(self, scenario):
|
||||
self._scenario = scenario
|
||||
self._dummy = True
|
||||
|
||||
def __repr__(self):
|
||||
if self._dummy:
|
||||
return ""
|
||||
return f"{self._value}"
|
||||
|
||||
def __str__(self):
|
||||
if self._dummy:
|
||||
return ""
|
||||
return f"{self._value}"
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: data-b
|
||||
|
||||
#+name: data-a
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
class A():
|
||||
def __init__(self, id, values, scenario):
|
||||
self._id = id
|
||||
self._values = values
|
||||
self._scenario = scenario
|
||||
self._dummy = False
|
||||
|
||||
@classmethod
|
||||
def create_db(cls, db):
|
||||
execute(db, """
|
||||
CREATE TABLE a(
|
||||
id INTEGER PRIMARY KEY,
|
||||
dummy_data BOOLEAN,
|
||||
scenario_id INTEGER,
|
||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
||||
)
|
||||
""")
|
||||
|
||||
def save(self, db):
|
||||
self.reduce_change()
|
||||
|
||||
execute(db, f"DELETE FROM a WHERE scenario_id = {self._scenario}")
|
||||
execute(
|
||||
db,
|
||||
"INSERT INTO a (id, dummy_data, scenario_id) " +
|
||||
f"VALUES ({self._id}, {self._dummy}, {self._scenario})"
|
||||
)
|
||||
|
||||
execute(
|
||||
db,
|
||||
"DELETE FROM b " +
|
||||
f"WHERE scenario_id = {self._scenario} " +
|
||||
f"AND a_id = {self._id}"
|
||||
)
|
||||
|
||||
for value in self._values:
|
||||
value.save(db, self._id)
|
||||
|
||||
def reduce_change(self):
|
||||
diff_scenar = reduce(
|
||||
lambda acc, x: max(acc, x._scenario),
|
||||
self._values,
|
||||
self._scenario
|
||||
)
|
||||
print(f" ~> Reduce: {self._scenario} to {diff_scenar}")
|
||||
self._scenario = diff_scenar
|
||||
|
||||
# Reduce new scenario value on each value
|
||||
for value in self._values:
|
||||
value._scenario = self._scenario
|
||||
|
||||
# Delete useless dummy data
|
||||
self._values = list(
|
||||
filter(
|
||||
lambda v: not v._dummy,
|
||||
self._values
|
||||
)
|
||||
)
|
||||
|
||||
# HACK: keep id unique
|
||||
self._id = self._id * 100 + diff_scenar
|
||||
|
||||
@classmethod
|
||||
def load_db(cls, db, senar):
|
||||
if senar is None:
|
||||
return []
|
||||
|
||||
cur = execute(
|
||||
db,
|
||||
f"SELECT id, scenario_id, dummy_data FROM a WHERE scenario_id = {senar}"
|
||||
)
|
||||
table = cur.fetchall()
|
||||
|
||||
if len(table) == 0:
|
||||
parent = execute(
|
||||
db,
|
||||
f"SELECT parent_id FROM scenario WHERE id = {senar}"
|
||||
).fetchone()
|
||||
|
||||
return cls.load_db(db, parent[0])
|
||||
|
||||
new = []
|
||||
for values in table:
|
||||
if values[2]:
|
||||
continue
|
||||
|
||||
bb = B.load_db(db, values[0], senar)
|
||||
new.append(cls(values[0], bb, values[1]))
|
||||
|
||||
return new
|
||||
|
||||
def set_dummy(self, scenario):
|
||||
self._scenario = scenario
|
||||
self._dummy = True
|
||||
|
||||
def add(self, b):
|
||||
self._values.append(b)
|
||||
|
||||
def delete(self, index, scenario):
|
||||
self._values[index].set_dummy(scenario)
|
||||
self._scenario = scenario
|
||||
|
||||
def __repr__(self):
|
||||
if self._dummy:
|
||||
return "{ }"
|
||||
return f"{{ {self._values}, ({self._scenario}) }}"
|
||||
|
||||
def __str__(self):
|
||||
if self._dummy:
|
||||
return "{ }"
|
||||
return f"{{ {list(map(str, self._values))}, ({self._scenario}) }}"
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: data-a
|
||||
|
||||
** Full prototype
|
||||
|
||||
#+begin_src python :python python3 :results output :exports both :noweb yes :cache yes
|
||||
from functools import reduce
|
||||
|
||||
def execute(db, query):
|
||||
# print(query)
|
||||
return db.execute(query)
|
||||
|
||||
def get_tree(db):
|
||||
from treelib import Node, Tree
|
||||
|
||||
tree = Tree()
|
||||
|
||||
for senar in db.execute("SELECT * FROM scenario").fetchall():
|
||||
if senar[1] is None:
|
||||
tree.create_node(
|
||||
str(Scenario(senar[0], senar[1], senar[2])) +
|
||||
" : " + str(A.load_db(db, senar[0])[0]),
|
||||
senar[0]
|
||||
)
|
||||
else:
|
||||
tree.create_node(
|
||||
str(Scenario(senar[0], senar[1], senar[2])) +
|
||||
" : " + str(A.load_db(db, senar[0])[0]),
|
||||
senar[0],
|
||||
parent=senar[2]
|
||||
)
|
||||
|
||||
return tree
|
||||
|
||||
### --- Scenario ---
|
||||
<<scenario>>
|
||||
|
||||
### --- class B ---
|
||||
<<data-b>>
|
||||
|
||||
### --- class A ---
|
||||
<<data-a>>
|
||||
|
||||
### --- Script ---
|
||||
import sqlite3
|
||||
import pprint
|
||||
|
||||
pp = pprint.PrettyPrinter(width=79, compact=True, depth=6)
|
||||
|
||||
with sqlite3.connect(":memory:") as db:
|
||||
cur = db.cursor()
|
||||
|
||||
Scenario.create_db(cur)
|
||||
A.create_db(cur)
|
||||
B.create_db(cur)
|
||||
|
||||
print("--- Default scenario (s0) ---")
|
||||
|
||||
# One scenario
|
||||
|
||||
s0 = Scenario(0, "s0", None)
|
||||
a0 = A(
|
||||
1,
|
||||
[
|
||||
B(0, s0._id),
|
||||
B(1, s0._id),
|
||||
B(2, s0._id),
|
||||
],
|
||||
s0._id
|
||||
)
|
||||
|
||||
s0.save(cur)
|
||||
a0.save(cur)
|
||||
|
||||
print("--- New scenario (s1) -- copy without modification ---")
|
||||
|
||||
# New scenario
|
||||
|
||||
s1 = Scenario(1, "s1", s0._id)
|
||||
s1.save(cur)
|
||||
|
||||
print("--- New scenario (s2) -- data modification ---")
|
||||
|
||||
# New scenario 2 with data
|
||||
|
||||
s2 = Scenario(2, "s2", s0._id)
|
||||
s2.save(cur)
|
||||
|
||||
a2 = a0
|
||||
bb = list(map(
|
||||
lambda b: b.set_value(b._value + 1, s2._id),
|
||||
a2._values
|
||||
))
|
||||
|
||||
a2.save(cur)
|
||||
|
||||
print("--- New scenario (s3) -- delete data ---")
|
||||
|
||||
# New scenario 3 with data
|
||||
|
||||
s3 = Scenario(3, "s3", s0._id)
|
||||
s3.save(cur)
|
||||
|
||||
a3 = A.load_db(cur, s3._id)[0]
|
||||
a3._values[0].set_dummy(s3._id)
|
||||
a3.save(cur)
|
||||
|
||||
print("--- New scenario (s4) -- new data ---")
|
||||
|
||||
s4 = Scenario(4, "s4", s3._id)
|
||||
s4.save(cur)
|
||||
|
||||
a4 = A.load_db(cur, s3._id)[0]
|
||||
a4.add(B(666, s4._id))
|
||||
a4.save(cur)
|
||||
|
||||
print("--- Scenario tree ---")
|
||||
|
||||
tree = get_tree(db)
|
||||
print(tree)
|
||||
|
||||
print("--- Modify s0 data ---")
|
||||
|
||||
aa = A.load_db(db, s0._id)[0]
|
||||
bb = list(map(
|
||||
lambda b: b.set_value(b._value * 100 - 1, s0._id),
|
||||
aa._values
|
||||
))
|
||||
aa.save(db)
|
||||
|
||||
print("--- Scenario tree ---")
|
||||
|
||||
tree = get_tree(db)
|
||||
print(tree)
|
||||
|
||||
print("--- DB ---")
|
||||
|
||||
ss = db.execute("SELECT * FROM scenario").fetchall()
|
||||
print(f"+ scenario ({len(ss)}):")
|
||||
pp.pprint(ss)
|
||||
|
||||
aa = db.execute("SELECT * FROM a").fetchall()
|
||||
print(f"+ a ({len(aa)}):")
|
||||
pp.pprint(aa)
|
||||
|
||||
bb = db.execute("SELECT * FROM b").fetchall()
|
||||
print(f"+ b ({len(bb)}):")
|
||||
pp.pprint(bb)
|
||||
|
||||
#+end_src
|
||||
|
||||
#+RESULTS[96c84f23be557425ae2822ef7f174175ef69f9b9]:
|
||||
#+begin_example
|
||||
--- Default scenario (s0) ---
|
||||
~> Reduce: 0 to 0
|
||||
--- New scenario (s1) -- copy without modification ---
|
||||
--- New scenario (s2) -- data modification ---
|
||||
~> Reduce: 0 to 2
|
||||
--- New scenario (s3) -- delete data ---
|
||||
~> Reduce: 0 to 3
|
||||
--- New scenario (s4) -- new data ---
|
||||
~> Reduce: 3 to 4
|
||||
--- Scenario tree ---
|
||||
s0(0) : { ['0', '1', '2'], (0) }
|
||||
├── s1(1) : { ['0', '1', '2'], (0) }
|
||||
├── s2(2) : { ['1', '2', '3'], (2) }
|
||||
└── s3(3) : { ['1', '2'], (3) }
|
||||
└── s4(4) : { ['1', '2', '666'], (4) }
|
||||
|
||||
--- Modify s0 data ---
|
||||
~> Reduce: 0 to 0
|
||||
--- Scenario tree ---
|
||||
s0(0) : { ['-1', '99', '199'], (0) }
|
||||
├── s1(1) : { ['-1', '99', '199'], (0) }
|
||||
├── s2(2) : { ['1', '2', '3'], (2) }
|
||||
└── s3(3) : { ['1', '2'], (3) }
|
||||
└── s4(4) : { ['1', '2', '666'], (4) }
|
||||
|
||||
--- DB ---
|
||||
+ scenario (5):
|
||||
[(0, 's0', None), (1, 's1', 0), (2, 's2', 0), (3, 's3', 0), (4, 's4', 3)]
|
||||
+ a (4):
|
||||
[(10000, 0, 0), (10002, 0, 2), (10003, 0, 3), (1000304, 0, 4)]
|
||||
+ b (14):
|
||||
[(1, 0, 0, 100, 0), (2, 0, 1, 100, 0), (3, 0, 2, 100, 0), (4, 0, 1, 10002, 2),
|
||||
(5, 0, 2, 10002, 2), (6, 0, 3, 10002, 2), (7, 0, 1, 10003, 3),
|
||||
(8, 0, 2, 10003, 3), (9, 0, 1, 1000304, 4), (10, 0, 2, 1000304, 4),
|
||||
(11, 0, 666, 1000304, 4), (12, 0, -1, 10000, 0), (13, 0, 99, 10000, 0),
|
||||
(14, 0, 199, 10000, 0)]
|
||||
#+end_example
|
||||
|
|
@ -0,0 +1,525 @@
|
|||
# presentation.org -- Pamhyr
|
||||
# Copyright (C) 2023 INRAE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#+STARTUP: indent hideblocks content beamer
|
||||
|
||||
#+TITLE: \textbf{Pamhyr2}
|
||||
#+SUBTITLE: Scenarios managements
|
||||
|
||||
# #+AUTHOR: Pierre-Antoine Rouby
|
||||
#+LATEX_HEADER: \author[Pierre-Antoine Rouby]{\textbf{Pierre-Antoine Rouby}}
|
||||
#+EMAIL: pierre-antoine.rouby@inrae.fr
|
||||
#+INSTITUTE: INRAE Lyon-Villerbanne, teams River Hydraulics
|
||||
#+LATEX_HEADER: \institute{INRAE Lyon-Villerbanne, River Hydraulics team}
|
||||
#+DATE: 9th November
|
||||
|
||||
#+KEYWORDS: g ui, hydraulics, sedimentary, hydro-sedimentary, 1D modelling, freesoftware, foss
|
||||
|
||||
#+OPTIONS: H:2 toc:t num:t author:nil
|
||||
#+LATEX_CLASS: beamer
|
||||
#+LATEX_CLASS_OPTIONS: [slideopt,A4,showboxes,svgnames,aspectratio=169]
|
||||
# #+LATEX_CLASS_OPTIONS: [presentation]
|
||||
#+BEAMER_THEME: default
|
||||
#+COLUMNS: %45ITEM %10BEAMER_ENV(Env) %10BEAMER_ACT(Act) %4BEAMER_COL(Col)
|
||||
|
||||
#+LANGUAGE: fr
|
||||
#+LATEX_HEADER: \usepackage[frenchb]{babel}
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :results silent :exports none
|
||||
(setq org-latex-pdf-process (list
|
||||
"latexmk -pdflatex='lualatex -shell-escape -interaction nonstopmode' -pdf -f %f"))
|
||||
|
||||
(add-to-list 'org-latex-packages-alist '("" "minted"))
|
||||
(setq org-latex-listings 'minted)
|
||||
(setq org-src-fontify-natively t)
|
||||
|
||||
(org-add-link-type
|
||||
"cite" 'ebib
|
||||
(lambda (path desc format)
|
||||
(cond
|
||||
((eq format 'html)
|
||||
(format "(<cite>%s</cite>)" path))
|
||||
((eq format 'text)
|
||||
(format "[%s]" path))
|
||||
((eq format 'latex)
|
||||
(if (or (not desc) (equal 0 (search "cite:" desc)))
|
||||
(format "\\cite{%s}" path)
|
||||
(format "\\cite[%s][%s]{%s}"
|
||||
(cadr (split-string desc ";"))
|
||||
(car (split-string desc ";")) path))))))
|
||||
#+END_SRC
|
||||
|
||||
# Wrapper
|
||||
#+NAME: attr_wrap
|
||||
#+HEADER: :var width="\\textwidth"
|
||||
#+HEADER: :var caption=""
|
||||
#+HEADER: :var smallcaption=""
|
||||
#+HEADER: :var name=""
|
||||
#+HEADER: :var data=""
|
||||
#+HEADER: :var float="nil"
|
||||
#+BEGIN_SRC sh :results output :exports none
|
||||
echo "#+CAPTION[$smallcaption]: $caption"
|
||||
echo "#+NAME: $name"
|
||||
echo "#+ATTR_LATEX: :width $width :float $float"
|
||||
echo "$data"
|
||||
#+END_SRC
|
||||
|
||||
#+LATEX_HEADER: \setbeamertemplate{footline}[frame number]
|
||||
#+LATEX_HEADER: \setbeamertemplate{headline}{}
|
||||
#+LATEX_HEADER: \usepackage{multirow}
|
||||
#+LATEX_HEADER: \usepackage{fontawesome5}
|
||||
#+LATEX_HEADER: \usepackage{tcolorbox}
|
||||
#+LATEX_HEADER: \usepackage{tikz}
|
||||
|
||||
#+LATEX_HEADER: \logo{\includegraphics[width=.1\textwidth]{../../../src/View/ui/ressources/Logo-INRAE.png}}
|
||||
|
||||
#+LATEX_HEADER: \newcommand{\R}[1]{\rotatebox[origin=c]{90}{#1}}
|
||||
|
||||
# Beamer colors
|
||||
#+LATEX_HEADER: \setbeamersize{text margin left=0.5cm,text margin right=0.5cm}
|
||||
#+LATEX_HEADER: \setbeamerfont{alerted text}{series=\bfseries}
|
||||
#+LATEX_HEADER: \setbeamerfont{example text}{series=\bfseries}
|
||||
|
||||
#+LATEX_HEADER: \definecolor{inrae_1}{RGB}{102,193,191}
|
||||
#+LATEX_HEADER: \definecolor{inrae_2}{RGB}{0,150,152}
|
||||
#+LATEX_HEADER: \definecolor{inrae_3}{RGB}{39,86,98}
|
||||
|
||||
#+LATEX_HEADER: \setbeamercolor{alerted text}{fg=inrae_1}
|
||||
#+LATEX_HEADER: \setbeamercolor{structure}{fg=inrae_2}
|
||||
#+LATEX_HEADER: \setbeamercolor{normal text}{fg=inrae_3}
|
||||
#+LATEX_HEADER: \setbeamercolor*{author}{fg=inrae_3}
|
||||
|
||||
#+LATEX_HEADER: \usepackage[absolute,showboxes,overlay]{textpos}
|
||||
|
||||
#+LATEX_HEADER: \TPshowboxesfalse
|
||||
#+LATEX_HEADER: \textblockorigin{5mm}{0mm}
|
||||
|
||||
# Biblio
|
||||
#+LATEX_HEADER: \usepackage{natbib}
|
||||
# #+LATEX_HEADER: \bibliographystyle{apalike}
|
||||
#+LATEX_HEADER: \setbeamertemplate{bibliography item}{}
|
||||
#+LATEX_HEADER: \renewcommand\bibfont{\scriptsize}
|
||||
#+LATEX_HEADER: \setbeamertemplate{frametitle continuation}[from second]
|
||||
#+LATEX_HEADER: \setbeamercolor*{bibliography entry title}{fg=inrae_3}
|
||||
#+LATEX_HEADER: \setbeamercolor*{bibliography entry author}{fg=inrae_3}
|
||||
#+LATEX_HEADER: \setbeamercolor*{bibliography entry location}{fg=inrae_3}
|
||||
|
||||
#+LATEX_HEADER: \usepackage{bbding}
|
||||
#+LATEX_HEADER: \usepackage{pdfpages}
|
||||
|
||||
# Renew title page
|
||||
#+LATEX_HEADER: \defbeamertemplate*{title page}{customized}[1][]
|
||||
#+LATEX_HEADER: {
|
||||
# #+LATEX_HEADER: {
|
||||
# #+LATEX_HEADER: \usebeamerfont{date}\usebeamercolor[fg]{date}
|
||||
# #+LATEX_HEADER: \tikz[overlay,remember picture]
|
||||
# #+LATEX_HEADER: \node[xshift=-1cm,yshift=-1cm,text width=10cm] at (current page.north east) {
|
||||
# #+LATEX_HEADER: 7$^{th}$ International Conference\\
|
||||
# #+LATEX_HEADER: 8TH-10TH November\\
|
||||
# #+LATEX_HEADER: EDF Lab - CHATOU, FRANCE
|
||||
# #+LATEX_HEADER: }
|
||||
# #+LATEX_HEADER: }
|
||||
#+LATEX_HEADER: {
|
||||
#+LATEX_HEADER: \usebeamerfont{title}\inserttitle\par
|
||||
#+LATEX_HEADER: \usebeamerfont{title}\usebeamercolor[fg]{title}
|
||||
#+LATEX_HEADER: }
|
||||
#+LATEX_HEADER: \bigskip
|
||||
#+LATEX_HEADER: \begin{center}
|
||||
#+LATEX_HEADER: \usebeamerfont{subtitle}\usebeamercolor[fg]{subtitle}
|
||||
#+LATEX_HEADER: {
|
||||
#+LATEX_HEADER: \LARGE\insertsubtitle\par
|
||||
#+LATEX_HEADER: }
|
||||
#+LATEX_HEADER: \bigskip
|
||||
#+LATEX_HEADER: \usebeamerfont{author}\usebeamercolor[fg]{author}\insertauthor\par
|
||||
#+LATEX_HEADER: \bigskip
|
||||
#+LATEX_HEADER: \usebeamerfont{institute}\usebeamercolor[fg]{institute}\insertinstitute\par
|
||||
#+LATEX_HEADER: \bigskip
|
||||
#+LATEX_HEADER: \usebeamerfont{date}\usebeamercolor[fg]{date}\insertdate\par
|
||||
#+LATEX_HEADER: \usebeamercolor[fg]{titlegraphic}\inserttitlegraphic
|
||||
#+LATEX_HEADER: \end{center}
|
||||
#+LATEX_HEADER: }
|
||||
|
||||
# #+LATEX_HEADER: \newcommand{\tred}[1]{\textcolor{rouge_inrae}{#1}}
|
||||
|
||||
#+TODO: TODO WIP TOREVIEW | DONE
|
||||
|
||||
#+MACRO: pamhyr \textsc{PAMHyR}
|
||||
#+MACRO: pamhyr2 \textsc{Pamhyr2}
|
||||
|
||||
#+MACRO: pause \pause
|
||||
#+MACRO: framebreak \framebreak
|
||||
#+MACRO: no
|
||||
# #+MACRO: OK \checkmark
|
||||
#+MACRO: OK \faIcon{check}
|
||||
#+MACRO: bf \textbf{$1}
|
||||
|
||||
# #+latex: \frame{\tocpage}
|
||||
|
||||
* Problématique
|
||||
|
||||
** Problématique
|
||||
|
||||
*** Utilisateur
|
||||
|
||||
- Définire une version modifier de l'étude (changement dans les
|
||||
données)
|
||||
- Comparé des résultats entre une variante et l'étude
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
**** COMMENT Développeur
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
|
||||
- Un unique fichier
|
||||
- Limité la duplication de donnée à sauvegarder
|
||||
- Garder une bonne cohérence entre données et résultats
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
*** Scénarios
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 1
|
||||
:BEAMER_ENV: definition
|
||||
:END:
|
||||
|
||||
Un scénario est une variate d'une étude.
|
||||
|
||||
** Problématique -- Avancer
|
||||
|
||||
*** Utilisateur
|
||||
|
||||
- Définire une version modifier d'un senarios
|
||||
- Comparé des résultats entre plusieurs senarios
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
**** COMMENT Développeur
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
|
||||
- Un unique fichier
|
||||
- Limité la duplication de donnée à sauvegarder
|
||||
- Garder une bonne cohérence entre données et résultats
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
*** Scénarios
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 1
|
||||
:BEAMER_ENV: definition
|
||||
:END:
|
||||
|
||||
Un scénario peut être l'étude ou une variate d'un autre scénario.
|
||||
|
||||
** Problématique -- Encore plus avancer
|
||||
|
||||
*** Utilisateur
|
||||
|
||||
- Définire un ensemble fini de variante correspondant a un ensemble de
|
||||
valeur pour un(des) paramètre(s) et executer un solveur sur
|
||||
l'ensemble des variantes
|
||||
- Pouvoir comparé des metrics sur un ensemble de résultats trop grand
|
||||
pour une comparaisont à la mains
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
**** COMMENT Développeur
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
|
||||
- Un unique fichier
|
||||
- Limité la duplication de donnée à sauvegarder
|
||||
- Garder une bonne cohérence entre données et résultats
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
*** Scénarios
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 1
|
||||
:BEAMER_ENV: definition
|
||||
:END:
|
||||
|
||||
Un scénario peut être l'étude, une variate d'un scénarios ou un
|
||||
ensemble fini de variantes d'un scénarios.
|
||||
|
||||
** Problématique -- Encore plus plus avancer
|
||||
|
||||
*** Utilisateur
|
||||
|
||||
- Définire un ensemble fini de variante correspondant a un ensemble de
|
||||
valeur pour un(des) paramètre(s) et faire un nombre fini de tirage
|
||||
aléatoire dans ces paramètre pour executer un solveur ces paramètres
|
||||
- Pouvoir comparé des metrics sur un ensemble de résultats trop grand
|
||||
pour une comparaisont à la mains
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
*** Scénarios
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 1
|
||||
:BEAMER_ENV: definition
|
||||
:END:
|
||||
|
||||
Un scénario peut être l'étude, une variate d'un scénarios ou un
|
||||
ensemble fini ou un tirage aléatoire dans un ensemble de variantes
|
||||
d'un scénarios.
|
||||
|
||||
** Problématique -- Développeur
|
||||
|
||||
*** Développeur
|
||||
|
||||
- Permetre a l'utilisateur de *garder une bonne cohérence entre
|
||||
données et résultats* de façon claire
|
||||
- Permetre a l'utilisateur de *géré les scénarios* de façon claire
|
||||
(représentation graphique)
|
||||
- Garder *un unique fichier* (=.pamhyr=)
|
||||
- *Limité la duplication* de donnée à sauvegarder
|
||||
- Permetre à l'utilisateur de garder les *résultats pertinant* sans
|
||||
exploser la mémoire de l'ordinateur
|
||||
|
||||
* Proposition
|
||||
|
||||
** Proposition -- Concept
|
||||
|
||||
*** {{{no}}}
|
||||
\vspace{-1.2cm}
|
||||
**** Deux type de scénarios
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
|
||||
- Scénarios simple
|
||||
- Scénarios d'éxecution d'ensemble
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
**** Scénarios simple
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
|
||||
Est composer:
|
||||
- d'un identifiant unique
|
||||
- d'un nom (définie par l'utilisateur)
|
||||
- d'une description (définie par l'utilisateur)
|
||||
- d'un parent (définie par l'utilisateur)
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
\vspace{-0.5cm}
|
||||
|
||||
*** Scénarios d'ensembles
|
||||
|
||||
**** {{{no}}}
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
\vspace{-1cm}
|
||||
Est composer:
|
||||
- d'un identifiant unique
|
||||
- d'un nom (définie par l'utilisateur)
|
||||
- d'une description (définie par l'utilisateur)
|
||||
- d'un parent (définie par l'utilisateur)
|
||||
- d'un ou plusieurs ensemble de valeur
|
||||
|
||||
**** {{{no}}}
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
\vspace{-0.5cm}
|
||||
Un ensemble de valeurs est definie par:
|
||||
- une valeurs de départ
|
||||
- une valeurs de fin
|
||||
- un pas de valeur
|
||||
- un élément d'origine (élément définie dans l'étude)
|
||||
- une fonctions (modification de l'élément à l'aide d'une valeur de
|
||||
l'ensemble)
|
||||
|
||||
** Proposition -- Limitation
|
||||
|
||||
*** Scénarios simple
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
|
||||
Si le scénarios est le *parent d'un autre scénarios*, il ne *peut plus
|
||||
être modifier* sans risquer des comportement difficilement
|
||||
comprehensible pour l'utilisateur.
|
||||
|
||||
{{{pause}}}
|
||||
|
||||
*** Scénarios d'ensembles
|
||||
:PROPERTIES:
|
||||
:BEAMER_COL: 0.5
|
||||
:BEAMER_ENV: block
|
||||
:END:
|
||||
|
||||
- Un scénarios d'ensemble *ne peut pas être parent* d'un autre
|
||||
scénarios
|
||||
- Le nombre d'exécution peut très rapidement grossir
|
||||
|
||||
** Proposition -- Affichage
|
||||
|
||||
#+name: graph-scenarios
|
||||
#+header: :results drawer
|
||||
#+header: :exports results
|
||||
#+header: :post attr_wrap(width="7cm", data=*this*, name="graph-scenarios", caption="Exemple of scenarios graph", float="t")
|
||||
#+begin_src dot :file "images/graph-scenarios.png" :cache no
|
||||
digraph {
|
||||
bgcolor="transparent";
|
||||
node[colorscheme=pastel19,shape=ellipse,style="filled",fillcolor=9];
|
||||
|
||||
b[fillcolor=2];
|
||||
|
||||
aa[shape=box,fillcolor=2];
|
||||
aa2[label="aa'", shape=box, fillcolor=2];
|
||||
ad[shape=box, fillcolor=2];
|
||||
re[shape=box, fillcolor=2];
|
||||
rf[shape=box, fillcolor=2];
|
||||
|
||||
default -> a;
|
||||
default -> b;
|
||||
default -> c;
|
||||
|
||||
a -> aa;
|
||||
a -> aa2;
|
||||
a -> f -> rf;
|
||||
c -> d -> ad;
|
||||
c -> e -> re;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
#+RESULTS[80ed2484b9cc71143f800ff23c10846fd04697c3]: graph-scenarios
|
||||
:results:
|
||||
#+CAPTION[]: Exemple of scenarios graph
|
||||
#+NAME: graph-scenarios
|
||||
#+ATTR_LATEX: :width 7cm :float t
|
||||
[[file:images/graph-scenarios.png]]
|
||||
:end:
|
||||
|
||||
* Solution technique
|
||||
|
||||
** Technique -- scénarios
|
||||
|
||||
#+begin_src sql
|
||||
CREATE TABLE scenario(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL, -- Name of the scenario
|
||||
description TEXT NOT NULL, -- Rich text description
|
||||
parent_id INTEGER REFERENCES scenario(id) -- Recursive references
|
||||
-- for parent scenario
|
||||
)
|
||||
#+end_src
|
||||
|
||||
** Technique -- Données -- Enregistrement
|
||||
|
||||
#+begin_src sql
|
||||
-- Unique ID (Must be unused, to delete)
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
-- New ID (not unique)
|
||||
pamhyr_id INTEGER NOT NULL,
|
||||
-- (Optional) Flag for set there is no data in this scenario and
|
||||
-- ignore parent scenario
|
||||
dummy BOOLEAN NOT NULL,
|
||||
-- Corresponding scenario
|
||||
scenario_id INTEGER NOT NULL,
|
||||
PRIMARY KEY(pamhyr_id, scenario_id),
|
||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id),
|
||||
#+end_src
|
||||
|
||||
** Technique -- Données -- Chargement
|
||||
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def load(cls, db, current_scenario):
|
||||
# Default case, end of recursion
|
||||
if current_scenario is None:
|
||||
return [] # Default value
|
||||
|
||||
table = db.execute(f"<MY SELECT> WHERE scenario_id = {current_scenario}")
|
||||
|
||||
# If no data for this scenario, recursion
|
||||
if len(table) == 0:
|
||||
parent_id = db.get_parent_id(current_scenario)
|
||||
return cls.load(db, parent_id)
|
||||
|
||||
# Otherelse, parse data and return...
|
||||
#+end_src
|
||||
|
||||
** Technique -- Données -- Modification
|
||||
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def set_modified(self, current_scenario):
|
||||
self.scenario = current_scenario
|
||||
#+end_src
|
||||
|
||||
#+begin_src python :python python3 :results output :noweb yes
|
||||
def delete(self, current_scenario):
|
||||
self.deleted = True
|
||||
self.set_modified(current_scenario)
|
||||
#+end_src
|
||||
|
||||
** Technique -- Exécution d'ensemble
|
||||
|
||||
#+begin_src sql
|
||||
CREATE TABLE range_set(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
start REAL NOT NULL, -- Range start
|
||||
end REAL NOT NULL, -- Range end
|
||||
step REAL NOT NULL, -- Range step
|
||||
generator_name TEXT NOT NULL, -- Generator function name (ex:
|
||||
-- 'minor_strickler')
|
||||
original_data_id INTEGER, -- (optional) Ref to data who apply
|
||||
-- this range (ex: a strickler id)
|
||||
scenario_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
||||
)
|
||||
#+end_src
|
||||
|
||||
* Sauvegarde des résultats
|
||||
|
||||
** Sauvegarde des résultats
|
||||
|
||||
*** Scénarios simple
|
||||
|
||||
Sauvegarde d'un seul résultats complet par scénarios (et par solver).
|
||||
|
||||
*** Scénarios d'ensemble
|
||||
|
||||
Sauvegarde de metrics récupéré sur un ensemble de résultats (pas de
|
||||
sauvegarde de tous les résultats) ou certain résultats particuliers.
|
||||
|
||||
* Question & remarque
|
||||
|
||||
** Question & remarque
|
||||
|
||||
- Solution satisfaisante ?
|
||||
- Cas d'usage intéressent non couvert ?
|
||||
- Contre exemple ?
|
||||
- Meilleur solution ?
|
||||
Loading…
Reference in New Issue