Source code for pycomus.ComusDis.CmsPars

# --------------------------------------------------------------
# CmsPars.py
# Version: 1.0.0
# Author: Zhenjiang Wu
# Description: Set COMUS Model Control Parameter Attributes.
# --------------------------------------------------------------
import ctypes
import os
import platform
import sys
from typing import List

from pycomus.Utils.CONSTANTS import CON_PKG_NAME, CON_FILE_NAME, BCF_LYR_PKG_NAME, LPF_LYR_PKG_NAME


[docs] class ComusConPars: """ Set COMUS Model Control Params Attributes. Attributes: -------- model: COMUS Model Object dim_unit: str The unit of spatial measurement (Length) time_unit: str The unit of spatial measurement (Time) sim_mtd: int The simulation method option. 1 for the ACC method; 2 for the original MODFLOW method. sim_type: int The simulation type option. 1 for steady-flow simulation; 2 for transient-flow simulation. acc_lambda: float It is the resistance coefficient in the additional term on the right side of the grid cell differential equation. intblkm: int Option for the input format of layer type and grid cell data. 1 for BCF format; 2 for LPF format. solve: int The option for the method of solving the matrix equation (1 for SIP; 2 for PCG). max_iter: int The maximum number of iterations for matrix solving. damp: float Iterative calculation damping factor (-), usually set to 1.0 (valid range: 0.0001~1.0). h_close: float The accuracy threshold for water level calculation (L). r_close: float Valid only when solve=2 (Preconditioned Conjugate Gradient Method). relax: int Option to enable the deep relaxation iterative algorithm. theta: float It is the reduction coefficient for the dynamic relaxation factor when oscillations occur during iterative calculations (-). gamma: float It is the increase coefficient for the dynamic relaxation factor. akappa: float It is the unit increase value for the dynamic relaxation factor. n_iter: int It is the number of consecutive non-oscillatory iterations required to increase the dynamic relaxation factor. hno_flo: float The water head value for invalid computational cells (L). ch_flg: int Option to calculate the flow between two adjacent fixed head cells. wd_flg: int An option indicating whether to simulate the conversion between dry and wet cells. wet_fct: float A multiplier for the trial thickness of the aquifer layer when a cell is reWetted. newt_iter: int The number of iterations between attempts to convert a cell from dry to wet. hd_wet: int An option for the algorithm to calculate the trial aquifer thickness when a cell is reWetted. reg_sta: int An option to enable the functionality for sub-regional water balance statistics. mul_td: int An option to enable multi-threaded parallel computation. num_td: int This parameter specifies the number of threads to use for parallel computation. Methods: -------- __init__(self, model, dim_unit: str = "m", time_unit: str = "day", sim_mtd: int = 1, sim_type: int = 2, acc_lambda: float = -1, intblkm: int = 1, solve: int = 2, max_iter: int = 200, damp: float = 1, h_close: float = 0.0001, r_close: float = 0.001, relax: int = 0, theta: float = 0.7, gamma: float = 3, akappa: float = 0.001, n_iter: int = 5, hno_flo: float = -1E+30, ch_flg: int = 0, wd_flg: int = 0, wet_fct: float = 0.1, newt_iter: int = 1, hd_wet: int = 1, reg_sta: int = 0, mul_td: int = 0, num_td: int = -1) Set COMUS Model Control Params Attributes. load(cls, model, ctrl_params_file: str) Load parameters from a CtrlPar.in file and create a ComusConPars instance. write_file(self, folder_path: str) Typically used as an internal function but can also be called directly, it outputs the `pycomus.ComusConPars` module to the specified path as <CtrlPar.in>. Returns: -------- controlParams: pycomus.ComusConPars COMUS Control Params Object. Example: -------- >>> import pycomus >>> model1 = pycomus.ComusModel(model_name="test") >>> controlParams = pycomus.ComusConPars(model=model1, sim_type=1, max_iter=10000) """ def __init__(self, model, dim_unit: str = "m", time_unit: str = "day", sim_mtd: int = 1, sim_type: int = 2, acc_lambda: float = -1, intblkm: int = 1, solve: int = 2, max_iter: int = 200, damp: float = 1, h_close: float = 0.0001, r_close: float = 0.001, relax: int = 0, theta: float = 0.7, gamma: float = 3, akappa: float = 0.001, n_iter: int = 5, hno_flo: float = -1E+30, ch_flg: int = 0, wd_flg: int = 0, wet_fct: float = 0.1, newt_iter: int = 1, hd_wet: int = 1, reg_sta: int = 0, mul_td: int = 0, num_td: int = -1): self._CheckLib = None self._model = model self.dim_unit: str = dim_unit self.time_unit: str = time_unit self.sim_mtd: int = sim_mtd self.sim_type: int = sim_type self.acc_lambda: float = acc_lambda self.intblkm: int = intblkm self.solve: int = solve self.max_iter: int = max_iter self.damp: float = damp self.h_close: float = h_close self.r_close: float = r_close self.relax: int = relax self.theta: float = theta self.gamma: float = gamma self.akappa: float = akappa self.n_iter: int = n_iter self.hno_flo: float = hno_flo self.ch_flg: int = ch_flg self.wd_flg: int = wd_flg self.wet_fct: float = wet_fct self.newt_iter: int = newt_iter self.hd_wet: int = hd_wet self.reg_sta: int = reg_sta self.mul_td: int = mul_td self.num_td: int = num_td self._SetDlls() self._Check() model.package[CON_PKG_NAME] = self
[docs] @classmethod def load(cls, model, ctrl_params_file: str): """ Load parameters from a CtrlPar.in file and create a ComusConPars instance. Parameters: -------- model: pycomus.ComusModel COMUS Model Object. ctrl_params_file: str Control Params file path. Returns: -------- instance: pycomus.ComusConPars COMUS Control Params Object. Example: -------- >>> import pycomus >>> model1 = pycomus.ComusModel(model_name="test") >>> controlParams = pycomus.ComusConPars.load(model1,"./InputFiles/CtrlPar.in") """ with open(ctrl_params_file, 'r') as file: lines: List[str] = file.readlines() if len(lines) != 2: raise ValueError("The Control Params file should have exactly two lines of data.") if len(lines[0].strip().split()) != 30: raise ValueError("The Control Params file header should have 30 fields.") data = lines[1].strip().split() if len(data) != 30: raise ValueError("The Control Params data line should have 30 values.") params = { 'dim_unit': data[3], 'time_unit': data[4], 'sim_mtd': int(data[7]), 'sim_type': int(data[8]), 'acc_lambda': float(data[9]), 'intblkm': int(data[10]), 'solve': int(data[11]), 'max_iter': int(data[12]), 'damp': float(data[13]), 'h_close': float(data[14]), 'r_close': float(data[15]), 'relax': int(data[16]), 'theta': float(data[17]), 'gamma': float(data[18]), 'akappa': float(data[19]), 'n_iter': int(data[20]), 'hno_flo': float(data[21]), 'ch_flg': int(data[22]), 'wd_flg': int(data[23]), 'wet_fct': float(data[24]), 'newt_iter': int(data[25]), 'hd_wet': int(data[26]), 'reg_sta': int(data[27]), 'mul_td': int(data[28]), 'num_td': int(data[29]), } instance = cls(model, **params) return instance
[docs] def write_file(self, folder_path: str): """ Typically used as an internal function but can also be called directly, it outputs the `pycomus.ComusConPars` module to the specified path as <CtrlPar.in>. :param folder_path: Output folder path. """ if BCF_LYR_PKG_NAME not in self._model.package and LPF_LYR_PKG_NAME not in self._model.package: raise ValueError( "Before writing the ComusConPars, `pycomus.ComusDisLpf` or `pycomus.ComusDisBcf` should be set first.") if BCF_LYR_PKG_NAME in self._model.package: cms_dis = self._model.package[BCF_LYR_PKG_NAME] else: cms_dis = self._model.package[LPF_LYR_PKG_NAME] num_lyr = cms_dis.num_lyr num_row = cms_dis.num_row num_col = cms_dis.num_col x_coord = cms_dis.x_coord y_coord = cms_dis.y_coord header_line = "NUMLYR NUMROW NUMCOL DIMUNIT TIMEUNIT XSTCORD YSTCORD SIMMTHD SIMTYPE LAMBDA INTBLKM ISOLVE MAXIT DAMP HCLOSE " \ "RCLOSE IRELAX THETA GAMMA AKAPPA NITER HNOFLO ICHFLG IWDFLG WETFCT IWETIT IHDWET IREGSTA IMULTD NUMTD" conParsData = [num_lyr, num_row, num_col, self.dim_unit, self.time_unit, x_coord, y_coord, self.sim_mtd, self.sim_type, self.acc_lambda, self.intblkm, self.solve, self.max_iter, self.damp, self.h_close, self.r_close, self.relax, self.theta, self.gamma, self.akappa, self.n_iter, self.hno_flo, self.ch_flg, self.wd_flg, self.wet_fct, self.newt_iter, self.hd_wet, self.reg_sta, self.mul_td, self.num_td] with open(os.path.join(folder_path, CON_FILE_NAME), "w") as file: file.write(header_line + "\n") file.write(' '.join(map(str, conParsData)))
def _SetDlls(self): current_file_path = os.path.abspath(__file__) current_dir_path = os.path.dirname(current_file_path) system = platform.system() if system == 'Windows': dll_path = os.path.join(current_dir_path, '.././Utils', 'WinCheckParams.dll') elif system == 'Linux': dll_path = os.path.join(current_dir_path, '.././Utils', 'LinuxCheckParams.so') else: raise ValueError("Pycomus only supports Windows and Linux systems.") self._CheckLib = ctypes.CDLL(dll_path) self._CheckLib.CheckCtrlParData.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_double, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_double, ctypes.c_double, ctypes.c_double, ctypes.c_int, ctypes.c_double, ctypes.c_double, ctypes.c_double, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_double, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int] self._CheckLib.CheckCtrlParData.restype = ctypes.c_bool def _Check(self): if not self._CheckLib.CheckCtrlParData(self.sim_mtd, self.sim_type, self.acc_lambda, self.intblkm, self.solve, self.max_iter, self.damp, self.h_close, self.r_close, self.relax, self.theta, self.gamma, self.akappa, self.n_iter, self.ch_flg, self.wd_flg, self.wet_fct, self.newt_iter, self.hd_wet, self.reg_sta, self.mul_td, self.num_td): sys.exit()