#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Tue Dec 3 10:45:01 2019
@author: F. Bauget
Parameters class:
:init: set the default values
:Methods:
- read_file: read the Yaml configuration file containing the model parameters into a Class.
- init_calculation: perform some initialization
- parameters_to_list: convert some parameters to list if they are not
"""
# F. Bauget 2021-05-04: added solute parameters
import numpy
import yaml
import pandas
import glob
[docs]
class Parameters():
"""Init Parameters class. Set setting default values and structure
Parameters contains 5 dictionaries grouping inputs in 5 gategories:
- archi: all inputs related to the architecture generation or reconstruction
* read_architecture: Bool, True = read the architecture from a data (i.e. reconstructed MTG)
* input_dir: string, input directory of the architecture file
* input_file: list of string, architecture files
* seed: integer or list of integer, seed for the Markov generator, if none seed is generated
* length_file: list of strings, files name (included relative path) containing data used to calculate
length laws for the laterals generation
* length_data: float data containing in the files above
* primary_length: float or list of float, length of the primary root
* branching_delay: float or list of float, average distance between to branching
* branching_variability: float, add variability in the delay
* order_max: integer, maximum laterals order
* segment_length: float, length of the vertices
* nude_length: float or list of float, length from the tip without any branching
* ref_radius: float, radius of the primary root
* order_decrease_factor: float, decrease factor apply to the radius to account for its decrease
with lateral order
- hydro: inputs related to the hydrodynamics
* k0: float, the radial conductivity
* axial_conductance_data: list of 2 list of float, the axial conductance vs distance from the tip
- solute: inputs related to solutes transport eather permating or not
* J_s: float, active pumping rate
* P_s: float, permeability coefficient
* Cse: float, concentration of permeating solutes
* Ce: float, concentration of non-permeating solutes
* sigma: float, reflextion coefficient
- exp: inputs related to the experimental conditions and measurements
* jv: float, measured flux at the base
* psi_e: float, water potential surrounding the root
* psi_base: float, water potential at the base
- output: inputs related to the simulations and its output
* radfold: float or list of float, in factor to k0
* axfold: float or list of float, in factor to axial_conductance_data
* intercepts: list of float, distance from base to calculate the number of intercepts
* run_nb: int, number of simulation with the same set of input
"""
def __init__(self):
# default values
self.archi = {
'read_architecture': False,
'input_dir': '',
'input_file': [],
'seed': [None],
'length_file': ['data/length*order2*.csv', 'data/length*order2*.csv'],
'length_data': [],
'primary_length': [0.13],
'branching_delay': [2e-3],
'branching_variability': 0.25,
'order_max': 4,
'segment_length': 1.e-4,
'nude_length': [0.021],
'ref_radius': 7.0e-5,
'order_decrease_factor': 0.7}
self.hydro = {
'k0': 92.0,
'axial_conductance_data': [[0., 0.03, 0.06, 0.09, 0.12, 0.15, 0.18],
[2.9e-4, 34.8e-4, 147.4e-4, 200.3e-4, 292.6e-4, 262.5e-4, 511.1e-4]]}
self.solute = {
'J_s': 0.,
'P_s': 0.,
'Cse': 0.,
'Ce': 0.,
'Sigma': 1.}
self.exp = {
'Jv': 0.1,
'psi_e': 0.4,
'psi_base': 0.101325}
self.output = {
'radfold': [1.0],
'axfold': [1.0],
'intercepts': [0.01, 0.02, 0.03, 0.045, 0.06, 0.08],
'run_nb': 1}
[docs]
def read_file(self, filename = None):
"""Read the input yaml file, set the class variables and perform some initialization
see :func:`~init_parameter.Parameters.init_calculation`
:param filename: (string) - the input yaml file (Default value = None)
"""
# read the file as a dictionary
f = open(filename)
parameter = yaml.load(f.read(), Loader = yaml.FullLoader)
# pass the parameters to the class
for pid in parameter['archi']:
self.archi[pid] = parameter['archi'][pid]
for pid in parameter['hydro']:
self.hydro[pid] = parameter['hydro'][pid]
if 'solute' in list(parameter.keys()): # because not in the actual version
for pid in parameter['solute']:
self.solute[pid] = parameter['solute'][pid]
for pid in parameter['experimental']:
self.exp[pid] = parameter['experimental'][pid]
for pid in parameter['output']:
self.output[pid] = parameter['output'][pid]
# transform the parameters as list if needed
# at this stage allow to launch a set of simulations for different set
# see def parameters_to_list
self.archi['primary_length'] = self.parameters_to_list(self.archi['primary_length'])
self.archi['seed'] = self.parameters_to_list(self.archi['seed'])
self.archi['branching_delay'] = self.parameters_to_list(self.archi['branching_delay'])
self.archi['nude_length'] = self.parameters_to_list(self.archi['nude_length'])
self.output['intercepts'] = self.parameters_to_list(self.output['intercepts'])
self.output['radfold'] = self.parameters_to_list(self.output['radfold'])
self.output['axfold'] = self.parameters_to_list(self.output['axfold'])
# few initializations
self.init_calculation()
[docs]
def init_calculation(self):
"""Set archi['length_data'] by reading the two files archi['length_file']
Set the seed to None if seed is not an integer nor a list of integer
:return: itself
"""
# set the data used to calculate the length laws
# column names in the length law files
col_names = ('LR_length_mm', 'relative_distance_to_tip')
self.archi['length_data'] = []
for f in self.archi['length_file']:
d_path = glob.glob(f)[0]
pd = pandas.read_csv(d_path, sep = ';', header = 1, names = col_names)
pd.sort_values(by = 'relative_distance_to_tip', inplace = True)
self.archi['length_data'].append(pd)
if type(self.archi['seed']) == str:
# F. Bauget 2020-04-06 : case where seeds are given in a file
d_path = glob.glob(self.archi['seed'])[0]
self.archi['seed'] = []
lineList = [line.rstrip('\n') for line in open(d_path)]
for x in lineList:
try:
value = int(x)
self.archi['seed'].append(value)
except ValueError:
break
elif type(self.archi['seed']) != int and type(self.archi['seed']) != list:
#set seed to None if not integer
self.archi['seed'] = [None]
[docs]
def parameters_to_list(self, parameter):
"""transform parameter to a list
* in the yaml file it is possible to use the following syntaxe "range(start, end, step)"
then a test checks this syntax and the corresponding list is calculated
* if it is a float or integer, then it is transformed to a list of one single element
:param parameter: the parameter to transform to a list
:return:
- parameter
"""
if type(parameter) != list:
if type(parameter) == str:
if parameter.find('range') == 0:
parameter = parameter.replace('range(', '')
parameter = parameter.replace(')', '')
var = parameter.split(',')
parameter = numpy.arange(float(var[0]), float(var[1]), float(var[2])).tolist()
elif type(parameter) == float or type(parameter) == int :
parameter = [parameter]
return parameter