Compare commits

...

3 Commits

4 changed files with 243 additions and 2 deletions

View File

@ -16,7 +16,6 @@
# -*- coding: utf-8 -*-
from copy import copy
from tools import trace, timer
from Model.Tools.PamhyrListExt import PamhyrModelList

View File

@ -0,0 +1,176 @@
# Function.py -- Pamhyr
# Copyright (C) 2023-2026 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 -*-
import struct
import logging
import itertools
from tools import logger_exception
from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Scenario import Scenario
logger = logging.getLogger()
class Function(SQLSubModel):
_sub_classes = []
def __init__(self, id: int = -1,
name: str = "", script: str = "",
status=None):
super(Function, self).__init__(
id=id, status=status,
owner_scenario=0
)
self._name = name
self._type = "generic"
self._script = script
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE ensemble_function{ext} (
{cls.create_db_add_pamhyr_id()},
deleted BOOLEAN NOT NULL DEFAULT FALSE,
name TEXT NOT NULL,
script TEXT NOT NULL,
PRIMARY KEY(pamhyr_id)
)
""")
if ext == "_tmp":
return True
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) < 2:
cls._db_create(execute)
elif major == "0" and int(minor) == 2:
if int(release) < 8:
cls._db_create(execute)
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
new = []
scenario = data["scenario"]
loaded = data['loaded_pid']
table = execute(
"SELECT pamhyr_id, deleted, name, script " +
"FROM ensemble_function " +
f"WHERE pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
)
for row in table:
it = iter(row)
id = next(it)
deleted = (next(it) == 1)
name = next(it)
script = next(it)
new_function = cls(
id, name=name, script=script,
status=data["status"]
)
if deleted:
new_function.set_as_deleted()
loaded.add(id)
new.append(new_function)
return new
def _db_save(self, execute, data=None):
# Only save custom function
if self._type != "custom":
return True
if not self.must_be_saved():
return True
execute(
"INSERT INTO " +
"ensemble_function(pamhyr_id, deleted, " +
" name, script) " +
"VALUES (?, ?, ?, ?)",
self.pamhyr_id, self.is_deleted(),
self._name, self._script
)
return True
def _data_traversal(self,
predicate=lambda obj, data: True,
modifier=lambda obj, data: None,
data={}):
if predicate(self, data):
modifier(self, data)
def get_sample(self, r):
raise NotImplementedMethodeError(cls, cls._load)
###########################
# FUNCTION IMPLEMENTATION #
###########################
class Uniform(Function):
_sub_classes = []
def __init__(self, id: int = -1,
name: str = "uniform", script: str = "",
status=None):
super(Uniform, self).__init__(
id=id, status=status,
name=name, script=script
)
def get_sample(self, dist, ens_range):
return range(ens_range[0], ens_range[1])
class Custom(Function):
_sub_classes = []
def __init__(self, id: int = -1,
name: str = "", script: str = "",
status=None):
super(Custom, self).__init__(
id=id, status=status,
name=name, script=script
)
self._type = "custom"
def get_sample(self, dist, ens_range):
try:
# Run script to (re)define the sample function
f = eval(self._script)
return f(dist, ens_range)
except Exception as e:
logger_exception(e)
return None # TODO: Raise helpful exception

View File

@ -0,0 +1,53 @@
# FunctionList.py -- Pamhyr
# Copyright (C) 2023-2026 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 -*-
from tools import trace, timer
from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.Ensembles.Function import *
class FunctionList(PamhyrModelList):
_sub_classes = [Function]
@classmethod
def _db_load(cls, execute, data=None):
new = cls(status=data['status'])
new._lst = [Uniform(status=data['status'])]
new._lst += Function._db_load(
execute, data
)
return new
def _db_save(self, execute, data={}):
execute(
"DELETE FROM ensemble_function"
)
for fun in self._lst:
fun._db_save(execute, data)
return True
def new(self, index):
r = Custom(status=self._status)
self._lst.insert(index, r)
self._status.modified()
return r

View File

@ -33,6 +33,7 @@ from Model.Status import StudyStatus
from Model.Except import NotImplementedMethodeError
from Model.River import River
from Model.Geometry.Reach import Reach
from Model.Ensembles.FunctionList import FunctionList
from Model.HydraulicStructures.HydraulicStructures import (
HydraulicStructure
)
@ -50,6 +51,7 @@ class Study(SQLModel):
_sub_classes = [
Scenario,
FunctionList,
River,
]
@ -84,6 +86,9 @@ class Study(SQLModel):
self.scenarios = Scenarios(status=self.status)
self.scenarios[0] = s0
self.status.scenario = s0
self._ens_function = FunctionList(status=self.status)
self._river = River(status=self.status)
else:
self._init_db_file(filename, is_new=False)
@ -394,6 +399,11 @@ class Study(SQLModel):
data["scenario"] = scenario
data["loaded_pid"] = set()
# Get ensemble function
new._ens_function = FunctionList._db_load(
sql_exec, data=data
)
# Load river data
new._river = River._db_load(
sql_exec, data=data
@ -455,7 +465,10 @@ class Study(SQLModel):
progress()
self._save_submodel(
[self.scenarios, self._river],
[
self.scenarios, self._ens_function,
self._river
],
data=progress
)