# SolverParametersList.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 . # -*- coding: utf-8 -*- from copy import copy from tools import trace, timer from Solver.Solvers import solver_type_list from Model.Scenario import Scenario from Model.Tools.PamhyrDB import SQLSubModel from Model.Tools.PamhyrList import PamhyrModelList class Parameter(SQLSubModel): def __init__(self, id: int = -1, name="", value="", status=None, owner_scenario=-1): super(Parameter, self).__init__( id=id, status=status, owner_scenario=owner_scenario ) self._name = name self._value = value @classmethod def _db_create(cls, execute, ext=""): execute(f""" CREATE TABLE solver_parameter{ext} ( pamhyr_id INTEGER NOT NULL, ind INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL, solver TEXT NOT NULL, {Scenario.create_db_add_scenario()}, {Scenario.create_db_add_scenario_fk()}, PRIMARY KEY(pamhyr_id, scenario) ) """) return cls._create_submodel(execute) @classmethod def _db_update(cls, execute, version, data=None): major, minor, release = version.strip().split(".") if major == minor == "0": release = int(release) if release < 3: execute( "UPDATE solver_parameter SET name='mage_implicitation' " + "WHERE name='mage_implication'" ) execute( "UPDATE solver_parameter SET name='mage_iteration_type' " + "WHERE name='mage_iter_type'" ) def insert(name, value, ind): return execute( "INSERT INTO " + "solver_parameter(ind, name, value, solver) " + "VALUES (" + f"{ind}, " + f"'{cls._db_format(name)}', " + f"'{cls._db_format(value)}', " + f"'{cls._db_format(solver)}'" + ")" ) if release < 4: cls._db_update_to_0_0_4(execute, insert) if release < 7: cls._db_update_to_0_0_7(execute, insert) if release < 14: cls._db_update_to_0_0_14(execute, data) return cls._update_submodel(execute, version, data) @classmethod def _db_update_to_0_0_4(cls, execute, insert): solvers = execute( "SELECT DISTINCT solver FROM solver_parameter" ) for solver in solvers: if solver != "mage8": continue data = execute("SELECT ind, name, value " + "FROM solver_parameter " + f"WHERE solver = '{solver}' " + "ORDER BY ind ACS") execute( "DELETE FROM solver_parameter " + f"WHERE solver = '{solver}'" ) ind = 0 lst = [] for v in data: insert(v[0], v[1], ind) ind += 1 @classmethod def _db_update_to_0_0_7(cls, execute, insert): solvers = execute( "SELECT DISTINCT solver FROM solver_parameter" ) for solver in solvers: if solver != "mage8": continue insert("mage_init_internal", "N", ind) ind += 1 new = [ ("mage_sediment_masse_volumique", "2650.0"), ("mage_sediment_angle_repos", "40.0"), ("mage_sediment_porosity", "0.40"), ("mage_distance_han", "0.0"), ("mage_distance_chargement_d50", "100.0"), ("mage_distance_chargement_sigma", "100.0"), ("mage_methode_modification_geometrie", "1"), ("mage_shields_critique", "1"), ("mage_shields_correction", "1"), ("mage_capacite_solide", "1"), ("mage_pas_de_temps_charriage", "1"), ("mage_facteur_multiplicateur", "1.0"), ] for v in new: insert(v[0], v[1], ind) ind += 1 @classmethod def _db_update_to_0_0_14(cls, execute, data): table = "solver_parameter" cls.update_db_add_pamhyr_id(execute, table, data) Scenario.update_db_add_scenario(execute, table) cls._db_create(execute, ext="_tmp") execute( f"INSERT INTO {table}_tmp " + "(pamhyr_id, ind, name, value, solver, scenario) " + "SELECT pamhyr_id, ind, name, value, solver, scenario " + f"FROM {table}" ) execute(f"DROP TABLE {table}") execute(f"ALTER TABLE {table}_tmp RENAME TO {table}") @classmethod def _db_load(cls, execute, data=None): status = data["status"] scenario = data["scenario"] loaded = data['loaded_pid'] solver = data['solver'] new = [] if scenario is None: return new table = execute( "SELECT pamhyr_id, ind, name, value, scenario " + "FROM solver_parameter " + f"WHERE solver = '{solver}' " + f"AND scenario = {scenario.id} " + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})" ) for row in table: it = iter(row) pid = next(it) ind = next(it) name = next(it) value = next(it) owner = next(it) new.append( ( ind, cls( id=pid, name=name, value=value, status=status, owner_scenario=owner ) ) ) loaded.add(pid) data["scenario"] = scenario.parent new += cls._db_load(execute, data) data["scenario"] = scenario return new def _db_save(self, execute, data=None): ind = data["ind"] solver = data["solver"] execute( "INSERT INTO " + "solver_parameter(pamhyr_id, ind, " + "name, value, solver, scenario) " + "VALUES (" + f"{self.pamhyr_id}, " + f"{ind}, " + f"'{self._db_format(self.name)}', " + f"'{self._db_format(self.value)}', " + f"'{self._db_format(solver)}', " + f"{self._status.scenario_id}" + ")" ) return True @property def name(self): return self._name @property def value(self): return self._value def __getitem__(self, key): if key == "name": return self._name elif key == "value": return self._value return None def __setitem__(self, key, value): if key == "name": self._name = str(value) elif key == "value": self._value = str(value) self.modified() @classmethod def from_tuple(cls, data, status): new = cls(status=status) new["name"] = data[0] new["value"] = data[1] return new class SolverParametersList(PamhyrModelList): _sub_classes = [Parameter] def __init__(self, solver_type=None, status=None): super(SolverParametersList, self).__init__() self._status = status self._solver = solver_type self._lst = list( map( lambda t: Parameter.from_tuple(t, self._status), solver_type.default_parameters() ) ) @classmethod def _db_create(cls, execute, ext=""): return cls._create_submodel(execute) @classmethod def _db_load(cls, execute, data=None): status = data["status"] scenario = data["scenario"] loaded = data['loaded_pid'] solvers = execute("SELECT DISTINCT solver FROM solver_parameter") new = {} if scenario is None: return new for solver in solvers: solver = solver[0] if solver not in solver_type_list: continue st = solver_type_list[solver] n = cls(solver_type=st, status=status) data['solver'] = solver lst = Parameter._db_load(execute, data) n._lst = list(map(lambda t: t[1], sorted(lst))) new[solver] = n return new def _db_save(self, execute, data=None): t = self._solver._type execute( "DELETE FROM solver_parameter " + f"WHERE solver = '{t}' " + f"AND scenario = {self._status.scenario_id}" ) data["solver"] = t ind = 0 for param in self._lst: data["ind"] = ind param._db_save(execute, data) ind += 1 return True @property def parameters(self): return self.lst def get(self, index): return self._lst[index] def get_by_key(self, key): try: return next( filter( lambda p: p["name"] == key, self._lst ) )["value"] except Exception: return None def set(self, index, new): self._lst[index] = new new.modified() def set_value(self, pid, key, value, owner): for p in self._lst: if p["name"] == key: p["value"] = value self._status.modified() return self._lst.append( Parameter( id=-1, name=key, value=value, status=self._status, owner_scenario=self._status.scenario_id ) ) self._status.modified() def new(self, index): n = Parameter(status=self._status) self._lst.insert(index, n) self._status.modified() return n