"""Class for FEHM data"""
"""
Copyright 2013.
Los Alamos National Security, LLC.
This material was produced under U.S. Government contract DE-AC52-06NA25396 for
Los Alamos National Laboratory (LANL), which is operated by Los Alamos National
Security, LLC for the U.S. Department of Energy. The U.S. Government has rights
to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS
ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES
ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce
derivative works, such modified software should be clearly marked, so as not to
confuse it with the version available from LANL.
Additionally, this library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your option)
any later version. Accordingly, this library 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 Lesser General
Public License for more details.
"""
import numpy as np
from copy import copy, deepcopy
import os,time,platform,shutil
from subprocess import Popen, PIPE
from time import sleep
from collections import Counter
import Tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.patches import Rectangle
try: import ctypes; has_ctypes = True
except: has_ctypes = False
try:
from matplotlib import pyplot as plt
#plt.ion()
from mpl_toolkits.mplot3d import axes3d
from matplotlib import cm
from matplotlib.pylab import subplots,close
except ImportError:
'placeholder'
from fgrid import*
from fvars import*
from fpost import*
from fdflt import*
from fhelp import*
dflt = fdflt()
WINDOWS = platform.system()=='Windows'
if not WINDOWS: has_ctypes = False
# list of macros that might be encountered
fdata_sections = ['cont','pres','zonn','zone','cond','time','ctrl','iter','rock','perm',
'boun','flow','strs','text','sol','nfin','hist','node','carb','rlp','grad','nobr',
'flxz','rlpm','hflx','trac','vcon','ppor','vapl','adif','ngas','flxo','head','flxn','air','airwater']
# list of potential CONTOUR output variables
contour_variables=[['strain','stress'],
['co2'],
['vapor','dp','grid','capillary','density','displacement','fhydrate','flux','permeability','porosity','source','velocity','wt','zid'],
['formatted','material','liquid','geo','nodit','concentration','head','pressure','saturation','temperature','xyz','zone']]
# list of potential HISTORY output variables
history_variables=['deg','temperature','mpa','pressure','head','meters','feet','saturation','wco','flow','kgs',
'enthalpy','efl','mjs','density','humidity','viscosity','zflux','concentration','wt','co2s',
'co2sl','co2sg','co2m','co2mt','co2mf','co2md','cfluxz','displacements','disx','disy','disz',
'stress','strsx','strsy','strsz','strsxy','strsxz','strsyz','strain','rel','global']
# dictionary of perm model parameters, indexed by perm model number, ORDER OF LIST MUST EQUAL ORDER OF INPUT
permDicts = dict((
(1,[]),
(21,['nx','ny','nz','frict_coef','cohesion','pore_press_factor','tau_ex_ramp_range',
'yngs_mod_mult_x','yngs_mod_mult_y','yngs_mod_mult_z','perm_mult_x','perm_mult_y','perm_mult_z']),
(22,['frict_coef','cohesion','pore_press_factor','tau_ex_ramp_range','yngs_mod_mult_x','yngs_mod_mult_y',
'yngs_mod_mult_z','perm_mult_x','perm_mult_y','perm_mult_z','por_mult','perm_incremental','tau_ex_init']),
(24,['shear_frac_tough','static_frict_coef','dynamic_frict_coef','frac_num','onset_disp',
'disp_interval','max_perm_change','frac_cohesion','frac_dens']),
(25,['shear_frac_tough',
'static_frict_coef','dynamic_frict_coef','frac_num','onset_disp','disp_interval','max_perm_change',
'frac_cohesion']),
(100,['perm_mult_x','perm_mult_y','perm_mult_z','ramp_range']),
))
# dictionary of plastic model parameters, indexed by plastic model number, ORDER OF LIST MUST EQUAL ORDER OF INPUT
plasticDicts = dict((
(3,['youngs_modulus','poissons_ratio','eta_drucker','zeta_drucker','c_drucker']),
))
# dictionary of perm model units, indexed by perm model number, ORDER OF LIST MUST EQUAL ORDER OF INPUT
permUnits = dict((
(1,[]),
(21,['','','','','MPa','','MPa','','','','','','']),
(22,['','MPa','','MPa','','','','','','','','','MPa']),
(24,['MPa/m','','','','m','m','log(m^2)','MPa','']),
(25,['MPa/m','','','','m','m','log(m^2)','MPa']),
(100,['','','','MPa']),
))
# dictionary of relative permeability model parameters, indexed by model number, ORDER OF LIST MUST EQUAL ORDER OF INPUT
rlpDicts = dict((
(-1,['liq_rel_perm','vap_rel_perm','cap_pres_zero_sat','sat_zero_cap_pres']),
(1,['resid_liq_sat','resid_vap_sat','max_liq_sat','max_vap_sat','cap_pres_zero_sat','sat_zero_cap_pres']),
(2,['resid_liq_sat','resid_vap_sat','cap_pres_zero_sat','sat_zero_cap_pres']),
(3,['resid_liq_sat','resid_vap_sat','inv_air_entry_head','power_exponent',
'low_sat_fit_param','cutoff_sat']),
(16,['resid_water_sat','max_water_sat','water_exp','resid_co2_sat','max_co2_sat','co2_exp',
'co2_water_Pc_exp','co2_water_Pc_max']),
(17,['resid_water_sat','max_water_sat','water_exp','resid_co2liq_sat','max_co2liq_sat','co2liq_exp',
'co2gasliq_exp','resid_co2gas_sat','max_co2gas_sat','co2gas_exp','co2liq_water_Pc_exp','co2liq_water_Pc_max',
'co2liq_co2gas_Pc_exp','co2liq_co2gas_Pc_max']),
))
adsorptionDicts = dict((
(0,['alpha1','alpha2','beta']), # conservative solute
(1,['alpha1','alpha2','beta']), # linear sorption isotherm
(2,['alpha1','alpha2','beta']), # Freundlich sorption isotherm
(3,['alpha1','alpha2','beta']), # Modified Freundlich sorption isotherm
(4,['alpha1','alpha2','beta']), # Langmuir sorption isotherm
))
diffusionDicts = dict((
(0,['diffusion','dispersion_x','dispersion_y','dispersion_z']), # molecular diffusion coefficient is constant
(1,['diffusion','dispersion_x','dispersion_y','dispersion_z']), # Millington Quark diffusion model
(2,['diffusion','dispersion_x','dispersion_y','dispersion_z']), # Conca Wright diffusion model
(3,['diffusion','dispersion_x','dispersion_y','dispersion_z']), # calculated from adif
))
vconDicts = dict((
(1,['T_ref','cond_ref','dcond_dT']), # linear variation of thermal conductivity with temperature
(2,['cond_s1','cond_s0']), # square root variation of thermal conductivity with liquid saturation
(3,['T_ref','cond_ref','exponent']), # intact salt
(4,['T_ref','cond_ref','coeff_phi4','coeff_phi3','coeff_phi2','coeff_phi1','coeff_phi0','exponent']), # crushed salt
))
pporDicts = dict((
(1,['compressibility']), # aquifer compressibility
(-1,['specific_storage']), # specific storage
(-2,['exponent','Px']), # gangi model
(7,['param1','param2','param3','param4']), # unknown - salt
))
model_list = dict((('permmodel',permDicts),
('plasticmodel',plasticDicts),
('rlp',rlpDicts),
('vcon',vconDicts),
('ppor',pporDicts),
('adsorption',adsorptionDicts),
('diffusion',diffusionDicts)))
model_titles = dict((('rlp','RELATIVE PERMEABILITY'),
('permmodel',''),
('plasticmodel',''),
('vcon','VARIABLE THERMAL CONDUCTIVITY'),
('ppor','VARIABLE POROSITY'),
('dispersion',''),
('adsorption',''),
))
# list of macros
fpres = (('pressure',None),('temperature',None),('saturation',None))
fperm = (('kx',None),('ky',None),('kz',None))
fcond = (('cond_x',None),('cond_y',None),('cond_z',None))
fflow = (('rate',None),('energy',None),('impedance',None))
frock = (('density',None),('specific_heat',None),('porosity',None))
fgrad = (('reference_coord',None),('direction',None),('variable',None),('reference_value',None),('gradient',None))
fbiot = (('thermal_expansion',None),('pressure_coupling',None))
felastic = (('youngs_modulus',None),('poissons_ratio',None))
fbodyforce = (('fx',None),('fy',None),('fz',None))
fco2frac = (('water_rich_sat',None),('co2_rich_sat',None),('co2_mass_frac',None),('init_salt_conc',None),('override_flag',None))
fco2flow = (('rate',None),('energy',None),('impedance',None),('bc_flag',None))
fco2diff = (('diffusion',None),('tortuosity',None))
fco2pres = (('pressure',None),('temperature',None),('phase',None))
fstressboun = (('value',None),('direction',None))
fhflx = (('heat_flow',None),('multiplier',None))
ftpor = (('tracer_porosity',None),)
macro_list = dict((('pres',fpres),('perm',fperm),('cond',fcond),('flow',fflow),('rock',frock),
('biot',fbiot),('elastic',felastic),('bodyforce',fbodyforce),('co2frac',fco2frac),('co2flow',fco2flow),
('co2pres',fco2pres),('co2diff',fco2diff),
('stressboun',fstressboun),('grad',fgrad),('hflx',fhflx),('tpor',ftpor)))
# potential nodal properties
node_props = ('kx','ky','kz','cond_x','cond_y','cond_z','density','specific_heat','porosity','thermal_expansion','pressure_coupling',
'youngs_modulus','poissons_ratio')
node_gen = ('rate','energy','impedance')
macro_titles = dict((('pres','INITIAL TEMPERATURE AND PRESSURE'),
('perm','PERMEABILITY'),
('cond','ROCK CONDUCTIVITY'),
('flow','GENERATORS'),
('hflx',''),
('rock','MATERIAL PARAMETERS'),
('grad','INITIAL VARIABLE GRADIENTS'),
('elastic',''),
('biot',''),
('bodyforce',''),
('co2flow',''),
('co2frac',''),
('co2pres',''),
('co2diff',''),
('stressboun',''),
('rlpm','RELATIVE PERMEABILITY'),
('tpor',''),))
macro_descriptor = dict((
('pres','Initial conditions'),
('perm','Permeability'),
('cond','Thermal conductivity properties'),
('flow','Source or sink'),
('rock','Material properties'),
('grad','Initial condition gradients'),
('elastic','Elastic properties'),
('bodyforce','Body force at node'),
('biot','Fluid-stress coupling properties'),
('co2flow','CO2 source of sink'),
('co2frac','CO2 fraction'),
('co2pres','CO2 initial conditions'),
('co2diff','CO2 diffusion properties'),
('stressboun','Stress boundary condition'),
('permmodel','Stress permeability relationship'),
('plasticmodel','Plasticity relationship'),
('rlp','Relative permeablity relationship'),
('hflx','Heat flux boundary condition'),
('tpor','Tracer porosity'),)
)
rlpm_dicts=dict((
('constant',{}),
('linear',(('minimum_saturation',0),('maximum_saturation',1))),
('exponential',(('minimum_saturation',0),('maximum_saturation',1),('exponent',1),('maximum_relperm',1))),
('corey',(('minimum_saturation',0),('maximum_saturation',1))),
('brooks-corey',(('minimum_saturation',0),('maximum_saturation',1),('exponent',1))),
('vg',(('minimum_saturation',0),('maximum_saturation',1),('air_entry_head',1),('exponent',1))),
('linear_cap',(('cap_at_zero_sat',None),('sat_at_zero_cap',None))),
('brooks-corey_cap',(('minimum_saturation',0),('maximum_saturation',1),('exponent',1),('capillary_entry_presure',0.01),
('low_saturation_fitting',None),('cutoff_saturation',None))),
('vg_cap',(('minimum_saturation',0),('maximum_saturation',1),('air_entry_head',1),('exponent',1),
('low_saturation_fitting',None),('cutoff_saturation',None))),
))
rlpm_cap1 = dict((('vg_cap','vg_cap'),('brooks-corey_cap','brooks-corey'),('linear_cap','linear_for')))
rlpm_cap2 = dict((('vg_cap','vg_cap'),('brooks-corey','brooks-corey_cap'),('linear_for','linear_cap')))
rlpm_phases = ['water','air','co2_liquid','co2_gas','vapor']
buildWarnings = []
def _buildWarnings(s):
global buildWarnings
buildWarnings.append(s)
def _title_string(s,n): #prepends headers to sections of FEHM input file
if not n: return
ws = '# '
pad = int(np.floor((n - len(s) - 2)/2))
for i in range(pad): ws+='-'
ws+=s
for i in range(pad): ws+='-'
ws+='\n'
return ws
def _zone_ind(indStr): return abs(int(indStr))-(int(indStr)+abs(int(indStr)))/2
[docs]def process_output(filename,input=None,grid=None,hide=False,silent=False,write=True):
"""Runs an FEHM \*.outp file through the diagnostic tool. Writes output files containing simulation balance,
convergence, time stepping information.
:param filename: Path to the \*.outp file.
:type filename: str
:param input: Path to corresponding FEHM input file .
:type input: str
:param grid: Path to corresponding FEHM grid file.
:type grid: str
:param hide: Suppress diagnostic window (default False).
:type hide: bool
:param silent: Suppress output to the screen (default False).
:type silent: bool
:param write: Write output files (default True).
:type write: bool
"""
if input and grid: dat = fdata(filename=input,gridfilename=grid)
else:
dat = fdata()
dat._path.filename=filename
dat.files.root = dat.filename.split('.')[0]
dat.hist.variables=list(flatten(dat.hist.variables))
dat._diagnostic.hide = hide
dat._diagnostic.write = write
dat._diagnostic.silent = silent
dat._diagnostic.refresh_nodes()
dat._diagnostic.stdout = open(filename)
dat._diagnostic.poll = True
dat._diagnostic.read_with_tcl()
return dat._diagnostic
[docs]class fzone(object): #FEHM zone object.
"""FEHM Zone object.
"""
__slots__ = ['_index','_type','_points','_file','_name','_parent','_nodelist','_file','_permeability',
'_conductivity','_density','_specific_heat','_porosity','_youngs_modulus','_poissons_ratio',
'_thermal_expansion','_pressure_coupling','_Pi','_Ti','_Si','_fixedT','_fixedP','_updateFlag','_silent']
def __init__(self,index=None,type='',points=[],nodelist=[],file='',name = ''):
self._index=None
self._silent = dflt.silent
if index != None: self._index = index
self._type=''
if type: self._type = type
self._points=[]
if points: self._points=points
self._file = ''
self._name = ''
self._parent = None
if name: self._name = name
self._nodelist=[]
if (self.type == 'nnum' or self.type == 'list') and nodelist:
self._nodelist = nodelist
if file: self._file = file
# material properties
self._permeability = None
self._conductivity = None
self._density = None
self._specific_heat = None
self._porosity = None
self._youngs_modulus = None
self._poissons_ratio = None
self._thermal_expansion = None
self._pressure_coupling = None
self._Pi = None
self._Ti = None
self._Si = None
self._fixedT = None
self._fixedP = None
self._updateFlag = True
def __repr__(self): return 'zn'+str(self.index)
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
def _get_index(self): return self._index
def _set_index(self,value): self._index = value
index = property(_get_index,_set_index) #: (*int*) Integer number denoting the zone.
def _get_type(self): return self._type
def _set_type(self,value):
oldtype = self._type
self._type = value
if self._type != oldtype:
self._points = []
type = property(_get_type,_set_type) #: (*str*) String denoting the zone type. Default is 'rect', alternatives are 'list', 'nnum'
def _get_name(self): return self._name
def _set_name(self,value): self._name = value
name = property(_get_name,_set_name) #: (*str*) Name of the zone. Will appear commented beside the zone definition in the input file. Can be used to index the ``fdata.zone`` attribute.
def _get_file(self): return self._file
def _set_file(self,value): self._file=value
file = property(_get_file,_set_file) #: (*str*) File name if zone data is or is to be contained in a separate file. If file does not exist, it will be created and written to when the FEHM input file is being written out.
[docs] def rect(self,p1,p2): #generates a rectangular zone based on corner coordinates
"""Create a rectangular zone corresponding to the bounding box delimited by p1 and p2.
:param p1: coordinate of first corner of the bounding box.
:type p1: ndarray
:param p2: coordinate of the second corner of the bounding box.
:type p2: ndarray
"""
self.type='rect'
if len(p1) == 2:
xmax,xmin = np.max([p1[0],p2[0]]),np.min([p1[0],p2[0]])
ymax,ymin = np.max([p1[1],p2[1]]),np.min([p1[1],p2[1]])
self.points=[[xmin,xmax,xmax,xmin],
[ymax,ymax,ymin,ymin],
#[0., 0., 0., 0., 0., 0., 0., 0.]
]
elif len(p1) == 3:
xmax,xmin = np.max([p1[0],p2[0]]),np.min([p1[0],p2[0]])
ymax,ymin = np.max([p1[1],p2[1]]),np.min([p1[1],p2[1]])
zmax,zmin = np.max([p1[2],p2[2]]),np.min([p1[2],p2[2]])
self.points=[[xmin,xmax,xmax,xmin,xmin,xmax,xmax,xmin],
[ymax,ymax,ymin,ymin,ymax,ymax,ymin,ymin],
[zmax,zmax,zmax,zmax,zmin,zmin,zmin,zmin]]
[docs] def fix_temperature(self,T,multiplier=1.e10,file=None):
''' Fixes temperatures at nodes within this zone. Temperatures fixed by adding an HFLX macro with high
heat flow multiplier.
:param T: Temperature to fix.
:type T: fl64
:param multiplier: Multiplier for HFLX macro (default = 1.e10)
:type multiplier: fl64
:param file: Name of auxiliary file to save macro.
:type file: str
'''
if not self._parent:
pyfehm_print('fix_temperature() only available if zone associated with fdata() object',self._silent)
return
self._parent.add(fmacro('hflx',zone=self,param=(('heat_flow',T),('multiplier',multiplier)),file=file))
self._fixedT = T
[docs] def fix_pressure(self,P=0, T=30., impedance=1.e6, file = None):
''' Fixes pressures at nodes within this zone. Pressures fixed by adding a FLOW macro with high
impedance.
:param P: Pressure to fix. Default is 0, corresponding to fixing initial pressure.
:type P: fl64
:param T: Temperature to fix (default = 30 degC).
:type T: fl64
:param impedance: Impedance for FLOW macro (default = 1.e6)
:type impedance: fl64
:param file: Name of auxiliary file to save macro.
:type file: str
'''
if not self._parent:
pyfehm_print('fix_pressure() only available if zone associated with fdata() object',self._silent)
return
self._parent.add(fmacro('flow',zone=self,param=(('rate',P),('energy',-T),('impedance',impedance)),file=file))
self._fixedP = [P,T]
[docs] def fix_displacement(self,direction,displacement,file=None):
''' Fixes displacement at nodes within this zone. Displacements fixed by adding a STRESSBOUN macro.
:param direction: Direction in which displacement is fixed. Specify as string or integer, e.g., 1 = 'x', 2 = 'y', 3 = 'z'.
:type direction: str, int
:param displacement: Fixed displacement
:type displacement: fl64
:param file: Name of auxiliary file to save macro.
:type file: str
'''
if not self._parent:
pyfehm_print('fix_displacement() only available if zone associated with fdata() object',self._silent)
return
if direction == 'x': direction = 1
elif direction == 'y': direction = 2
elif direction == 'z': direction = 3
elif direction in [1,2,3]: pass
else:
pyfehm_print('direction must be specified as either 1, 2, 3, \'x\', \'y\', \'z\'.',self._silent)
return
self._parent.add(fmacro('stressboun',zone=self,param=(('direction',direction),('value',displacement)),file=file))
[docs] def fix_stress(self,direction,stress,file=None):
''' Fixes displacement at nodes within this zone. Displacements fixed by adding a STRESSBOUN macro.
:param direction: Direction in which stress is fixed. Specify as string or integer, e.g., 1 = 'x', 2 = 'y', 3 = 'z'.
:type direction: str, int
:param stress: Fixed stress
:type stress: fl64
:param file: Name of auxiliary file to save macro.
:type file: str
'''
if not self._parent:
pyfehm_print('fix_stress() only available if zone associated with fdata() object',self._silent)
return
if direction == 'x': direction = 1
elif direction == 'y': direction = 2
elif direction == 'z': direction = 3
elif abs(direction) in [1,2,3]: pass
else:
pyfehm_print('direction must be specified as either 1, 2, 3, \'x\', \'y\', \'z\'.',self._silent)
return
self._parent.add(fmacro('stressboun',zone=self,param=(('direction',-abs(direction)),('value',stress)),file=file))
[docs] def roller(self,direction=None,file=None):
''' Assigns a roller boundary condition to the zone (zero displacement in normal direction).
:param direction: Normal of roller. Specify as string or integer, e.g., 1 = 'x', 2 = 'y', 3 = 'z'. Defaults to zone normal for 'XMIN', 'ZMAX' etc.
:type direction: str, int
'''
if direction is None:
if self.name in ['XMIN','XMAX']: direction = 1
elif self.name in ['YMIN','YMAX']: direction = 2
elif self.name in ['ZMIN','ZMAX']: direction = 3
else:
pyfehm_print('no direction specified',self._silent)
return
self.fix_displacement(direction=direction,displacement=0,file=file)
[docs] def free_surface(self,direction=None,file=None):
''' Assigns a free surface boundary condition to the zone (zero stress in normal direction).
:param direction: Normal of free surface. Specify as string or integer, e.g., 1 = 'x', 2 = 'y', 3 = 'z'. Defaults to zone normal for 'XMIN', 'ZMAX' etc.
:type direction: str, int
'''
if direction is None:
if self.name in ['XMIN','XMAX']: direction = -1
elif self.name in ['YMIN','YMAX']: direction = -2
elif self.name in ['ZMIN','ZMAX']: direction = -3
else:
pyfehm_print('no direction specified',self._silent)
return
self.fix_stress(direction=direction,stress=0,file=file)
def copy_from(self,from_zone=None,grid_new=None,method = 'nearest',grid_old=None):
'''Transfer zone information from one grid to another.
:param from_zone: Zone object to convert.
:type from_zone: fzone
:param method: Method for node to node transfer of zone. Options are 'nearest', 'volume'.
:type method: str
'''
if grid_new: self.grid = grid_new
if grid_old: from_zone.grid = grid_old
if not self.index: self.index = from_zone.index
if not from_zone: pyfehm_print('No zone supplied',self._silent); return
if from_zone.type == 'rect': # if rectangular zone, copy across bounding box
self.type = 'rect'
self.points = from_zone.points
else: # zone comprises a list of nodes
if not from_zone.grid or not from_zone.nodelist:
pyfehm_print('Supplied zone does not contain grid information.',self._silent)
return
from_nodes = from_zone.nodelist
if method == 'nearest':
ndinds = np.unique([self.grid.node_nearest_point(nd.position) for nd in from_nodes])
ndinds = [nd.index for nd in ndinds if nd != None]
elif method == 'volume':
'a'
if not self.type: self.type = from_zone.type
self.nodelist = [self.grid.node[ndind] for ndind in ndinds]
[docs] def plot(self,save='',angle=[45,45],color='k',connections=False,equal_axes=True,
xlabel='x / m',ylabel='y / m',zlabel='z / m',title='',font_size='small'): #generates a 3-D plot of the zone.
'''Generates and saves a 3-D plot of the zone.
:param save: Name of saved zone image.
:type save: str
:param angle: View angle of zone. First number is azimuth angle in degrees, second number is tilt. Alternatively, if angle is 'x', 'y', 'z', view is aligned along the corresponding axis.
:type angle: [fl64,fl64], str
:param color: Colour of zone.
:type color: str, [fl64,fl64,fl64]
:param connections: Plot connections. If ``True`` all connections plotted. If between 0 and 1, random proportion plotted. If greater than 1, specified number plotted.
:type connections: bool
:param equal_axes: Force plotting with equal aspect ratios for all axes.
:type equal_axes: bool
:param xlabel: Label on x-axis.
:type xlabel: str
:param ylabel: Label on y-axis.
:type ylabel: str
:param zlabel: Label on z-axis.
:type zlabel: str
:param title: Title of plot.
:type title: str
:param font_size: Size of text on plot.
:type font_size: str, int
*Example:*
``zn.plot(save='myzone.png', angle = [45,45], xlabel = 'x / m', font_size = 'small', color = 'r')``
'''
save = os_path(save)
if isinstance(angle,str):
if angle == 'x': angle = [0,0]
elif angle == 'y': angle = [0,90]
elif angle == 'z': angle = [90,90]
else: return
plotBoundingBox = False
plotZoneBox = False
if self._parent: plotBoundingBox = True
if self.type == 'rect': plotZoneBox = True
# plot bounding box
plt.clf()
fig = plt.figure(figsize=[8.275,11.7])
ax = plt.axes(projection='3d')
ax.set_aspect('equal', 'datalim')
ax.set_xlabel(xlabel,size=font_size)
ax.set_ylabel(ylabel,size=font_size)
ax.set_zlabel(zlabel,size=font_size)
ax.set_title(title,size=font_size)
for t in ax.get_xticklabels():
t.set_fontsize(font_size)
for t in ax.get_yticklabels():
t.set_fontsize(font_size)
for t in ax.get_zticklabels():
t.set_fontsize(font_size)
xmin,xmax = self._parent.grid.xmin, self._parent.grid.xmax
ymin,ymax = self._parent.grid.ymin, self._parent.grid.ymax
zmin,zmax = self._parent.grid.zmin, self._parent.grid.zmax
if equal_axes:
MAX = np.max([xmax-xmin,ymax-ymin,zmax-zmin])/2
C = np.array([xmin+xmax,ymin+ymax,zmin+zmax])/2
for direction in (-1, 1):
for point in np.diag(direction * MAX * np.array([1,1,1])):
ax.plot([point[0]+C[0]], [point[1]+C[1]], [point[2]+C[2]], 'w')
ax.view_init(angle[0],angle[1])
if plotBoundingBox:
# x lines
ax.plot([xmin,xmax],[ymin,ymin],[zmin,zmin],'k--')
ax.plot([xmin,xmax],[ymax,ymax],[zmin,zmin],'k--')
ax.plot([xmin,xmax],[ymax,ymax],[zmax,zmax],'k--')
ax.plot([xmin,xmax],[ymin,ymin],[zmax,zmax],'k--')
# y lines
ax.plot([xmin,xmin],[ymin,ymax],[zmin,zmin],'k--')
ax.plot([xmax,xmax],[ymin,ymax],[zmin,zmin],'k--')
ax.plot([xmax,xmax],[ymin,ymax],[zmax,zmax],'k--')
ax.plot([xmin,xmin],[ymin,ymax],[zmax,zmax],'k--')
# z lines
ax.plot([xmin,xmin],[ymin,ymin],[zmin,zmax],'k--')
ax.plot([xmin,xmin],[ymax,ymax],[zmin,zmax],'k--')
ax.plot([xmax,xmax],[ymax,ymax],[zmin,zmax],'k--')
ax.plot([xmax,xmax],[ymin,ymin],[zmin,zmax],'k--')
# plot node connections
if connections:
conns = []
for node in self.nodelist:
for nb in node.connected_nodes:
if nb in self.nodelist:
p1 = node.position
p2 = nb.position
conns.append((p1,p2))
if not isinstance(connections,bool): # plot all connections
if connections>0 and connections<1: # plot proportion of connections
connections = int(len(conns)*connections)
connections = np.min([connections,len(conns)])
import random
random.shuffle(conns)
conns = conns[:connections]
for p1,p2 in conns:
ax.plot([p1[0],p2[0]],[p1[1],p2[1]],[p1[2],p2[2]],color=color, linestyle = '-', linewidth = 0.5) # plot some connections
# plot nodes
for node in self.nodelist:
ax.plot([node.position[0],],[node.position[1],],[node.position[2],],
markerfacecolor=color,marker='o',markersize=3,markeredgecolor=color)
if plotZoneBox:
xmax,xmin = np.max(self.points[0]),np.min(self.points[0])
ymax,ymin = np.max(self.points[1]),np.min(self.points[1])
zmax,zmin = np.max(self.points[2]),np.min(self.points[2])
# x lines
ax.plot([xmin,xmax],[ymin,ymin],[zmin,zmin],color=color,linestyle='-')
ax.plot([xmin,xmax],[ymax,ymax],[zmin,zmin],color=color,linestyle='-')
ax.plot([xmin,xmax],[ymax,ymax],[zmax,zmax],color=color,linestyle='-')
ax.plot([xmin,xmax],[ymin,ymin],[zmax,zmax],color=color,linestyle='-')
# y lines
ax.plot([xmin,xmin],[ymin,ymax],[zmin,zmin],color=color,linestyle='-')
ax.plot([xmax,xmax],[ymin,ymax],[zmin,zmin],color=color,linestyle='-')
ax.plot([xmax,xmax],[ymin,ymax],[zmax,zmax],color=color,linestyle='-')
ax.plot([xmin,xmin],[ymin,ymax],[zmax,zmax],color=color,linestyle='-')
# z lines
ax.plot([xmin,xmin],[ymin,ymin],[zmin,zmax],color=color,linestyle='-')
ax.plot([xmin,xmin],[ymax,ymax],[zmin,zmax],color=color,linestyle='-')
ax.plot([xmax,xmax],[ymax,ymax],[zmin,zmax],color=color,linestyle='-')
ax.plot([xmax,xmax],[ymin,ymin],[zmin,zmax],color=color,linestyle='-')
extension, save_fname, pdf = save_name(save,variable='zone'+str(self.index),time=1)
plt.savefig(save_fname, dpi=200, facecolor='w', edgecolor='w',orientation='portrait',
format=extension,transparent=True, bbox_inches=None, pad_inches=0.1)
if pdf:
os.system('epstopdf ' + save_fname)
os.remove(save_fname)
[docs] def topo(self,save='',cbar=True,equal_axes=True, method = 'nearest', divisions=[30,30], xlims=[],
ylims=[], clims=[], levels=10,clabel='',
xlabel='x / m',ylabel='y / m',zlabel='z / m',title='',font_size='small'): #generates a 2-D topographical plot of the zone.
'''Returns a contour plot of the top surface of the zone.
:param divisions: Resolution to supply mesh data.
:type divisions: [int,int]
:param method: Method of interpolation, options are 'nearest', 'linear'.
:type method: str
:param levels: Contour levels to plot. Can specify specific levels in list form, or a single integer indicating automatic assignment of levels.
:type levels: lst[fl64], int
:param cbar: Add colour bar to plot.
:param type: bool
:param xlims: Plot limits on x-axis.
:type xlims: [fl64, fl64]
:param ylims: Plot limits on y-axis.
:type ylims: [fl64, fl64]
:param clims: Colour limits.
:type clims: [fl64,fl64]
:param save: Name to save plot. Format specified extension (default .png if none give). Supported extensions: .png, .eps, .pdf.
:type save: str
:param xlabel: Label on x-axis.
:type xlabel: str
:param ylabel: Label on y-axis.
:type ylabel: str
:param title: Plot title.
:type title: str
:param font_size: Specify text size, either as an integer or string, e.g., 10, 'small', 'x-large'.
:type font_size: str, int
:param equal_axes: Specify equal scales on axes.
:type equal_axes: bool
*Example:*
``dat.zone[2].topo('zoneDEMtopo.png',method = 'linear')``
'''
save = os_path(save)
if not self.nodelist:
pyfehm_print('ERROR: No node information, aborting...',self._silent)
return
if not title:
title = 'Topographic plot of zone ' +str(self.index)
if self.name: title += ': '+self.name
# assemble data
xs = np.unique([nd.position[0] for nd in self.nodelist])
ys = np.unique([nd.position[1] for nd in self.nodelist])
xmin = np.min(xs); xmax = np.max(xs)
ymin = np.min(ys); ymax = np.max(ys)
xrange = np.linspace(xmin,xmax,divisions[0])
yrange = np.linspace(ymin,ymax,divisions[1])
XI,YI = np.meshgrid(xs,ys)
X,Y = np.meshgrid(xrange,yrange)
ZI = np.ones((len(ys),len(xs)))*self._parent.grid.zmin-1
for nd in self.nodelist:
i = np.where(xs==nd.position[0])[0][0]
j = np.where(ys==nd.position[1])[0][0]
ZI[j,i] = np.max([ZI[j,i],nd.position[2]])
pts = np.transpose(np.reshape((X,Y),(2,X.size)))
ptsI = np.transpose(np.reshape((XI,YI,ZI),(3,XI.size)))
from scipy.interpolate import griddata
vals = griddata(ptsI[:,:2],ptsI[:,2],pts,method=method)
vals = np.reshape(vals,(X.shape[0],X.shape[1]))
# plot topo
plt.clf()
fig = plt.figure(figsize=[8.275,11.7])
ax = plt.axes([0.15,0.15,0.7,0.7])
if xlims: ax.set_xlim(xlims)
if ylims: ax.set_ylim(ylims)
if equal_axes: ax.set_aspect('equal', 'datalim')
CS = plt.contourf(X,Y,vals,levels)
if clims: CS.vmin=clims[0]; CS.vmax=clims[1]
if xlabel: plt.xlabel(xlabel,size=font_size)
if ylabel: plt.ylabel(ylabel,size=font_size)
if title: plt.title(title,size=font_size)
if cbar:
cbar=plt.colorbar(CS)
for t in cbar.ax.get_yticklabels():
t.set_fontsize(font_size)
for t in ax.get_xticklabels():
t.set_fontsize(font_size)
for t in ax.get_yticklabels():
t.set_fontsize(font_size)
ax.set_aspect('equal', 'datalim')
extension, save_fname, pdf = save_name(save,variable='zone_topo'+str(self.index),time=1)
plt.savefig(save_fname, dpi=200, facecolor='w', edgecolor='w',orientation='portrait',
format=extension,transparent=True, bbox_inches=None, pad_inches=0.1)
def _get_info(self):
"""Print details of the zone to the screen."""
ws = 'Zone '+str(self.index)+'\n'
ws+='\nGeometric properties.......\n'
if self.type == 'rect':
ws += ' Type: rectangular box\n'
if self.nodelist: ws += ' Contains '+str(len(self.nodelist))+' nodes\n'
pts = np.array(self.points)
if np.size(pts)==24: pts=pts.reshape(3,8)
else: pts = pts.reshape(2,4)
xmin, xmax = np.min(pts[0,:]), np.max(pts[0,:])
ymin, ymax = np.min(pts[1,:]), np.max(pts[1,:])
if pts.shape[1] == 3:
zmin, zmax = np.min(pts[2,:]), np.max(pts[2,:])
dx,dy,dz = [xmax-xmin,ymax-ymin,zmax-zmin]
ws += ' dimensions: ['+str(dx)+', '+str(dy)+', '+str(dz)+']'
if 100*dz<dx and 100*dz<dy: ws += ' (plane at z = '+str((zmin+zmax)/2)+')'
elif 100*dx<dy and 100*dx<dz: ws += ' (plane at x = '+str((xmin+xmax)/2)+')'
elif 100*dy<dz and 100*dy<dz: ws += ' (plane at y = '+str((ymin+ymax)/2)+')'
elif 100*dz<dx and 100*dy<dx: ws += ' (column at x = '+str((xmin+xmax)/2)+')'
elif 100*dx<dy and 100*dz<dy: ws += ' (column at y = '+str((ymin+ymax)/2)+')'
elif 100*dx<dz and 100*dy<dz: ws += ' (column at z = '+str((zmin+zmax)/2)+')'
ws += '\n'
ws += ' x-range: '+str(xmin)+' - '+str(xmax)+'\n'
ws += ' y-range: '+str(ymin)+' - '+str(ymax)+'\n'
if pts.shape[1] == 3:
ws += ' z-range: '+str(zmin)+' - '+str(zmax)+'\n'
ws += ' mid-point: ['+str((xmin+xmax)/2.)+', '+str((ymin+ymax)/2.)+', '+str((zmin+zmax)/2.)+']\n'
elif pts.shape[1] ==1:
ws += ' mid-point: ['+str((xmin+xmax)/2.)+', '+str((ymin+ymax)/2.)+']\n'
elif self.type == 'list':
#ws = 'Zone '+str(self.index)+'\n'
#ws+='\nGeometric properties.......\n'
ws += ' Type: list\n'
ws += ' Contains '+str(len(self.nodelist))+' nodes\n'
pts=np.array(self.points)
ws += ' x-range: '+str(np.min(pts[:,0]))+' - '+str(np.max(pts[:,0]))+'\n'
ws += ' y-range: '+str(np.min(pts[:,1]))+' - '+str(np.max(pts[:,1]))+'\n'
ws += ' z-range: '+str(np.min(pts[:,2]))+' - '+str(np.max(pts[:,2]))+'\n'
ws += ' mid-point: ['+str(np.mean(pts[:,0]))+', '+str(np.mean(pts[:,1]))+', '+str(np.mean(pts[:,2]))+']\n'
if pts.shape[0]<20:
ws+=' points: ['+str(self.points[0][0])+', '+str(self.points[0][1])+', '+str(self.points[0][2])+']\n'
for pt in self.points[1:]:
ws+=' ['+str(pt[0])+', '+str(pt[1])+', '+str(pt[2])+']\n'
elif self.type == 'nnum':
ws += ' Type: nnum (list of nodes)\n'
ws += ' Contains '+str(len(self.nodelist))+' nodes\n'
elif not self.index:
ws += ' Background zone (denoted 1 0 0), encompassing all nodes.\n'
else: return
ws += '\nMaterial properties........\n'
if self._permeability != None: ws += ' permeability...... '+str(self._permeability)+'\n'
if self._conductivity != None: ws += ' conductivity...... '+str(self._conductivity)+'\n'
if self._density: ws += ' density........... '+str(self._density)+'\n'
if self._specific_heat: ws += ' specific heat..... '+str(self._specific_heat)+'\n'
if self._porosity: ws += ' porosity.......... '+str(self._porosity)+'\n'
if self._youngs_modulus: ws += ' Youngs modulus.... '+str(self._youngs_modulus)+'\n'
if self._poissons_ratio: ws += ' Poissons ratio.... '+str(self._poissons_ratio)+'\n'
if self._thermal_expansion: ws += ' thermal expansion. '+str(self._thermal_expansion)+'\n'
if self._pressure_coupling: ws += ' pressure coupling. '+str(self._pressure_coupling)+'\n'
ws += '\nInitial conditions.........\n'
if self._Pi: ws += ' pressure.......... '+str(self._Pi)+'\n'
if self._Ti: ws += ' temperature....... '+str(self._Ti)+'\n'
if self._Si: ws += ' saturation........ '+str(self._Si)+'\n'
ws += '\nBoundary conditions........\n'
if self._fixedT: ws += ' fixed temperature.... '+str(self._fixedT)+'\n'
if self._fixedP:
ws += ' fixed pressure....... '+str(self._fixedP[0])+'\n'
ws += ' inflow temperature. '+str(self._fixedP[1])+'\n'
print ws
what = property(_get_info) #: Print to screen information about the zone.
def _get_nodes(self):
"""Assemble a list of fnode objects contained in the zone."""
nds = []
if self.type == 'rect':
if not self._parent.grid: self._nodelist = nds; return self._nodelist
xmax,xmin = np.max(self.points[0]),np.min(self.points[0])
ymax,ymin = np.max(self.points[1]),np.min(self.points[1])
if self._parent._grid.dimensions == 3:
zmax,zmin = np.max(self.points[2]),np.min(self.points[2])
else:
zmax,zmin = self._parent._grid.zmax+.01,self._parent._grid.zmin-.01
x,y,z = np.array([nd._position for nd in self._parent._grid._nodelist]).T
inds = np.where((x<=xmax)&(x>=xmin)&(y<=ymax)&(y>=ymin)&(z<=zmax)&(z>=zmin))
self._nodelist = [self._parent._grid._nodelist[i] for i in inds[0]]
if self._index == 0: self._nodelist = self._parent._grid._nodelist
return self._nodelist
def _set_nodes(self,value):
if self.type == 'rect':
pyfehm_print('ERROR: nodelist for zone defined by content of points.',self._silent)
return
self._nodelist = value
nodelist = property(_get_nodes,_set_nodes) #: (*lst[fnode]*) List of nodes contained within the zone.
def _get_node(self): return dict([(nd.index,nd) for nd in self.nodelist])
node = property(_get_node) #: (*dict[fnode]*) Dictionary of nodes contained within the zone, indexed by node number.
def _get_points(self):
"""Assemble spatial data as it would appear in an FEHM input file."""
pts=[]
if self.type in ['nnum','list']:
if not self._parent.grid: self._points = pts; return self._points
row = []
if self.type == 'nnum': row.append(len(self.nodelist))
for nd in self.nodelist:
if isinstance(nd,int): nd = self._parent.grid.node[nd]
if self.type == 'list': pts.append(nd.position)
elif self.type == 'nnum': row.append(nd.index)
if len(row) == 10: pts.append(row); row = []
if row != []: pts.append(row)
self._points = pts
return self._points
def _set_points(self,value):
if self.type in ['list','nnum']:
pyfehm_print('ERROR: points defined by content of nodelist.',self._silent)
return
self._points = value
points = property(_get_points,_set_points)#: (*lst[fl64]*) Spatial data defining the zone.
def _get_permeability(self): return self._permeability
def _set_permeability(self,value):
self._permeability = value
# set commands
if not self._parent: _buildWarnings('Zone not associated with input file, no macro changes made.'); return
if isinstance(value,(int,float)): kx = value; ky = value; kz = value
elif isinstance(value,(list,tuple,np.ndarray)) and len(value)==3: kx,ky,kz = value
if self.index in self._parent.perm.keys():
if self._updateFlag:
self._parent.perm[self.index].param['kx']=kx
self._parent.perm[self.index].param['ky']=ky
self._parent.perm[self.index].param['kz']=kz
else:
self._parent.add(fmacro('perm',zone=self.index,param=(('kx',kx),('ky',ky),('kz',kz))))
if self._parent:
for nd in self.nodelist:
if not (nd.permeability is not None and self.index == 0):
nd._permeability = np.array([kx,ky,kz])
permeability = property(_get_permeability, _set_permeability) #: (*fl64*,*lst*) Permeability properties of zone.
def _get_conductivity(self): return self._conductivity
def _set_conductivity(self,value):
self._conductivity = value
# set commands
if not self._parent: _buildWarnings('Zone not associated with input file, no macro changes made.'); return
if isinstance(value,(int,float)): kx = value; ky = value; kz = value
elif isinstance(value,(list,tuple,np.ndarray)) and len(value)==3: kx,ky,kz = value
if self.index in self._parent.cond.keys():
if self._updateFlag:
self._parent.cond[self.index].param['cond_x']=kx
self._parent.cond[self.index].param['cond_y']=ky
self._parent.cond[self.index].param['cond_z']=kz
else:
self._parent.add(fmacro('cond',zone=self.index,param=(('cond_x',kx),('cond_y',ky),('cond_z',kz))))
if self._parent:
for nd in self.nodelist:
if not (nd.conductivity is not None and self.index == 0):
nd._conductivity = np.array([kx,ky,kz])
conductivity = property(_get_conductivity, _set_conductivity) #: (*fl64*,*lst*) Conductivity properties of zone.
def _set_property(self,value,prop0,props,macro):
self.__setattr__(prop0,value)
if not self._parent:
pyfehm_print('Zone not associated with input file, no macro changes made.',self._silent)
return
# macro creation/modification
ks = self._parent.__getattribute__(macro).keys()
if self.index in ks:
if self._updateFlag:
self._parent.__getattribute__(macro)[self.index].param[prop0[1:]]=value
else:
params = [(prop0[1:],value)]
for prop in props:
params.append((prop[1:],dflt.__getattribute__(prop[1:])))
self._parent.add(fmacro(macro,zone=self.index,param=tuple(params)))
warn_string = 'WARNING: Assigning default '
for prop in props:
warn_string += prop[1:]+' (%6.1f'%dflt.__getattribute__(prop[1:])+'), '
warn_string = warn_string[:-2] + ' to zone '+str(self.index)+'.'
_buildWarnings(warn_string)
for prop in props:
self.__setattr__(prop[1:],dflt.__getattribute__(prop[1:]))
# node association
if self._parent:
for nd in self._nodelist:
if isinstance(nd,int): nd = self._parent.grid.node[nd]
if len(set([zn.index for zn in nd.zonelist])-set([994,995,996,997,998,999]))==0:
nd.__setattr__(prop0,value)
elif len([zn.index for zn in nd.zonelist if zn.index in ks])==0:
nd.__setattr__(prop0,value)
elif self.index == np.max([zn.index for zn in nd.zonelist if zn.index in ks]):
nd.__setattr__(prop0,value)
def _get_density(self): return self._density
def _set_density(self,value):
self._set_property(value,'_density',['_specific_heat','_porosity'],'rock')
density = property(_get_density, _set_density) #: (*fl64*) Density of zone.
def _get_specific_heat(self): return self._specific_heat
def _set_specific_heat(self,value):
self._set_property(value,'_specific_heat',['_density','_porosity'],'rock')
specific_heat = property(_get_specific_heat, _set_specific_heat) #: (*fl64*) Specific heat of zone.
def _get_porosity(self): return self._porosity
def _set_porosity(self,value):
self._set_property(value,'_porosity',['_density','_specific_heat'],'rock')
porosity = property(_get_porosity, _set_porosity) #: (*fl64*) Porosity of zone.
def _get_youngs_modulus(self): return self._youngs_modulus
def _set_youngs_modulus(self,value):
self._set_property(value,'_youngs_modulus',['_poissons_ratio'],'elastic')
youngs_modulus = property(_get_youngs_modulus, _set_youngs_modulus) #: (*fl64*) Young's modulus of zone.
def _get_poissons_ratio(self): return self._poissons_ratio
def _set_poissons_ratio(self,value):
self._set_property(value,'_poissons_ratio',['_youngs_modulus'],'elastic')
poissons_ratio = property(_get_poissons_ratio, _set_poissons_ratio) #: (*fl64*) Poisson's ratio of zone.
def _get_thermal_expansion(self): return self._thermal_expansion
def _set_thermal_expansion(self,value):
self._set_property(value,'_thermal_expansion',['_pressure_coupling'],'biot')
thermal_expansion = property(_get_thermal_expansion, _set_thermal_expansion) #: (*fl64*) Coefficient of thermal expansion of zone.
def _get_pressure_coupling(self): return self._pressure_coupling
def _set_pressure_coupling(self,value):
self._set_property(value,'_pressure_coupling',['_thermal_expansion'],'biot')
pressure_coupling = property(_get_pressure_coupling, _set_pressure_coupling) #: (*fl64*) Pressure coupling term of zone.
def _get_Pi(self): return self._Pi
def _set_Pi(self,value):
self._Pi = value
# set commands
if not self._parent:
pyfehm_print('Zone not associated with input file, no macro changes made.',self._silent)
return
if self.index in self._parent.pres.keys():
if self._updateFlag:
self._parent.pres[self.index].param['pressure']=value
else:
self._parent.add(fmacro('pres',zone=self.index,param=(('pressure',value),('temperature',dflt.Ti),('saturation',1))))
_buildWarnings('WARNING: Assigning default initial temperature (%4.1f'%dflt.Ti+' degC, fully saturated liquid) to zone '+str(self.index)+'.')
self.Ti = dflt.Ti
if self.Ti > tsat(self.Pi)[0]:
self._parent.pres[self.index].param['saturation']=3
self.Si = 0.
else:
self._parent.pres[self.index].param['saturation']=1
self.Si = 1.
if self._parent:
for nd in self.nodelist:
if not (nd.Pi is not None and self.index == 0):
nd._Pi = value
Pi = property(_get_Pi, _set_Pi) #: (*fl64*) Initial pressure in zone.
def _get_Ti(self): return self._Ti
def _set_Ti(self,value):
self._Ti = value
# set commands
if not self._parent:
pyfehm_print('Zone not associated with input file, no macro changes made.',self._silent)
return
if self.index in self._parent.pres.keys():
if self._updateFlag:
self._parent.pres[self.index].param['temperature']=value
else:
self._parent.add(fmacro('pres',zone=self.index,param=(('pressure',dflt.Pi),('temperature',value),('saturation',1))))
_buildWarnings('WARNING: Assigning default initial pressure (%4.1f'%dflt.Pi+' MPa, fully saturated liquid) to zone '+str(self.index)+'.')
self._Pi = dflt.Pi
if self._Ti > tsat(self._Pi)[0]:
self._parent.pres[self.index].param['saturation']=3
self._Si = 0.
else:
self._parent.pres[self.index].param['saturation']=1
self._Si = 1.
if self._parent:
for nd in self.nodelist:
if not (nd.Ti is not None and self.index == 0):
nd._Ti = value
Ti = property(_get_Ti, _set_Ti) #: (*fl64*) Initial temperature in zone.
def _get_Si(self): return self._Si
def _set_Si(self,value):
self._Si = value
# set commands
if not self._parent:
pyfehm_print('Zone not associated with input file, no macro changes made.',self._silent)
return
if self.index in self._parent.pres.keys():
if self._updateFlag:
self._parent.pres[self.index].param['temperature']=value
self._parent.pres[self.index].param['saturation']=2
else:
self._parent.add(fmacro('pres',zone=self.index,param=(('pressure',dflt.Pi),('temperature',value),('saturation',2))))
_buildWarnings('WARNING: Assigning default initial pressure (%4.1f'%dflt.Pi+' MPa, two phase) to zone '+str(self.index)+'.')
self._Ti = tsat(self._parent.pres[self.index].param['pressure'])[0]
if self._parent:
for nd in self.nodelist:
if not (nd.Si is not None and self.index == 0):
nd._Si = value
Si = property(_get_Si, _set_Si) #: (*fl64*) Initial saturation in zone.
def _check(self):
# if file called for but non-existant on disk, print warning
if self.type == None: _buildWarnings('WARNING: Zone '+str(self.index)+' type not assigned.')
if self.points == None: _buildWarnings('WARNING: Zone '+str(self.index)+' points array is empty.')
[docs]class fmacro(object): #FEHM macro object
"""FEHM macro object.
"""
__slots__=['_silent','_type','_param','_parent','_zone','_subtype','_file','_write_one_macro']
def __init__(self,type='',zone=[],param=[],subtype='',file = None,write_one_macro=False):
self._type = type
self._silent = dflt.silent
if type == 'stressboun' and not subtype: subtype = 'fixed'
if type == 'bodyforce' and not subtype: subtype = 'force'
if type == 'stressboun': write_one_macro = True
self._param = None
self._check_type()
self._assign_param()
self._parent = None
if param: self._set_param2(param)
self._zone = []
if zone != []: self.zone = zone
self._subtype = subtype
self._check_zone()
self._file = file
self._write_one_macro = write_one_macro
def _assign_param(self):
'''Assign parameters if supplied on initialisation.'''
self._param = dict(macro_list[self.type])
def _set_param2(self,param):
'''Assign keys in param attribute appropriate to specific macro.'''
for par,key in zip(param,macro_list[self.type]):
if isinstance(par,list) or isinstance(par,tuple):
self._param[par[0]] = par[1]
else:
self._param[key[0]] = par
def _check_type(self):
'''Determine if macro is supported.'''
if self.type not in macro_list.keys(): return None
def _check_zone(self):
'''Determine if zone definitions are acceptable.'''
if not self.zone: return
elif isinstance(self.zone,fzone): return
elif isinstance(self.zone,fnode):
self.zone = (self.zone.index,self.zone.index,1)
elif isinstance(self.zone,tuple):
if len(self.zone) != 3: self.zone=None; return
self.zone = tuple([int(pt) for pt in self.zone])
elif isinstance(self.zone,list):
newlist = []
for zn in self.zone:
if isinstance(zn,fzone) or (isinstance(zn,tuple) and len(zn) == 3): newlist.append(zn)
self.zone = newlist
elif isinstance(self.zone,int) or isinstance(self.zone,str): return
else: self.zone=None; return
def __repr__(self):
prntStr = self.type +': '
if isinstance(self.zone,fzone): prntStr += str(self.zone.index)
elif isinstance(self.zone,list):
for zn in self.zone: prntStr += str(self.zone.index)+ ' ,'
prntStr = prntStr[:-2]
elif isinstance(self.zone,tuple):
zn = self.zone
prntStr += '('+str(zn[0]) +', ' +str(zn[1]) +', '+str(zn[2]) +')'
return prntStr
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
def _get_type(self): return self._type
def _set_type(self,type):
oldtype = self._type
self._type = type
if oldtype != type: self.param = dict(macro_list[self._type])
type = property(_get_type,_set_type)#: (*str*) Name of the macro. Macro names are identical to those invoked in FEHM.
def _get_param(self): return self._param
def _set_param(self,value): self._param = value
param = property(_get_param, _set_param) #: (*dict[fl64]*) A dictionary of values defining the operation of the macro. See table below for macro-specific dictionary keys.
def _get_zone(self): return self._zone
def _set_zone(self,value): self._zone = value
zone = property(_get_zone,_set_zone)#: (*fzone, lst[fzone], tuple[int,int,int], zone key*) The zone, zones or nodes to which the macro is assigned. Note, only permmodel and rlp can be assigned lists of zones. Optionally, a key (index or string) may be passed, in which case the zone will be retrieved when the macro is added to the model.
def _get_subtype(self): return self._subtype
def _set_subtype(self,value):
self._subtype = value
if self.type not in ['stressboun','bodyforce']: _buildWarnings('WARNING: subtype ignored unless macro is stressboun or bodyforce')
subtype = property(_get_subtype,_set_subtype) #: (*str*) Macro subtype, required for **STRESSBOUN** or **BODYFORCE** macros.
def _get_file(self): return self._file
def _set_file(self,value): self._file = value
file = property(_get_file,_set_file)#: (*str*) File string where information about the macro is stored. If file does not currently exist, it will be created and written to when the FEHM input file is written.
def _check(self):
# if not zone assigned, apply default background
if self._zone == None:
_buildWarnings('WARNING: Macro '+str(self.type)+' has no zone assigned.')
# if parameter value not assigned, print warning
for key in self.param.keys():
if self.param[key] is None: _buildWarnings('WARNING: Macro '+str(self.type)+':'+str(self.zone.index)+' '+key+' not assigned.')
def _get_info(self):
prntStr = self.type + ': ' + macro_descriptor[self.type] +'\n'
# print zone info
zns = self.zone
if isinstance(zns,fzone):
prntStr += 'Assigned to zone ' +str(zns.index)+'.\n'
elif isinstance(zns,list):
prntStr += 'Assigned to zones '
for zn in zns:
if isinstance(zn,fzone):
prntStr += str(zn.index) +', '
elif isinstance(zn,tuple):
prntStr += '('+str(zn[0]) +', ' +str(zn[1]) +', '+str(zn[2]) +'), '
prntStr = prntStr[:-2]+'.\n'
elif isinstance(zns,tuple):
prntStr += 'Assigned to node set ('+str(zns[0]) +', ' +str(zns[1]) +', '+str(zns[2]) +').\n'
# print parameter info
prntStr += 'Parameters: \n'
for par in macro_list[self.type]:
if self.param[par[0]] is None:
prntStr += ' ' + par[0] + ': Not assigned\n'
else:
prntStr += ' ' + par[0] + ': ' + str(self.param[par[0]]) + '\n'
if self.type == 'stressboun': prntStr += self._more_info_stressboun()
if self.type == 'flow': prntStr += self._more_info_flow()
print prntStr + '\n'
what = property(_get_info) #: Return a summary of the macros function.
def _more_info_flow(self):
'''Return additional information about flow macro.'''
prntStr = ''
if self.param['rate']>0:
if self.param['energy']>0:
if self.param['impedance']==0: prntStr += 'Mass production at fixed rate of ' + str(abs(self.param['rate']))+' kg/s'
else: prntStr += 'Mass production against specified WHP of ' + str(abs(self.param['rate'])) + ' MPa'
else:
if self.param['energy']>0:
if self.param['impedance']==0: prntStr += 'Mass injection of '+str(self.param['energy'])+' MJ/kg fluid at fixed rate of ' + str(abs(self.param['rate']))+' kg/s'
else:prntStr += 'Mass injection of '+str(self.param['energy'])+' MJ/kg fluid against specified WHP of ' + str(abs(self.param['rate']))+' MPa'
else:
if self.param['impedance']==0: prntStr += 'Mass injection of '+str(abs(self.param['energy']))+' degC fluid at fixed rate of ' + str(abs(self.param['rate']))+' kg/s'
else:prntStr += 'Mass injection of '+str(abs(self.param['energy']))+' degC fluid against specified WHP of ' + str(abs(self.param['rate']))+' MPa'
return prntStr
def _more_info_stressboun(self):
'''Return additional information about stressboun macro.'''
prntStr = ''
strsDirs = dict([(1,'x-dir'),(2,'y-dir'),(3,'z-dir')])
if self.subtype=='lithograd':
prntStr += 'lithograd, '
prntStr += strsDirs[abs(self.param['direction'])]+', '
prntStr += 'stress grad = ' + str(self.param['value']) + ' MPa/m'
elif self.subtype == 'distributed':
prntStr += self.subtype +' force, '
prntStr += strsDirs[abs(self.param['direction'])]+', '
prntStr += str(self.param['value'])+ ' MPa'
elif self.subtype == 'lithostatic':
prntStr +='not done!'
else:
if self.param['direction']>0: prntStr += 'fixed disp, '
else: prntStr += 'fixed force, '
prntStr += strsDirs[abs(self.param['direction'])]+', '
prntStr += str(self.param['value'])+' '
if self.param['direction']>0: prntStr += 'm'
else: prntStr += 'MPa'
return prntStr
[docs]class fmodel(object): #FEHM model object.
'''Model object, used in a variety of macro definitions.
Model objects should have:
- a type for the macro
- a list of zones to which the model is assigned.
- an index for the specific model.
- a dictionary of parameters for the model.
'''
__slots__=['_silent','_type','_param','_index','_parent','_zonelist','_zone','_file']
def __init__(self,type='',zonelist=[],param=[],index = None,file = None):
self._type = type
self._silent = dflt.silent
self._param = None
self._check_type()
self._index = None
if index: self._index = index
self._assign_param()
self._parent = None
# check param hasn't been misspecified
if len(param) ==2:
if not isinstance(param[0],tuple): param = (param,)
if param: self._set_param2(param)
self._zonelist = []
if zonelist != []: self.zonelist = zonelist
self._zone = {}
self._check_zone()
self._file = file
def _check_type(self):
'''Determine if macro is supported.'''
if self.type not in model_list.keys(): return None
def _assign_param(self):
'''Assign parameters if supplied on initialisation.'''
if self.index in model_list[self.type].keys():
self._param = dict([(par,None) for par in model_list[self.type][self.index]])
else:
self._param = {}
def _set_param2(self,param):
'''Assign keys in param attribute appropriate to specific macro.'''
if self.index not in model_list[self.type].keys():
self._param = dict([('param'+str(i+1),par[1]) for i,par in enumerate(param)])
return
elif param.__len__() != len(self._param.keys()):
return # return if numbers don't match up
if self.index in model_list[self.type].keys():
paramDict = model_list[self.type][self.index]
else:
paramDict = ['param'+str(i+1) for i in range(len(self._param.keys()))]
for par,key in zip(param,paramDict):
if isinstance(par,list) or isinstance(par,tuple):
self._param[par[0]] = par[1]
else:
self._param[key] = par
def _check_zone(self):
'''Determine if zone definitions are acceptable.'''
if not self.zonelist: return
elif isinstance(self.zonelist,(fzone,int,str)): self.zonelist = [self.zonelist]
elif isinstance(self.zonelist,tuple):
if len(self.zonelist) != 3: self.zonelist=None; return
self.zonelist = [tuple([int(pt) for pt in self.zonelist])]
elif isinstance(self.zonelist,list):
newlist = []
for zn in self.zonelist:
if isinstance(zn,(fzone,int,str)) or (isinstance(zn,tuple) and len(zn) == 3): newlist.append(zn)
self.zonelist = newlist
else: self.zonelist=None; return
if self._parent:
newlist = []
for zn in self.zonelist:
if zn in self._parent.zone.keys():
newlist.append(self._parent.zone[zn])
self.zonelist = newlist
def __repr__(self):
prntStr = self.type +': '
if isinstance(self.zonelist,fzone): prntStr += str(self.zonelist.index)
elif isinstance(self.zonelist,list):
for zn in self.zonelist: prntStr += str(self.zonelist.index)+ ' ,'
prntStr = prntStr[:-2]
elif isinstance(self.zonelist,tuple):
zn = self.zonelist
prntStr += '('+str(zn[0]) +', ' +str(zn[1]) +', '+str(zn[2]) +')'
return prntStr
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
def _get_type(self): return self._type
def _set_type(self,value):
oldtype = self._type
self._type = type
if oldtype != type:
param_dicts = model_list[self._type]
if self.index not in param_dicts:
pyfehm_print('ERROR: model index not known.',self._silent)
return
self.param = param_dicts[self.index]
type = property(_get_type, _set_type) #: (*str*) Name of the macro for this model. Macro names are identical to those invoked in FEHM.
def _get_param(self): return self._param
def _set_param(self,value): self._param = value
param = property(_get_param, _set_param) #: (*dict[fl64]*) A dictionary of values defining the operation of the macro. See table below for macro-specific dictionary keys.
def _get_index(self): return self._index
def _set_index(self,value): self._index = value
index = property(_get_index, _set_index) #: (*int*) Index of the model to be invoked.
def _get_zonelist(self): return self._zonelist
def _set_zonelist(self,value): self._zonelist = value
zonelist = property(_get_zonelist, _set_zonelist) #: (*list*) A list of zones to which the model is applied.
def _get_zone(self):
tempdict = []
for zn in self.zonelist:
if isinstance(zn,tuple):
tempdict.append([tuple,tuple])
elif isinstance(zn,fzone):
tempdict.append([zn.index,zn])
return dict(tempdict)
zone = property(_get_zone) #: (**)
def _get_file(self): return self._file
def _set_file(self,value): self._file = value
file = property(_get_file, _set_file) #: (**)
[docs]class fincon(object): #FEHM restart object.
'''Initial conditions object. Also called a restart file.
Reading one of these files associates the temperature, pressure, saturation etc. data with grid nodes
and sets up fehmn.files to use the file for restarting.
'''
__slots__=['_silent','_source','_parent','_time','_changeTime','_writeOut','_stressgradCalled','_T','_P','_S',
'_co2aq','_eos','_co2_eos','_dc_eos','_S_co2l','_strs_xx','_strs_yy','_strs_zz','_strs_xy',
'_strs_yz','_strs_xz','_disp_x','_disp_y','_disp_z','_path']
def __init__(self,inconfilename=''):
self._source = ''
self._silent = dflt.silent
self._parent = None
self._time = None
self._changeTime = False
self._writeOut = False # flag that changes have been made and the incon file should be rewritten
self._stressgradCalled = False
self._T = []
self._P = []
self._S = []
self._co2aq = []
self._eos = []
self._co2_eos = []
self._dc_eos = []
self._S_co2l = []
self._strs_xx = []
self._strs_yy = []
self._strs_zz = []
self._strs_xy = []
self._strs_yz = []
self._strs_xz = []
self._disp_x = []
self._disp_y = []
self._disp_z = []
self._path = fpath(parent=self)
if inconfilename: self._path.filename = inconfilename
if self.filename: self.read()
def __repr__(self):
if self.filename == None:
return 'no initial conditions'
else:
return self.filename #Print out details
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
[docs] def read(self,inconfilename='',if_new = False):
'''Parse a restart file for variable information.
:param inconfilename: Name of restart file.
:type inconfilename: str
'''
if inconfilename: self._path.filename = inconfilename
if if_new and not os.path.isfile(self._path.full_path): return False
# check if file exists, if not found, check in work directory
infile = open(self._path.full_path,'r')
self._parent.files.incon = inconfilename
lns = infile.readlines()
infile.close()
# check if incon file finished writing
if not (lns[-1].startswith('no fluxes') or lns[-2].startswith('no fluxes') or lns[-3].startswith('no fluxes')):
return
cnt = 0
ln = lns[cnt].strip(); cnt +=1
ln = lns[cnt].strip(); cnt +=1
self._source = ln
ln = lns[cnt].strip(); cnt +=1
new_time = float(ln)
if (new_time == self.time) and if_new:
return False
self.time = float(ln) # get time stamp
self._changeTime = False
ln = lns[cnt].strip(); cnt +=1
node_number = int(ln.split('nddp')[0])
while True:
var = lns[cnt]; cnt +=1
if var.startswith('no fluxes') or var == -1: break
# read in data
values = []
while len(values) != node_number:
values += lns[cnt].strip().split(); cnt +=1
# save to attribute
if var.startswith('temperature') or var.startswith('co2temperat'):
self._T = np.array([float(v) for v in values])
if var.startswith('pressure') or var.startswith('co2pressure'):
self._P = np.array([float(v) for v in values])
if var.startswith('saturation') or var.startswith('wsaturation'):
self._S = np.array([float(v) for v in values])
if var.startswith('lco2saturat'):
self._S_co2l = np.array([float(v) for v in values])
if var.startswith('dissolvdco2'):
self._co2aq = np.array([float(v) for v in values])
if var.startswith('eoswater'):
self._eos = np.array([int(v) for v in values])
if var.startswith('eosco2'):
self._co2_eos = np.array([int(v) for v in values])
if var.startswith('eosdc'):
self._dc_eos = np.array([int(v) for v in values])
if var.startswith('xstress'):
self._strs_xx = np.array([float(v) for v in values])
if var.startswith('ystress'):
self._strs_yy = np.array([float(v) for v in values])
if var.startswith('zstress'):
self._strs_zz = np.array([float(v) for v in values])
if var.startswith('xystress'):
self._strs_xy = np.array([float(v) for v in values])
if var.startswith('xzstress'):
self._strs_xz = np.array([float(v) for v in values])
if var.startswith('yzstress'):
self._strs_yz = np.array([float(v) for v in values])
if var.startswith('xdisplacmnt'):
self._disp_x = np.array([float(v) for v in values])
if var.startswith('ydisplacmnt'):
self._disp_y = np.array([float(v) for v in values])
if var.startswith('zdisplacmnt'):
self._disp_z = np.array([float(v) for v in values])
infile.close()
if self._parent: self._parent._associate_incon()
if if_new: return True
[docs] def write(self,inconfilename=''):
'''Write out a restart file.
:param inconfilename: Name of restart file to write out.
:type inconfilename: str
'''
if inconfilename:
self._path.filename = inconfilename
if self._parent.work_dir:
path = self._path.absolute_to_workdir+os.sep+self._path.filename
else:
path = self._path.full_path
outfile = open(path,'w')
self._parent.files.incon = path
self._path.filename = path
pyfehm_print('Writing new INCON file '+inconfilename+'.',self._silent)
# write headers
outfile.write('PyFEHM V1.0 ')
import time
lt = time.localtime()
mon = str(lt.tm_mon)
if len(mon) == 1: mon = '0'+mon
day = str(lt.tm_mday)
if len(day) == 1: day = '0'+day
yr = str(lt.tm_year)
hr = str(lt.tm_hour)
if len(hr) == 1: hr = '0'+hr
min = str(lt.tm_min)
sec = str(lt.tm_sec)
outfile.write(mon+'/'+day+'/'+yr+' '+hr+':'+min+':'+sec+'\n')
outfile.write(self.source+'\n')
outfile.write(' '+'%20f' % self.time+'\n')
outfile.write(' '+'%8d' % len(self.T)+' nddp\n')
# write info
co2flag = (len(self._co2aq) != 0)
stressflag = (len(self._strs_xx) != 0)
#if self._parent and co2flag:
# if self._parent.carb.iprtype == 1: co2flag = False
if self._parent and stressflag:
if self._parent.strs.param['ISTRS']==0: stressflag = False
headers = ['temperature','saturation','pressure']
variables = [self.T,self.S,self.P]
formats = ['%#20.10e','%#20.10e','%#20.10e']
Ns = [4,4,4]
nan_subs = [25.,1.,1.]
if co2flag:
headers = ['co2temperat','wsaturation','co2pressure','lco2saturat','dissolvdco2',
'eoswater','eosco2','eosdc']
variables+=[self._S_co2l,self._co2aq,self._eos,self._co2_eos,self._dc_eos]
formats += ['%#20.10e','%#20.10e','%1d','%1d','%1d']
Ns += [4,4,30,30,30]
nan_subs += [0.,0.,1,1,0]
if stressflag:
headers += ['xdisplacmnt','ydisplacmnt','zdisplacmnt','xstress','ystress','xystress',
'zstress','xzstress','yzstress']
variables += [self._disp_x,self._disp_y,self._disp_z,self._strs_xx,self._strs_yy,
self._strs_xy,self._strs_zz,self._strs_xz,self._strs_yz]
formats += ['%#20.10e','%#20.10e','%#20.10e','%#20.10e','%#20.10e','%#20.10e','%#20.10e',
'%#20.10e','%#20.10e']
Ns += [4,4,4,4,4,4,4,4,4]
nan_subs += [0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.]
for header,variable,format,N,nan_sub in zip(headers,variables,formats,Ns,nan_subs):
if (len(variable) != 0):
outfile.write(header+'\n')
cnt = 0
for val in variable:
if val != val: val = nan_sub
if N != 30 and not (header.endswith('ess') or header.endswith('displacmnt')): val = np.max([val,1.e-98])
outfile.write(format % val + ' ')
cnt +=1
if cnt == N: outfile.write('\n'); cnt = 0
if cnt!=0: outfile.write('\n')
outfile.write('no fluxes\n\n')
outfile.close()
self._writeOut = False
[docs] def stressgrad(self,xgrad,ygrad,zgrad,xygrad = 0.,xzgrad=0.,yzgrad=0.,calculate_vertical=False,vertical_fraction=False):
'''Construct initial stress state with vertical stress gradients.
:param xgrad: Vertical gradient in x stress (MPa/m), assumed intercept at [0,0]. If a two element list is given, the first value is interpreted as the gradient, and the second value as the elevation where stress is zero (i.e., the intercept with the z-axis).
:type xgrad: fl64, list[fl64,fl64]
:param ygrad: Vertical gradient in y stress (MPa/m), format as for **xgrad**.
:type ygrad: fl64, list[fl64,fl64]
:param zgrad: Vertical gradient in z stress (MPa/m), format as for **xgrad**.
:type zgrad: fl64, list[fl64,fl64]
:param xygrad: Vertical gradient in xy stress (MPa/m), default is 0, format as for **xgrad**.
:type xygrad: fl64, list[fl64,fl64]
:param xzgrad: Vertical gradient in xz stress (MPa/m), default is 0, format as for **xgrad**.
:type xzgrad: fl64, list[fl64,fl64]
:param yzgrad: Vertical gradient in yz stress (MPa/m), default is 0, format as for **xgrad**.
:type yzgrad: fl64, list[fl64,fl64]
:param calculate_vertical: Vertical stress calculated by integrating density. If true, then zgrad specifies the overburden.
:type calculate_vertical: bool
:param vertical_fraction: Horizontal stresses calculated as a fraction of the vertical. If true, xgrad and ygrad are interpreted as fractions.
:type vertical_fraction: bool
'''
if not self.filename:
pyfehm_print('ERROR: initial conditions file containing temperature/pressure data not loaded.',self._silent)
return
if not self._parent._associate:
pyfehm_print('ERROR: incon file not associated with parent data file - node and coordinate data not available.',self._silent)
return
if vertical_fraction: calculate_vertical = True
if calculate_vertical:
pyfehm_print('NOTE: density integration to obtain vertical stress should only be done for orthogonal meshes',self._silent)
if not self._parent._associate:
pyfehm_print('ERROR: node property association required to access density data - set associate=True in data file',self._silent)
return
xs = np.unique([nd.position[0] for nd in self._parent.grid.nodelist])
ys = np.unique([nd.position[1] for nd in self._parent.grid.nodelist])
# for each x and y, find the column of z-values corresponding
zs = []
for x in xs:
for y in ys:
zs.append(((x,y),[]))
zs = dict(zs)
for nd in self._parent.grid.nodelist:
zs[(nd.position[0],nd.position[1])].append(nd)
self._strs_zz = np.zeros((1,self._parent.grid.number_nodes))[0]
for k in zs.keys():
zs[k].sort(key=lambda x: x.position[2],reverse=True)
z0 = zs[k][0].position[2]; sz = [0+zgrad]
oldRho = zs[k][0].density
oldZ = zs[k][0].position[2]
for z in zs[k][1:]:
sz.append(sz[-1]+9.81*(oldRho+z.density)*abs(z.position[2]-oldZ)/2/1e6)
oldRho = z.density
oldZ = z.position[2]
for nd,szi in zip(zs[k],sz):
self._strs_zz[nd.index-1] = szi
if vertical_fraction:
if isinstance(xgrad,(list,tuple)) and len(xgrad) == 2:
self._strs_xx = list(xgrad[0]*np.array(self.strs_zz)+xgrad[1])
else: self._strs_xx = list(xgrad*np.array(self.strs_zz))
if isinstance(ygrad,(list,tuple)) and len(ygrad) == 2:
self._strs_yy = list(ygrad[0]*np.array(self.strs_zz)+ygrad[1])
else: self._strs_yy = list(ygrad*np.array(self.strs_zz))
if isinstance(xygrad,(list,tuple)) and len(xygrad) == 2:
self._strs_xy = list(xygrad[0]*np.array(self.strs_zz)+xygrad[1])
else: self._strs_xy = list(xygrad*np.array(self.strs_zz))
if isinstance(xzgrad,(list,tuple)) and len(xzgrad) == 2:
self._strs_xz = list(xzgrad[0]*np.array(self.strs_zz)+xzgrad[1])
else: self._strs_xz = list(xzgrad*np.array(self.strs_zz))
if isinstance(yzgrad,(list,tuple)) and len(yzgrad) == 2:
self._strs_yz = list(yzgrad[0]*np.array(self.strs_zz)+yzgrad[1])
else: self._strs_yz = list(yzgrad*np.array(self.strs_zz))
else:
if isinstance(xgrad,(tuple,list)): dx = abs(xgrad[0]); x0 = xgrad[1]
else: dx = abs(xgrad); x0 = 0
if isinstance(ygrad,(tuple,list)): dy = abs(ygrad[0]); y0 = ygrad[1]
else: dy = abs(ygrad); y0 = 0
if isinstance(xygrad,(tuple,list)): dxy = abs(xygrad[0]); xy0 = xygrad[1]
else: dxy = abs(xygrad); xy0 = 0
if isinstance(xzgrad,(tuple,list)): dxz = abs(xzgrad[0]); xz0 = xzgrad[1]
else: dxz = abs(xzgrad); xz0 = 0
if isinstance(yzgrad,(tuple,list)): dyz = abs(yzgrad[0]); yz0 = yzgrad[1]
else: dyz = abs(yzgrad); yz0 = 0
z = np.array([nd.position[2] for nd in self._parent.grid.nodelist])
self._strs_xx = -dx*(z-x0)
self._strs_yy = -dy*(z-y0)
self._strs_xy = -dxy*(z-xy0)
self._strs_xz = -dxz*(z-xz0)
self._strs_yz = -dyz*(z-yz0)
else:
if isinstance(xgrad,(tuple,list)): dx = abs(xgrad[0]); x0 = xgrad[1]
else: dx = abs(xgrad); x0 = 0
if isinstance(ygrad,(tuple,list)): dy = abs(ygrad[0]); y0 = ygrad[1]
else: dy = abs(ygrad); y0 = 0
if isinstance(zgrad,(tuple,list)): dz = abs(zgrad[0]); z0 = zgrad[1]
else: dz = abs(zgrad); z0 = 0
if isinstance(xygrad,(tuple,list)): dxy = abs(xygrad[0]); xy0 = xygrad[1]
else: dxy = abs(xygrad); xy0 = 0
if isinstance(xzgrad,(tuple,list)): dxz = abs(xzgrad[0]); xz0 = xzgrad[1]
else: dxz = abs(xzgrad); xz0 = 0
if isinstance(yzgrad,(tuple,list)): dyz = abs(yzgrad[0]); yz0 = yzgrad[1]
else: dyz = abs(yzgrad); yz0 = 0
z = np.array([nd.position[2] for nd in self._parent.grid.nodelist])
self._strs_xx = -dx*(z-x0)
self._strs_yy = -dy*(z-y0)
self._strs_zz = -dz*(z-z0)
self._strs_xy = -dxy*(z-xy0)
self._strs_xz = -dxz*(z-xz0)
self._strs_yz = -dyz*(z-yz0)
self._writeOut = True
self._stressgradCalled = True
[docs] def critical_stress(self,regime=1,horiz_stress='x',mu=0.6,cohesion=0,proximity=0.,overburden=0.):
'''Construct initial stress state near Mohr-Coulomb failure. The vertical stress is calculated
using the assigned density. Minimum or maximum horizontal stress calculated using the specified
friction coefficient. Intermediate principal stress is assumed to be the average of the other two.
:param regime: Stress regime, 0 = compression, 1 = extension (default).
:type regime: bool int
:param horiz_stress: Horizontal coordinate direction ('x' or 'y') to assign the minimum or maximum principal stress (depending on stress regime).
:type horiz_stress: str
:param mu: Friction coefficient for Mohr-Coulomb failure (default = 0.6).
:type mu: fl64
:param cohesion: Cohesion for Mohr-Coulomb failure (default = 0).
:type cohesion: fl64
:param proximity: A negative quantity indicating how close the minimum principal stress is to Mohr-Coulomb failure (MPa, default = 0).
:type proximity: fl64
:param overburden: Overburden at top of model (MPa, default = 0).
:type overburden: fl64
'''
mult = np.sqrt(1+mu**2)+mu
self.stressgrad(xgrad=1.,ygrad=1.,zgrad=overburden,calculate_vertical = True)
if regime:
if horiz_stress == 'x':
self._strs_xx = list((np.array(self.strs_zz)-2*cohesion*mult)/mult**2-proximity)
self._strs_yy = list((np.array(self.strs_xx)+np.array(self.strs_zz))/2)
else:
self._strs_yy = list((np.array(self.strs_zz)-2*cohesion*mult)/mult**2-proximity)
self._strs_xx = list((np.array(self.strs_yy)+np.array(self.strs_zz))/2)
else:
if horiz_stress == 'x':
self._strs_xx = list(2*cohesion*mult+mult**2*np.array(self.strs_zz)-proximity)
self._strs_yy = list((np.array(self.strs_xx)+np.array(self.strs_zz))/2)
else:
self._strs_yy = list(2*cohesion*mult+mult**2*np.array(self.strs_zz)-proximity)
self._strs_xx = list((np.array(self.strs_yy)+np.array(self.strs_zz))/2)
def _summary(self):
L = 62
s = ['']
s.append(' IIII---------------------------------------------------------IIII')
line = ' IIII FEHM restart file \''+self.filename+'\' summary.'
for i in range(L-len(line)): line += ' '
s.append(line+'IIII')
s.append(' IIII---------------------------------------------------------IIII')
lines = []
lines.append(' IIII Restart parameters:')
if len(self.P): lines.append(' Heat and mass: pressure, temperature, saturation.')
if len(self.S_co2l): lines.append(' CO2 module: CO2 saturations, dissolve mass, EOS.')
if len(self.strs_xx): lines.append(' Stress module: normal and shear stresses, displacements.')
for line in lines:
if line.startswith(' II'):
for i in range(L-len(line)): line += ' '
s.append(line+'IIII')
else:
prntStr = ' IIII -'
keepGoing = True
line = line.split()
while keepGoing:
if not line:
for i in range(L-len(prntStr)): prntStr += ' '
s.append(prntStr+'IIII')
prntStr = ' IIII '
break
if len(prntStr)<(L-len(line[0])):
prntStr += ' '+line[0]
line = line[1:]
else:
for i in range(L-len(prntStr)): prntStr += ' '
s.append(prntStr+'IIII')
prntStr = ' IIII '
s.append(' IIII---------------------------------------------------------IIII')
s.append('')
s = '\n'.join(s)
pyfehm_print(s,self._silent)
def _get_filename(self): return self._path.filename
filename = property(_get_filename) #: (*str*) Name of restart file (initial conditions)
def _get_time(self): return self._time
def _set_time(self,value): self._time = value; self._changeTime = True
time = property(_get_time,_set_time) #: (*fl64*) Time stamp of initial conditions file (end time of simulation that produced this file).
def _get_T(self): return self._T
T = property(_get_T) #: (*lst[fl64]*) Initial node temperatures, ordered by node index.
def _get_P(self): return self._P
P = property(_get_P)#: (*lst[fl64]*) Initial node pressures, ordered by node index.
def _get_S(self): return self._S
S = property(_get_S)#: (*lst[fl64]*) Initial node water saturations, ordered by node index.
def _get_eos(self): return self._eos
eos = property(_get_eos)#: (*lst[fl64]*) Initial node water equation of state indices, ordered by node index.
def _get_co2_eos(self): return self._co2_eos
co2_eos = property(_get_co2_eos)#: (*lst[fl64]*) Initial node co2 equation of state indices, ordered by node index.
def _get_S_co2l(self): return self._S_co2l
S_co2l = property(_get_S_co2l)#: (*lst[fl64]*) Initial node co2 liquid/super-critical saturations, ordered by node index.
def _get_S_co2g(self):
if self.S_co2l == []: return []
else: return 1-self.S_co2l-self.S
S_co2g = property(_get_S_co2g)#: (*lst[fl64]*) Initial node co2 gas saturations, ordered by node index.
def _get_co2aq(self): return self._co2aq
co2aq = property(_get_co2aq)#: (*lst[fl64]*) Initial node dissolved co2 concentrations, ordered by node index.
def _get_disp_x(self): return self._disp_x
disp_x = property(_get_disp_x) #: (*lst[fl64]*) Initial node x displacement, ordered by node index
def _get_disp_y(self): return self._disp_y
disp_y = property(_get_disp_y) #: (*lst[fl64]*) Initial node y displacement, ordered by node index
def _get_disp_z(self): return self._disp_z
disp_z = property(_get_disp_z) #: (*lst[fl64]*) Initial node z displacement, ordered by node index
def _get_strs_xx(self): return self._strs_xx
strs_xx = property(_get_strs_xx) #: (*lst[fl64]*) Initial node x stress, ordered by node index
def _get_strs_yy(self): return self._strs_yy
strs_yy = property(_get_strs_yy) #: (*lst[fl64]*) Initial node y stress, ordered by node index
def _get_strs_zz(self): return self._strs_zz
strs_zz = property(_get_strs_zz) #: (*lst[fl64]*) Initial node z stress, ordered by node index
def _get_strs_xy(self): return self._strs_xy
strs_xy = property(_get_strs_xy) #: (*lst[fl64]*) Initial node xy stress, ordered by node index
def _get_strs_xz(self): return self._strs_xz
strs_xz = property(_get_strs_xz) #: (*lst[fl64]*) Initial node xz stress, ordered by node index
def _get_strs_yz(self): return self._strs_yz
strs_yz = property(_get_strs_yz) #: (*lst[fl64]*) Initial node yz stress, ordered by node index
def _get_source(self): return self._source
source = property(_get_source) #: (*str*) Name of input file that generated the restart.
[docs]class fstrs(object): #FEHM stress module.
"""Stress module object, sets properties for execution of FEHM stress module (see macro **STRS**).
"""
__slots__=['_silent','_initcalc','_fem','_parent','_bodyforce','_tolerance','_param','_excess_she']
def __init__(self,initcalc=True,fem=True,bodyforce=True,tolerance=-0.01,param={},parent=None):
self._initcalc=initcalc
self._silent = dflt.silent
self._fem=fem
self._parent = parent
self._bodyforce=bodyforce
self._tolerance=tolerance
self._param={}
self._param['IHMS']=-3
self._param['ISTRS']=0
self._param['porosity_factor']=None
self._excess_she = {}
self._excess_she['PAR1']=None
self._excess_she['PAR2']=None
self._excess_she['PAR3']=None
if param: self._param = param
def __repr__(self):
if not self.param['ISTRS']: return 'stress module inactive'
else: return 'stress module active'
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
[docs] def on(self):
"""Set parameters to turn stress calculations ON.
"""
self.param['ISTRS']=1
self._parent.ctrl['stor_file_LDA']=0 # seg fault when using stress module without this set
[docs] def off(self):
"""Set param to turn stress calculations OFF.
"""
self._parent.sol['element_integration_INTG']=-1
self.param['ISTRS']=0
def _get_info(self):
if self.param['ISTRS']: print 'Stress module activated.'
else: print 'Stress module inactive'; return
if self.bodyforce: print 'Body forces (gravity) will be calculated.'
else: print 'Body forces (gravity) will NOT be calculated.'
if self.initcalc: print 'Initial stress state will be calculated.'
else: print 'Initial stress state will NOT be calculated.'
what = property(_get_info)#: Return a summary of the stress module.
def _get_bodyforce(self): return self._bodyforce
def _set_bodyforce(self,value):
if not(isinstance(value,bool) or value in [0,1]): print 'Boolean values only'; return
if isinstance(value,int):
if value == 1: value = True
elif value == 0: value = False
self._bodyforce = value
bodyforce = property(_get_bodyforce,_set_bodyforce)#: (*bool*) Boolean calling for body force calculations (gravity). Default is True.
def _get_initcalc(self): return self._initcalc
def _set_initcalc(self,value):
if not(isinstance(value,bool) or value in [0,1]): print 'Boolean values only'; return
if isinstance(value,int):
if value == 1: value = True
elif value == 0: value = False
self._initcalc = value
initcalc = property(_get_initcalc,_set_initcalc)#: (*bool*) Boolean signalling if initial stress calculations should be performed. Default is True.
def _get_fem(self): return self._fem
def _set_fem(self,value):
if not(isinstance(value,bool) or value in [0,1]): print 'Boolean values only'; return
if isinstance(value,int):
if value == 1: value = True
elif value == 0: value = False
self._fem = value
fem = property(_get_fem,_set_fem)#: (*bool*) Boolean signalling use of finite element modules for calculating stress and displacement. Default is True.
def _get_tolerance(self): return self._tolerance
def _set_tolerance(self,value): self._tolerance=value
tolerance = property(_get_tolerance,_set_tolerance)#: (*flt*) Tolerance of stress calculations.
def _get_param(self): return self._param
def _set_param(self,value): self._param = value
param = property(_get_param, _set_param) #: (*dict[flt]*) Dictionary of stress parameters: 'IHMS' - coupling parameter, 'ISTRS' - type of stress solution.
def _get_excess_she(self): return self._excess_she
def _set_excess_she(self,value): self._excess_she = value
excess_she = property(_get_excess_she, _set_excess_she) #: Dictionary of excess shear parameters:
class fngas(object): #FEHM noncondensible gas transport.
"""Module for noncondensible gas transport (see macro **NGAS**).
"""
__slots__=['_silent','_parent','_dof','_init_pres','_ncg_pres','_source']
def __init__(self, parent=None, dof = None):
self._parent=parent
self._silent = dflt.silent
self._dof = dof
self._init_pres = {}
self._ncg_pres = {}
self._source = {}
def __repr__(self):
if self.dof == None: return 'ncg module inactive'
else: return 'ncg module active'
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
def add_init_pres(self,zone,init_pres):
if isinstance(zone,tuple): key = zone
else: key = zone.index
self.init_pres.update(dict(((key,init_pres),)))
def add_ncg_pres(self,zone,ncg_pres):
if isinstance(zone,tuple): key = zone
else: key = zone.index
self.ncg_pres.update(dict(((key,ncg_pres),)))
def add_source(self,zone,source):
if isinstance(zone,tuple): key = zone
else: key = zone.index
self.source.update(dict(((key,source),)))
def _get_init_pres(self): return self._init_pres
init_pres = property(_get_init_pres) #: (*dict*) dictionary of initial partial pressures indexed by zone number.
def _get_ncg_pres(self): return self._ncg_pres
ncg_pres = property(_get_ncg_pres) #: (*dict*) dictionary of noncondensible pressures indexed by zone number.
def _get_source(self): return self._source
source = property(_get_source) #: (*dict*) dictionary of air sources indexed by zone number.
def _get_dof(self): return self._dof
def _set_dof(self,value): self._dof = value
dof = property(_get_dof, _set_dof) #: (*int*) degree of freedom (1-3) for the calculation
[docs]class fcarb(object): #FEHM CO2 module.
"""CO2 module object, sets properties for execution of FEHM CO2 module (see macro **CARB**).
"""
__slots__=['_silent','_iprtype','_brine','_parent']
def __init__(self,iprtype=0,brine=False,parent=None):
self._iprtype = iprtype
self._silent = dflt.silent
self._brine = brine
self._parent =parent
def __repr__(self):
if self.iprtype==0: return 'CO2 module inactive'
else: return 'CO2 module active'
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
[docs] def on(self,iprtype=3):
"""Set parameters to turn CO2 calculations ON.
:param iprtype: Integer denoting type of simulation (1 = water only, 2 = CO2 only, 3 = water+CO2, no solubility, 4 = water+CO2, with solubility, 5 = water+CO2+air, with solubility)
:type iprtype: int
"""
self.iprtype = iprtype
[docs] def off(self):
"""Set parameters to turn CO2 calculations OFF.
"""
self.iprtype = 0
self._parent.files._use_co2in = False
def _get_info(self):
if self.iprtype != 0: print 'CO2 module activated.'
else: print 'CO2 module inactive'; return
prntStr = 'Components:'
if self.iprtype == 1: prntStr += 'Water only'
elif self.iprtype == 2: prntStr += 'CO2 only'
elif self.iprtype == 3:prntStr += 'CO2, water (no solubility)'
elif self.iprtype == 4:prntStr += 'CO2, water (with solubility)'
elif self.iprtype == 5:prntStr += 'CO2, water, air (with solubility)'
print prntStr
if self.brine: print 'CO2 solubility dependent on brine concentration.'
else: print 'CO2 solubility NOT dependent on brine concentration.'
what = property(_get_info)#: Return a summary of the CO2 module.
def _get_iprtype(self): return self._iprtype
def _set_iprtype(self,value): self._iprtype = value
iprtype = property(_get_iprtype, _set_iprtype) #: (*int*) Integer indicating type of simulation.
def _get_brine(self): return self._brine
def _set_brine(self,value): self._brine = value
brine = property(_get_brine, _set_brine) #: (*bool*) Boolean signalling calculation of brine in simulation.
[docs]class ftrac(object): #FEHM chemistry module.
"""Chemistry module object, sets properties for execution of FEHM chemistry module (see macro **TRAC**).
"""
def __init__(self, parent=None, ldsp=False):
self._param=copy(dflt.trac)
self._silent = dflt.silent
self._on=False
self._ldsp = ldsp
self._specieslist = []
self._common_modellist = []
self._common_model_key = None
self._transport_porosity = -1
self._parent = parent
self._file = None
self._zonelist = []
def on(self):
"""Switches on the **TRAC** macro. This occurs automatically when the first species object is added.
"""
self._on = True
def off(self):
"""Switches off the **TRAC** macro. This occurs automatically when the las species object is deleted.
"""
self._on = False
def add_species(self,phase,adsorption_model=0,adsorption=[],diffusion_model=0,diffusion=1.e-9,
dispersion = [.0005,.0005,.0005],species=None):
"""Add a new species to **TRAC**. The new species is accessible via the last item in the specieslist attribute.
:param phase: Flag indicating the phase of the species.
:type phase: int
:param adsorption_model: Flag for desired adsorption model (default 0).
:type adsorption_model: int
:param adsorption: Adsorption model parameters - three element list.
:type adsorption: lst[fl64]
:param diffusion_model: Flag for diffusion model (default 0).
:type diffusion_model: int
:param diffusion: Diffusion coefficien (default 1.e-9).
:type diffusion: fl64
:param dispersion: Three item list containing X,Y,Z dispersion coefficients. If the ldsp attribute is set to true, the first two entries are interpreted as longitudinal and transverse dispersion, and the third is ignored.
:type dispersion: lst[fl64]
:param species: Pre-defined fspecies object.
:type species: fspecies
"""
if not species:
species = fspecies(phase = phase,adsorption_model=adsorption_model, adsorption=adsorption,
diffusion_model=diffusion_model, diffusion=diffusion,dispersion=dispersion)
species._parent = self
self._specieslist.append(species)
if self.number_species == 1: self._on = True # only switch on if first species added
def delete_species(self,species):
"""Delete a species from ftrac.
:param species: Species object to be removed.
:type species: fspecies
"""
self._specieslist.remove(species)
if self.number_species == 0: self._on = False # switch off if no species remain
def add_common_model(self, zone = None, diffusion_model = 0, diffusion = 1.e-9, dispersion = [0.005,0.005,0.005]):
"""Add a new common dispersion/diffusion model for multiple species (see GROUP 9 entry of macro **TRAC** in FEHM user manual).
:param zone: Zone to which these parameters are applied.
:type zone: int, str, fzone
:param diffusion_model: Flag for desired diffusion model (default 0).
:type diffusion_model: int
:param diffusion: Diffusion coefficien (default 1.e-9).
:type diffusion: fl64
:param dispersion: Three item list containing X,Y,Z dispersion coefficients. If the ldsp attribute is set to true, the first two entries are interpreted as longitudinal and transverse dispersion, and the third is ignored.
:type dispersion: lst[fl64]
"""
cm = _common_model(zone = zone, diffusion = diffusion, dispersion = dispersion, diffusion_model = diffusion_model)
if isinstance(cm.zone,int) or isinstance(cm.zone,str):
if cm.zone in self._parent.zone.keys(): cm.zone = self._parent.zone[cm.zone]
self._common_modellist.append(cm)
def _get_number_species(self): return len(self._specieslist)
number_species = property(_get_number_species) #: (*int*) Number of species for which transport properties have been defined.
def _get_ldsp(self): return self._ldsp
def _set_ldsp(self,value): self._ldsp = value
ldsp = property(_get_ldsp,_set_ldsp) #: (*bool*) Boolean signalling longitudinal/transverse description of dispersivities to be used.
def _get_common_modellist(self): return self._common_modellist
def _set_common_modellist(self,value): self._common_modellist = value
common_modellist = property(_get_common_modellist,_set_common_modellist) #: (*lst*) List of common model definitions.
def _get_common_model(self):
tempDict = []
for cm in self._common_modellist:
if isinstance(cm.zone,list):
tempDict.append((tuple(cm.zone),cm))
elif isinstance(cm.zone,int):
tempDict.append((cm.zone,cm))
return dict(tempDict)
common_model = property(_get_common_model) #: (*dict*) Dictionary of common models.
def _get_param(self): return self._param
def _set_param(self,value): self._param = value
param = property(_get_param,_set_param) #: (*dict*) Dictionary of **TRAC** parameters.
def _get_transport_porosity(self): return self._transport_porosity
def _set_transport_porosity(self,value): self._transport_porosity = value
transport_porosity = property(_get_transport_porosity,_set_transport_porosity) #: (*fl64*) Transport porosity for entire domain (zone by zone not supported).
def _get_specieslist(self): return self._specieslist
def _set_specieslist(self,value): self._specieslist = value
specieslist = property(_get_specieslist,_set_specieslist) #: (*lst*) List of species objects.
def _get_file(self): return self._file
def _set_file(self,value):
self._file = value
self.on() # if set, turn on trac macro
file = property(_get_file, _set_file) #: (*str*) Path of auxiliary file containing trac information. PyFEHM will copy the contents of this file into the input file verbatim. Setting this variable will cause PyFEHM to use trac in 'stupid' mode.
def _get_zonelist(self): return self._zonelist
def _set_zonelist(self,value): self._zonelist = value
zonelist = property(_get_zonelist, _set_zonelist) #: (*lst[fzone]*) A list of zones to be written before the trac macro, for the situation that trac is used in 'stupid' model, i.e., the file attribute has been set to an auxiliary file.
class fspecies(object): # class for species transport model
"""Object for each chemical species. The species transport, adsorption, initial concentration and generator
properties are assigned in here.
"""
def __init__(self,phase,adsorption_model,adsorption,diffusion_model,diffusion, dispersion):
self._phase = phase
self._silent = dflt.silent
self._parent = None
self._adsorption_model = adsorption_model
if adsorption:
if len(adsorption) != 3:
pyfehm_print('ERROR: expecting three adsorption parameters',self._silent)
return
self._adsorption = adsorption
self._diffusion_model = diffusion_model
self._dispersion = dispersion
self._diffusion = diffusion
self._density_modifier = None
self._tracer_concentrationlist = []
self._tracer_generatorlist = []
def add_tracer_concentration(self,initial_concentration,zone=None):
"""Define initial tracer concentration in a zone.
:param initial_concentration: Initial concentration of the tracer.
:type initial_concentration: fl64
:param zone: Zone to which the initial concentration is assigned (default Zone 0).
:type zone: int, str, fzone
"""
if not zone: zone = self._parent._parent.zone[0]
ic = _tracer_concentration(initial_concentration,zone)
self._tracer_concentrationlist.append(ic)
def delete_tracer_concentration(self,tracer_concentration):
self._tracer_concentrationlist.remove(tracer_concentration)
def add_tracer_generator(self,tracer_generator,time_start,time_end,zone=None):
"""Define a tracer source or sink within a zone.
:param tracer_generator: Injection concentration at inlet node. If outlet, in place concentration will be used. If negative, concentration will be fixed at the absolute value.
:type tracer_generator:fl64
:param time_start: Time to start the source/sink.
:type time_start:fl64
:param time_end: Time to stop the source/sink.
:type time_end: fl64
"""
if not zone: zone = self._parent._parent.zone[0]
ic = _tracer_generator(tracer_generator,time_start,time_end,zone)
self._tracer_generatorlist.append(ic)
def delete_tracer_generator(self,tracer_generator):
self._tracer_generatorlist.remove(tracer_generator)
def _get_phase(self): return self._phase
def _set_phase(self,value): self._phase = value
phase = property(_get_phase,_set_phase) #: (*int*) Flag for species phase.
def _get_adsorption_model(self): return self._adsorption_model
def _set_adsorption_model(self,value): self._adsorption_model = value
adsorption_model = property(_get_adsorption_model,_set_adsorption_model) #: (*int*) Flag for adsorption model.
def _get_adsorption(self): return self._adsorption
def _set_adsorption(self,value): self._adsorption = value
adsorption = property(_get_adsorption,_set_adsorption) #: (*lst[fl64]*) Three item list of adsoprtion parameters.
def _get_diffusion_model(self): return self._diffusion_model
def _set_diffusion_model(self,value): self._diffusion_model = value
diffusion_model = property(_get_diffusion_model,_set_diffusion_model) #: (*int*) Flag for diffusion model.
def _get_diffusion(self): return self._diffusion
def _set_diffusion(self,value): self._diffusion = value
diffusion = property(_get_diffusion,_set_diffusion) #: (*fl64*) Diffusion coefficient.
def _get_dispersion(self): return self._dispersion
def _set_dispersion(self,value): self._dispersion = value
dispersion = property(_get_dispersion,_set_dispersion) #: (*lst[fl64]*) Three item list of X,Y,Z dispersion coefficients, or longitudinal and transverse components if ldsp = True.
def _get_density_modifier(self): return self._density_modifier
def _set_density_modifier(self,value): self._density_modifier = value
density_modifier = property(_get_density_modifier,_set_density_modifier) #: (*fl64*) Density modifier used in macro **CDEN**. If set, **CDEN** output will be written along with **TRAC**.
def _get_tracer_concentrationlist(self): return self._tracer_concentrationlist
def _set_tracer_concentrationlist(self,value): self._tracer_concentrationlist = value
tracer_concentrationlist = property(_get_tracer_concentrationlist,_set_tracer_concentrationlist) #: (*lst*) List of initial tracer concentration objects.
def _get_tracer_generatorlist(self): return self._tracer_generatorlist
def _set_tracer_generatorlist(self,value): self._tracer_generatorlist = value
tracer_generatorlist = property(_get_tracer_generatorlist,_set_tracer_generatorlist) #: (*lst*) List of tracer generator objects.
class _common_model(object):
def __init__(self, zone, diffusion_model, diffusion, dispersion):
self._zone = zone
self._silent = dflt.silent
self._diffusion = diffusion
self._dispersion = dispersion
self._diffusion_model = diffusion_model
def _get_zone(self): return self._zone
def _set_zone(self,value): self._zone = value
zone = property(_get_zone,_set_zone) #: (**)
def _get_diffusion(self): return self._diffusion
def _set_diffusion(self,value): self._diffusion = value
diffusion = property(_get_diffusion,_set_diffusion) #: (**)
def _get_diffusion_model(self): return self._diffusion_model
def _set_diffusion_model(self,value): self._diffusion_model = value
diffusion_model = property(_get_diffusion_model,_set_diffusion_model) #: (**)
def _get_dispersion(self): return self._dispersion
def _set_dispersion(self,value): self._dispersion = value
dispersion = property(_get_dispersion,_set_dispersion) #: (**)
class _tracer_concentration(object):
def __init__(self,initial_concentration,zone = None):
self._zone = zone
self._silent = dflt.silent
self._initial_concentration = initial_concentration
def _get_zone(self): return self._zone
def _set_zone(self,value): self._zone = value
zone = property(_get_zone,_set_zone) #: (**)
def _get_initial_concentration(self): return self._initial_concentration
def _set_initial_concentration(self,value): self._initial_concentration = value
initial_concentration = property(_get_initial_concentration,_set_initial_concentration) #: (**)
class _tracer_generator(object):
def __init__(self,injection_concentration, time_start, time_end ,zone = None):
self._zone = zone
self._silent = dflt.silent
self._injection_concentration = injection_concentration
self._time_start = time_start
self._time_end = time_end
def _get_injection_concentration(self): return self._injection_concentration
def _set_injection_concentration(self,value): self._injection_concentration = value
injection_concentration = property(_get_injection_concentration,_set_injection_concentration) #: (**)
def _get_time_start(self): return self._time_start
def _set_time_start(self,value): self._time_start = value
time_start = property(_get_time_start,_set_time_start) #: (**)
def _get_time_end(self): return self._time_end
def _set_time_end(self,value): self._time_end = value
time_end = property(_get_time_end,_set_time_end) #: (**)
def _get_zone(self): return self._zone
def _set_zone(self,value): self._zone = value
zone = property(_get_zone,_set_zone) #: (**)
class fgeneral(object): #PyFEHM general (undefined) macro class
def __init__(self, lines, insert_after, zonelist, parent):
self.lines = lines
self.zonelist = zonelist
self.insert_after = insert_after
self._parent = parent
# check zonelist correct format
if type(self.zonelist) in [int, str]:
if self.zonelist in self._parent.zone.keys():
self.zonelist = [self._parent.zone[self.zonelist],]
zns = []
for zn in self.zonelist:
if type(zn) in [int, str]:
if zn in self._parent.zone.keys():
zns.append(self._parent.zone[zn])
else:
zns.append(zn)
self.zonelist = zns
# check lines correct format
assert type(lines) in [list, tuple], "ERROR: bad type for lines, bro"
assert len(lines) > 0, "ERROR: lines are empty"
for line in lines:
assert type(line) is str, "ERROR: each entry in lines must be a string"
class fstorage(object):
"""Storage object - allows user to save information to the dat file.
"""
def __init__(self,parent=None):
self._parent =parent
self._silent = dflt.silent
[docs]class fcont(object): #FEHM contour output object.
"""Contour output object, makes request for contour data to be output (see macro **CONT**).
This data type outputs specified variables (e.g., temperature, permeability) for x,y,z or node locations
at fixed times (one file = one time). The data can be output in several formats (all of which are readable by
PyFEHM) and at specified times.
"""
__slots__=['_silent','_format','_timestep_interval','_time_interval','_time_flag','_variables','_zones']
def __init__(self,format=dflt.cont_format,timestep_interval=1000,time_interval=1.e30,time_flag=True,variables=[],zones=[]):
self._format = format
self._silent = dflt.silent
self._timestep_interval=timestep_interval
self._time_interval=time_interval
self._time_flag=time_flag
self._variables=[]
if variables: self.variables=variables
self._zones=[]
if zones: self.zones=zones
def __repr__(self):
retStr = self.format + ' contour output:\n'
for v in list(flatten(self.variables)):
retStr += v+'\n'
return retStr
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
def _get_options(self): #print out available variables
print 'The following are acceptable variables'
allVariables = list(flatten(self.variables))
for var in contour_variables:
for v in var:
if v in allVariables: print ' '+v +' (selected)'
else: print ' '+v
options = property(_get_options) #: Print out eligible variables for output.
def _get_info(self):
print 'Contour output requested ('+self.format+' format): '
print ' every ' + str(self.timestep_interval)+ ' timesteps'
print ' every ' + str(self.time_interval)+ ' days'
print ' for variables:'
for var in list(flatten(self.variables)):
print ' '+var
print
what = property(_get_info) #: Print out information about the attribute.
def _get_format(self): return self._format
def _set_format(self,value): self._format = value
format = property(_get_format, _set_format) #: (*str*) File format for contour output: 'tec', 'surf', 'avs', 'avsx'
def _get_time_interval(self): return self._time_interval
def _set_time_interval(self,value): self._time_interval = value
time_interval = property(_get_time_interval, _set_time_interval) #: (*flt*) Time interval to output data.
def _get_timestep_interval(self): return self._timestep_interval
def _set_timestep_interval(self,value): self._timestep_interval = value
timestep_interval = property(_get_timestep_interval, _set_timestep_interval) #: (*int*) Time step interval to output data.
def _get_variables(self): return self._variables
def _set_variables(self,value): self._variables = value
variables = property(_get_variables, _set_variables) #: (*lst[str]*)List of variables to write contour data for, e.g., ['temperature','pressure']
def _get_zones(self): return self._zones
def _set_zones(self,value): self._zones = value
zones = property(_get_zones, _set_zones) #: (*lst[fzone]*) List of zone objects for which contour data is to be written - **NOT FULLY SUPPORTED**.
def _get_time_flag(self): return self._time_flag
def _set_time_flag(self,value): self._time_flag = value
time_flag = property(_get_time_flag, _set_time_flag) #: (*bool*) Set to True to include in output title.
[docs]class fhist(object): #FEHM history output object.
"""FEHM history output object.
"""
__slots__=['_silent','_format','_timestep_interval','_time_interval','_variables','_nodelist','_zonelist','_zoneflux','_azonelist']
def __init__(self,format=dflt.hist_format,timestep_interval=1,time_interval=1.e30,variables=[],nodelist=[],zonelist=[],zoneflux=[],azonelist=[]):
self._format = format
self._silent = dflt.silent
self._timestep_interval=timestep_interval
self._time_interval=time_interval
self._variables=[]
self._nodelist=[]
if nodelist: self._nodelist = nodelist
self._zonelist=[]
if zonelist: self._zonelist = zonelist
self._zoneflux=[]
if zoneflux: self._zoneflux = zoneflux
self._azonelist=[]
if azonelist: self._azonelist = azonelist
if variables: self.variables=variables
def __repr__(self):
retStr = self.format + ' history output:\n'
for v in self.variables:
retStr += v[0]+'\n'
return retStr
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
def _get_options(self): #print out available variables
print 'The following are acceptable variables'
for var in history_variables:
if var in self.variables: print var +' (selected)'
else: print var
options = property(_get_options) #: Print out eligible variables for output.
def _get_nodelist(self): return self._nodelist
def _set_nodelist(self,value): self._nodelist = value
nodelist = property(_get_nodelist, _set_nodelist) #: (*list[fnode]*) list of node objects for which history output requested.
def _get_node(self): return dict([(nd.index,nd) for nd in self.nodelist])
node = property(_get_node) #: (*dict[fnode]*) Dictionary of nodes, indexed by node number, for which history output requested.
def _get_zonelist(self): return self._zonelist
def _set_zonelist(self,value): self._zonelist = value
zonelist = property(_get_zonelist, _set_zonelist) #: (*lst[fzone]*) List of zone objects for which history output required.
def _get_zoneflux(self): return self._zoneflux
def _set_zoneflux(self,value): self._zoneflux = value
zoneflux = property(_get_zoneflux, _set_zoneflux) #: (*lst[fzone,int]*) List of zone objects for which zone flux output required.
def _get_azonelist(self): return self._azonelist
def _set_azonelist(self,value): self._azonelist = value
azonelist = property(_get_azonelist, _set_azonelist) #: (*lst[fzone]*) List of zone objects for which zone history output required.
def _get_zone(self): return dict([(zn.index,zn) for zn in self.zonelist]+[(zn.name,zn) for zn in self.zonelist if zn.name != ''])
zone = property(_get_zone) #: (*dict[fzone]*) Dictionary of zones, indexed by number and name, for which history output is required.
def _get_info(self):
print 'History output requested ('+self.format+' format): '
if self.timestep_interval == None:
print ' every timestep'
else:
print ' every ' + str(self.timestep_interval)+ ' timesteps'
if self.time_interval == None:
print ' every ' + str(1.e30)+ ' days'
else:
print ' every ' + str(self.time_interval)+ ' days'
print ' for variables:'
for var in list(flatten(self.variables)):
print ' '+var
if self.nodelist:
print ' at nodes:'
for nd in self.nodelist:
print ' '+str(nd.index)
if self.zonelist:
print ' for nodes in zones:'
for zn in self.zonelist:
if zn.name:
print ' '+str(zn.index)+' ('+zn.name+')'
else:
print ' '+str(zn.index)
if self.azonelist:
print ' for zones:'
for zn in self.azonelist:
if zn.name:
print ' '+str(zn.index)+' ('+zn.name+')'
else:
print ' '+str(zn.index)
print
what = property(_get_info) #: Print out information about the attribute.
def _get_variables(self): return self._variables
def _set_variables(self,value): self._variables = value
variables = property(_get_variables, _set_variables) #: (*lst[str]*)List of variables to write contour data for, e.g., ['temperature','pressure']
def _get_format(self): return self._format
def _set_format(self,value): self._format = value
format = property(_get_format, _set_format) #: (*str*) File format for contour output: 'tecplot', 'csv', 'surfer'
def _get_timestep_interval(self): return self._timestep_interval
def _set_timestep_interval(self,value): self._timestep_interval = value
timestep_interval = property(_get_timestep_interval, _set_timestep_interval) #: (*int*) Time step interval to output data.
def _get_time_interval(self): return self._time_interval
def _set_time_interval(self,value): self._time_interval = value
time_interval = property(_get_time_interval, _set_time_interval) #: (*flt*) Time interval to output data.
[docs]class fboun(object): #FEHM boundary condition object.
'''Boundary condition object.
'''
def __init__(self,zone=[],type='ti',times=[],variable=[],file=None):
self._zone = []
self._silent = dflt.silent
if zone: self.zone=zone
self._type = type
self._times=times
self._parent = None
self._variable = []
self._file = file
if variable: self.variable = variable
def __repr__(self):
retStr = self.type + ' BC: variable = '
for var in self.variable: retStr += var[0] + ', '
return retStr
def _get_information(self):
prntStr = 'Boundary condition for zones: '
for zn in self.zone:
if isinstance(zn,fzone): prntStr += str(zn.index)+', '
else: prntStr += str(zn) + ', '
prntStr = prntStr[:-2]
print prntStr
what = property(_get_information) #: Print information about the boundary condition object.
def _get_zone(self): return self._zone
def _set_zone(self,value):
if isinstance(value,fzone): value = [value,]
self._zone = value
zone = property(_get_zone,_set_zone)#: (*lst[fzone]*) List of zones to which the boundary condition is to be applied.
def _get_type(self): return self._type
def _set_type(self,value): self._type = value
type = property(_get_type,_set_type)#: (*str*) Boundary condition type, 'ti' = step changes, 'ti_linear' = linear changes.
def _get_n_times(self): return len(self.times)
n_times = property(_get_n_times)#: (*int*) Length of time-series.
def _get_variable(self): return self._variable
def _set_variable(self,value): self._variable = value
variable = property(_get_variable,_set_variable)#: (*lst[lst[str,fl64,...]]*) List of lists of boundary data. Each list begins with a string denoting the boundary condition variable, and is followed by the time-series data, e.g., ['sw',0.2,0.5,0.8].
def _get_times(self): return self._times
def _set_times(self,value): self._times=value
times = property(_get_times,_set_times)#: (*lst[fl64]*) Vector of times.
def _get_file(self): return self._file
def _set_file(self,value): self._file = value
file = property(_get_file,_set_file)#: (*str*) File string where information about the macro is stored. If file does not currently exist, it will be created and written to when the FEHM input file is written.
def _check(self):
# check length of variable vectors correspond to length of time vectors
for var in self._variable:
if len(var)-1<len(self.times):
_buildWarnings('WARNING: Variable vector ('+var[0]+') specified in BOUN macro shorter than time vector.')
if len(var)-1>len(self.times):
_buildWarnings('WARNING: Variable vector ('+var[0]+') specified in BOUN macro longer than time vector.')
[docs]class frlpm(object): #FEHM relative permeability object (different to rlp macro).
'''Relative permeability model.
Relative permeability models are applied to zones. Each model assigns the relative permeability characteristics
for a particular phase (in attribute relperm) and capillary pressure relationships between phases (in attribute capillary).
In contrast to other macros, one relative permeability model may be applied to multiple zones.
'''
def __init__(self,zone=[],group = None,relperm=[],capillary=[]):
self._zone=[]
self._silent = dflt.silent
if zone: self._zone = zone
self._group = None
if group: self._group = group
self._relperm = {}
if relperm: self._assign_relperm(relperm)
self._capillary = {}
if capillary: self._assign_capillary(capillary)
self._parent = None
def __repr__(self):
prntStr = 'rlpm: '
if isinstance(self.zone,fzone): prntStr += str(self.zone.index)
elif isinstance(self.zone,list):
for zn in self.zone: prntStr += str(self.zone.index)+ ' ,'
prntStr = prntStr[:-2]
elif isinstance(self.zone,tuple):
zn = self.zone
prntStr += '('+str(zn[0]) +', ' +str(zn[1]) +', '+str(zn[2]) +')'
return prntStr
def _assign_relperm(self,relperms):
for relperm in relperms:
if not (isinstance(relperm,list) or isinstance(relperm,tuple)): continue
if len(relperm) != 3: print 'relperms specified incorrectly'; continue
self.add_relperm(relperm[0],relperm[1],relperm[2])
def _assign_capillary(self,capillarys):
for capillary in capillarys:
if not (isinstance(capillary,list) or isinstance(capillary,tuple)): continue
if len(capillary) != 3: print 'capillarys specified incorrectly'; continue
self.add_capillary(capillary[0],capillary[1],capillary[2])
[docs] def add_relperm(self,phase,type,param=[]):
'''Add a new relative permeability model for a given phase.
:param phase: Phase for which the relperm model is being defined, e.g., 'air','water','co2_liquid','co2_gas','vapor'.
:type phase: str
:param type: Type of model being assigned for that phase, e.g., 'constant','linear','exponential','corey','brooks-corey','vg' (Van Genuchten).
:type type: str
:param param: List of parameters for the specified model. See table above for a list of parameter names for each model.
:type param: list
'''
if param:
new_relperm = _relperm(phase,type,param)
else: new_relperm = _relperm(phase,type)
self._relperm.update(dict(((new_relperm.phase,new_relperm),)))
[docs] def add_capillary(self,phase,type,param=[]):
'''Add a new capillary pressure relationship between two phases.
:param phase: List or tuple of two phases for which the relationship is defined, e.g., ['air','water'].
:type phase: list, tuple
:param type: Type of model being assigned for that phase pair, e.g., 'linear_cap','vg_cap','brooks-corey_cap'.
:type type: str
:param param: List of parameters for the specified model. See table above for a list of parameter names for each model.
:type param: list
'''
if param:
new_capillary = _relperm(phase,type,param)
else: new_capillary = _relperm(phase,type)
self._capillary.update(dict(((new_capillary.phase,new_capillary),)))
[docs] def delete(self,model):
"""Delete a previously defined relative permeability or capillary pressure model.
"""
if isinstance(model,_relperm): model = relperm.phase
if model in self._relperm.keys(): self._relperm.__delitem__(model)
if model in self._capillary.keys(): self._capillary.__delitem__(model)
def _get_relperm(self): return self._relperm
relperm = property(_get_relperm) #: (*dict*) Dictionary of relative permeability models for each phase, indexed by phase name, e.g., 'water'. Each relperm model has a model type, and set of parameters for that type.
def _get_capillary(self): return self._capillary
capillary = property(_get_capillary) #: (*dict*) Dictionary of capillary pressure models, indexed by a tuple phase name pair, e.g., ('water/air'). Each capillary pressure model has a model type, and set of parameters for that type.
def _get_zone(self): return self._zone
def _set_zone(self,value): self._zone = value
zone = property(_get_zone, _set_zone) #: (*fzone*) Zone or list of zones to which the relative permeability model is assigned.
def _get_group(self): return self._group
def _set_group(self,value): self._group = value
group = property(_get_group, _set_group) #: (*int*) Group assignment for the relative permeability model.
def _get_phases(self):
if len(self.relperm) == 0: return []
else:
return [self.relperm[k].phase for k in self.relperm.keys()]
phases = property(_get_phases)
class _relperm(object): # private class for relative permeability model
def __init__(self,phase,type,param=[]):
self._type = type
self._silent = dflt.silent
self._phase = phase
self._param = None
self._assign_param()
if param: self._set_param(param)
def __repr__(self):
return self.phase + ' - ' + self.type
def _assign_param(self):
'''Assign parameters if supplied on initialisation.'''
self._param = dict(rlpm_dicts[self.type])
def _set_param(self,param):
if len(param) != len(self.param.keys()): return # return if numbers don't match up
for par,key in zip(param,rlpm_dicts[self.type]):
if isinstance(par,list) or isinstance(par,tuple):
self._param[par[0]] = par[1]
else:
self._param[key[0]] = par
def _get_phase(self): return self._phase
def _set_phase(self,value):
if value not in rlpm_phases: _buildWarnings('WARNING: '+str(value)+' not an available phase.');return
self._phase = value
phase = property(_get_phase, _set_phase) #: (**)
def _get_type(self): return self._type
def _set_type(self,value):
if value == self._type: return
if value not in rlpm_dicts.keys(): _buildWarnings('WARNING: '+str(value)+' not an available model.');return
self._type = value
self._param = rlpm_dicts[value]
type = property(_get_type, _set_type) #: (**)
def _get_param(self): return self._param
param = property(_get_param) #: (**)
class frlpm_table(object): # different object for specifying tables
"""Specify relative permeablity relationships using a lookup table.
Lookup tables can offer computational time savings where the relative permeability function is difficult to calculate.
"""
def __init__(self, zone=[], group=None, saturation=[], phase1 = [], phase2 = [], capillary=[]):
self._zone=[]
self._silent = dflt.silent
if zone: self._zone = zone
self._group = None
if group: self._group = group
self._saturation = None
if saturation: self._saturation = saturation
self._phase1 = None
vars = ['water','h2o_liquid','air','h2o_gas','vapor','co2_gas','co2_liquid','co2_sc']
if phase1:
if phase1[0] not in vars:
pyfehm_print('ERROR: first entry of phase1 must be one of '+str(vars),self._silent)
return
if not self._saturation:
pyfehm_print('ERROR: no saturation data supplied',self._silent)
return
if len(phase1[1]) != len(self._saturation):
pyfehm_print('ERROR: length of supplied phase1 relperm vector does not match saturation data',self._silent)
return
self._phase1 = phase1
if phase2:
if phase2[0] not in vars:
pyfehm_print('ERROR: first entry of phase2 must be one of '+str(vars),self._silent)
return
if not self._saturation:
pyfehm_print('ERROR: no saturation data supplied',self._silent)
return
if len(phase1[1]) != len(self._saturation):
pyfehm_print('ERROR: length of supplied phase2 relperm vector does not match saturation data',self._silent)
return
self._phase2 = phase2
if capillary:
if not self._saturation:
pyfehm_print('ERROR: no saturation data supplied',self._silent)
return
if len(capillary) != len(self._saturation):
pyfehm_print('ERROR: length of supplied capillary vector does not match saturation data',self._silent)
return
self._capillary = capillary
self._parent = None
def _get_zone(self): return self._zone
def _set_zone(self,value): self._zone = value
zone = property(_get_zone, _set_zone) #: (*fzone*) Zone or list of zones to which the relative permeability model is assigned.
def _get_group(self): return self._group
def _set_group(self,value): self._group = value
group = property(_get_group, _set_group) #: (*int*) Group assignment for the relative permeability model.
def _get_saturation(self): return self._saturation
def _set_saturation(self,value): self._saturation = value
saturation = property(_get_saturation, _set_saturation) #: (*lst*) List or array of saturation values for which relperm data are supplied.
def _get_phase1(self): return self._phase1
def _set_phase1(self,value): self._phase1 = value
phase1 = property(_get_phase1, _set_phase1) #: (*lst*) A two item list containing a string denoting the wetting phase, e.g., 'water','co2_liquid', and a vector of relperm data corresponding to the values in the saturation attribute.
def _get_phase2(self): return self._phase2
def _set_phase2(self,value): self._phase2 = value
phase2 = property(_get_phase2, _set_phase2) #: (*lst*) A two item list containing a string denoting the non-wetting phase, e.g., 'water','co2_liquid', and a vector of relperm data corresponding to the values in the saturation attribute.
def _get_capillary(self): return self._capillary
def _set_capillary(self,value): self._capillary = value
capillary = property(_get_capillary, _set_capillary) #: (*lst*) List or array of capillary pressure values corresponding to the supplied saturation data for the phase pair.def _get_phases:
def _get_phases(self):
phs = []
if len(self.phase1)>0: phs.append(self.phase1[0])
if len(self.phase2)>0: phs.append(self.phase2[0])
return phs
phases = property(_get_phases) #: (*lst*) List of phases for which relperm properties have been defined.
[docs]class files(object): #FEHM file constructor.
'''Class containing information necessary to write out fehmn.files.
'''
__slots__=['_silent','_root','_input','_grid','_incon','_use_incon','_rsto','_use_rsto','_outp','_use_outp','_check',
'_use_check','_hist','_use_hist','_co2in','_use_co2in','_stor','_use_stor','_parent','_exe','_co2_inj_time',
'_nopf','_use_nopf','_error','_use_error']
def __init__(self,root='',input='',grid='',incon='',rsto='',outp='',check='',hist='',co2in='',stor='',exe='fehm.exe',co2_inj_time=None):
self._root = ''
self._silent = dflt.silent
self._input = ''
self._grid = ''
self._incon = ''
self._use_incon = False
self._rsto = ''
self._use_rsto = True
self._outp = ''
self._use_outp = False
self._check = ''
self._use_check = False
self._hist = ''
self._use_hist = False
self._co2in = ''
self._use_co2in = False
self._stor = ''
self._use_stor = False
self._nopf = ''
self._use_nopf = False
self._error = ''
self._use_error = False
self._parent = None
self._exe = exe
self._co2_inj_time = co2_inj_time
if root: # if root specified assign as default for all inputs.
self._root = root
self._assign_root()
if input: self._input = input
if grid: self._grid = grid
if incon: self._incon = incon; self._use_incon = True
if rsto: self._rsto = rsto; self._use_rsto = True
if outp: self._outp = outp; self._use_outp = True
if check: self._check = check; self._use_check = True
if hist: self._hist = hist; self._use_hist = True
if stor: self._stor = stor; self._use_stor = True
def __repr__(self): return 'fehmn.files constructor'
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
def _assign_root(self):
'''Use root name for all inputs.
'''
self._root = root
self._input = root+'.dat'
self._grid = root+'.inp'
self._rsto = root+'.fin'
self._outp = root+'.out'
self._check = root+'.chk'
self._hist = root+'.his'
[docs] def write(self, use_paths=False):
'''Write out *fehmn.files*.
'''
if self._parent.work_dir:
outfile = open(self._parent.work_dir+os.sep+'fehmn.files','w')
else:
outfile = open('fehmn.files','w')
outfile.write('input: '+self.input+'\n')
if use_paths:
outfile.write('grida: '+self._parent.grid._path.full_path+'\n')
else:
outfile.write('grida: '+self.grid+'\n')
if not self.root: self.root = self.input.split('.')[0]
if not self.rsto: self.rsto = self.root+'.rsto' # default is to produce a restart file
outfile.write('rsto: '+self.rsto+'\n')
if self._use_outp:
if not self.outp: self.outp = self.root+'.outp'
outfile.write('outp: '+self.outp+'\n')
if self._use_check:
if not self.check: self.check = self.root+'.chk'
outfile.write('check: '+self.check+'\n')
if self.incon:
outfile.write('rsti:'+self.incon+'\n')
if self._use_hist:
if not self.check: self.check = self.root+'.hst'
if self.hist:
outfile.write('hist: '+self.hist+'\n')
if self._parent.carb.iprtype != 0 and not self.co2in:
self._use_co2in = True
co2_path = fpath()
co2_path.filename = dflt.co2_interp_path
if not os.path.isfile(co2_path.full_path):
co2_path.filename = dflt.co2_interp_path_2
if not os.path.isfile(co2_path.full_path):
_buildWarnings('WARNING: Cant find co2_interp.txt')
self._use_co2in = False
elif self.co2in:
co2_path = fpath()
co2_path.filename = self.co2in
if self._use_co2in:
outfile.write('co2in: '+co2_path.full_path+'\n')
if self._use_nopf:
outfile.write('nopf: '+self._nopf+'\n')
if self._use_error:
outfile.write('error: '+self._error+'\n')
if self._use_stor:
outfile.write('stor:')
if not self.stor: self.stor = self.root+'.stor'
outfile.write(' '+self.stor)
outfile.write('\n')
if self.root: outfile.write('root:'+self.root+'\n')
# level of print screen output
if self._parent.verbose: outfile.write('\nall\n')
else: outfile.write('\nnone\n')
# Set secret flag to use co2_inj.txt file to specify time to stop injection
if self.co2_inj_time:
if self._parent.carb.iprtype <= 1: _buildWarnings('WARNING: CO2 injection flag requested but no carb macro specified, CO2 injection flag will be ignored.')
else:
outfile.write('999\n')
if self._parent.work_dir:
co2_inj_file = open(self._parent.work_dir+os.sep+'co2_inj.txt','w')
else:
outfile = open('co2_inj.txt','w')
co2_inj_file.write( str(self.co2_inj_time)+'\n')
co2_inj_file.close()
outfile.close()
def _get_input(self): return self._input
def _set_input(self,value):
self._input = value
if self._root == None:
self._root = self._input.split('.')[0]
input = property(_get_input,_set_input) #: (*str*) Name of input file. This is set automatically when reading an input file in PyFEHM or when running a simulation.
def _get_root(self): return self._root
def _set_root(self,value):
self._root = value
self.outp = self.root +'.outp'
self.check = self.root +'.chk'
root = property(_get_root,_set_root) #: (*str*) Default file name string. If not already specified, this is set automatically when running a simulation.
def _get_grid(self): return self._grid
def _set_grid(self,value): self._grid = value
grid = property(_get_grid,_set_grid) #: (*str*) Name of grid file. This is set automatically when reading an grid file in PyFEHM.
def _get_incon(self): return self._incon
def _set_incon(self,value): self._incon = value; self._use_incon = True
incon = property(_get_incon,_set_incon) #: (*str*) Name of restart file to read in (initial condition). This is set automatically when reading an incon file in PyFEHM.
def _get_rsto(self): return self._rsto
def _set_rsto(self,value): self._rsto = value
rsto = property(_get_rsto,_set_rsto) #: (*str*) Name of restart file to write out.
def _get_outp(self): return self._outp
def _set_outp(self,value): self._outp = value; self._use_outp = True
outp = property(_get_outp,_set_outp) #: (*str*) Name of output file.
def _get_nopf(self): return self._nopf
def _set_nopf(self,value): self._nopf = value; self._use_nopf = True
nopf = property(_get_nopf,_set_nopf) #: (*str*) Name of nopf file.
def _get_check(self): return self._check
def _set_check(self,value): self._check = value
check = property(_get_check,_set_check) #: (*str*) Name of check file.
def _get_co2in(self): return self._co2in
def _set_co2in(self,value):
self._co2in = value
self._use_co2in = True
co2in = property(_get_co2in, _set_co2in) #: (*str*) Name or path to co2 properties file
def _get_hist(self): return self._hist
def _set_hist(self,value): self._hist = value
hist = property(_get_hist,_set_hist) #: (*str*) Name of history file.
def _get_stor(self): return self._stor
def _set_stor(self,value):
self._stor = value
self._use_stor = True
self._parent.ctrl['stor_file_LDA'] = 1
stor = property(_get_stor,_set_stor) #: (*str*) Name of store file.
def _get_exe(self): return self._exe
def _set_exe(self,value): self._exe = value
exe = property(_get_exe,_set_exe)#: (*str*) Path to FEHM executable. Default is 'fehm.exe'.
def _get_co2_inj_time(self): return self._co2_inj_time
def _set_co2_inj_time(self,value): self._co2_inj_time = value
co2_inj_time = property(_get_co2_inj_time,_set_co2_inj_time)#: (*fl64*) Number of years at which FEHM will terminate co2 injection
[docs]class fdata(object): #FEHM data file.
"""Class for FEHM data file.
"""
__slots__=['_silent','_gridfilename','_inconfilename','_sticky_zones','_allMacro','_allModel','_associate',
'_bounlist','_cont','_ctrl','_grid','_incon','_hist','_iter','_nfinv','_nobr','_head','_flxn','_vapl','_adif','_rlpmlist','_sol',
'_time','text','_times','_zonelist','_writeSubFiles','_strs','_ngas','_carb','_trac','_files','_verbose','_general_macrolist',
'_tf','_ti','_dti','_dtmin','_dtmax','_dtn','_dtx','_sections','_help','_running','_unparsed_blocks','keep_unknown','_flxo',
'_output_times','_path','_vtk','_diagnostic','_storage','_air']
def __init__(self,filename='',gridfilename='',inconfilename='',sticky_zones=dflt.sticky_zones,associate=dflt.associate,work_dir = None,
full_connectivity=dflt.full_connectivity,skip=[],keep_unknown=dflt.keep_unknown): #Initialise data file
from copy import copy
self._gridfilename=gridfilename
self._inconfilename=inconfilename
self._sticky_zones = sticky_zones
self._allMacro = dict([(key,[]) for key in macro_list.keys()])
self._allModel = dict([(key,[]) for key in model_list.keys()])
self._associate = associate
self._bounlist = []
self._cont = fcont()
self._ctrl=copy(dflt.ctrl)
self._grid=fgrid()
self.grid._parent = self
self._incon=fincon()
self.incon._parent = self
self._storage = fstorage()
self._storage._parent = self
self._hist = fhist()
self._iter=copy(dflt.iter)
self._nfinv = False
self._nobr = False
self._head = False
self._air = None
self._flxn = False
self._vapl = False
self._adif = None
self._rlpmlist=[]
self._general_macrolist = []
self._sol = copy(dflt.sol)
self.text=[] #: (*str*) Information about the model printed at the top of the input file.
self._time=copy(dflt.time)
self._times=[]
self._zonelist=[]
self._flxo = []
self._writeSubFiles = True # Boolean indicating macro and zone sub files should be written every time
# additional modules
self._strs = fstrs(parent=self)
self._carb = fcarb(parent=self)
self._trac = ftrac(parent=self)
self._ngas = fngas(parent=self)
self._help = fhelp(parent=self)
# run object
self._path = fpath(parent=self)
self._files = files()
self._vtk = None
self._files._parent = self
self._diagnostic = fdiagnostic(parent=self)
self._verbose = True
self._silent = dflt.silent
self._running = False # boolean indicating whether a simulation is in progress
self._unparsed_blocks = {}
self._sections = []
self.keep_unknown = keep_unknown
self.work_dir = work_dir
if self.work_dir:
try:
os.makedirs(self.work_dir)
except:
pass
# time stepping shortcuts
self._tf = dflt.time['max_time_TIMS']
self._ti = dflt.time['initial_day_INITTIME']
self._dti = dflt.time['initial_timestep_DAY']
self._dtmin = dflt.ctrl['min_timestep_DAYMIN']
self._dtmax = dflt.ctrl['max_timestep_DAYMAX']
self._dtn = dflt.time['max_timestep_NSTEP']
self._dtx = dflt.ctrl['timestep_multiplier_AIAA']
self._output_times = []
# add 'everything' zone
self._add_zone(fzone(index=0))
# OPTIONS
temp_path = fpath(); temp_path.filename = filename
# 1. fehmn.files was passed - assume that the directory in which this file sits is the work directory
if temp_path.filename == 'fehmn.files':
if temp_path.absolute_to_file != os.getcwd():
self.work_dir = temp_path.absolute_to_file
wd = temp_path.absolute_to_file+os.sep
inconfilename = None
with open(filename) as f:
for ln in f.readlines():
if ln.startswith('rsti:'):
inconfilename = ln.split('rsti:')[-1].strip()
if ln.startswith('grida:'):
gridfilename = ln.split('grida:')[-1].strip()
if ln.startswith('grid:'):
gridfilename = ln.split('grid:')[-1].strip()
if ln.startswith('gridf:'):
gridfilename = ln.split('gridf:')[-1].strip()
if ln.startswith('input:'):
filename = ln.split('input:')[-1].strip()
if ln.startswith('stor:'):
storfilename = ln.split('stor:')[-1].strip()
self.files.stor = storfilename
if ln.startswith('stori:'):
storfilename = ln.split('stori:')[-1].strip()
self.files.stor = storfilename
if ln.startswith('root:'):
self.files.root = ln.split('root:')[-1].strip()
if ln.startswith('error:'):
self.files._error = ln.split('error:')[-1].strip()
self.files._use_error = True
if ln.startswith('nopf:'):
self.files._nopf = ln.split('nopf:')[-1].strip()
self.files._use_nopf = True
# set up path objects then pass to read function
self._path.filename = filename
self.grid._path.filename = gridfilename
if inconfilename:
self.incon._path.filename
self.read(full_connectivity=full_connectivity,skip=skip)
# 2. nothing passed - no path objects to set up, no reading required
elif not filename and not gridfilename and not inconfilename:
return
# 3. input and grid file passed
elif filename and gridfilename and not inconfilename:
self._path.filename = filename
self.grid._path.filename = gridfilename
self.read(full_connectivity=full_connectivity,skip=skip)
# 4. all passed
elif filename and gridfilename and inconfilename:
self._path.filename = filename
self.grid._path.filename = gridfilename
self.incon._path.filename = inconfilename
self.read(full_connectivity=full_connectivity,skip=skip)
else:
pyfehm_print('ERROR: file configuration not recognized',self._silent)
return
def __repr__(self):
if self.filename == None:
return 'empty object'
else:
return self.filename #Print out details
def __getstate__(self):
return dict((k, getattr(self, k)) for k in self.__slots__)
def __setstate__(self, data_dict):
for (name, value) in data_dict.iteritems():
setattr(self, name, value)
[docs] def read(self,filename='',gridfilename='',inconfilename='',full_connectivity=dflt.full_connectivity,skip=[]): #Reads data from file.
'''Read FEHM input file and construct fdata object.
:param filename: Name of FEHM input file. Alternatively, supplying 'fehmn.files' will cause PyFEHM to query this file for input, grid and restart file names if they are available.
:type filename: str
:param gridfilename: Name of FEHM grid file.
:type gridfilename: str
:param inconfilename: Name of FEHM restart file.
:type inconfilename: str
:param skip: List of macro strings to ignore when reading an input file.
:type skip: list
'''
# set up paths
if filename: self._path.filename = filename
if gridfilename: self.grid._path.filename = filename
if inconfilename: self.incon._path.filename = filename
# THERE WILL ALWAYS BE A GRID PATH TO READ - INPUT FILES CANNOT BE READ WITHOUT A GRID
self.grid.read(self.grid._path.full_path,full_connectivity=full_connectivity)
self.files.grid = self.grid._path.full_path
if self.incon._path.filename:
self.incon.read(self.incon._path.full_path)
self.files.incon = self.incon._path.full_path
self.files._use_incon = True
if len(self.incon.P) != self.grid.number_nodes:
pyfehm_print('ERROR: grid and incon files contain different numbers of nodes',self._silent)
self.incon = fincon() # empty incon
self.files.incon = ''
self.files._use_incon = False
else: self._associate_incon()
self.files.input = self._path.full_path
pyfehm_print('reading input file',self._silent)
#infile = open(filename,'r')
infile = open(self._path.full_path,'r')
read_fn=dict(zip(fdata_sections,
[self._read_cont,self._read_macro,self._read_zonn,self._read_zonn,self._read_macro,
self._read_time,self._read_ctrl,self._read_iter,self._read_macro,self._read_macro,
self._read_boun,self._read_macro,self._read_strs,self._read_text,self._read_sol,
self._read_nfinv,self._read_hist,self._read_histnode,self._read_carb,self._read_model,
self._read_macro,self._read_nobr,self._read_flxz,self._read_rlpm,self._read_macro,
self._read_trac,self._read_model,self._read_model,self._read_vapl,self._read_adif,
self._read_ngas,self._read_flxo,self._read_head,self._read_flxn, self._read_air, self._read_air]))
self._sections=[]
"""Need to first establish dimensionality of input file. Requires initial read through."""
more = True
while more:
line=infile.readline()
keyword=line[0:4].strip()
if keyword=='ctrl':
self._read_ctrl(infile)
more = False
infile.close()
infile = open(self._path.full_path,'r')
more = True
precedingKey='start'
precedingZoneKey = None
line=infile.readline()
while more:
keyword=line[0:4].strip()
if keyword in fdata_sections and keyword not in skip:
pyfehm_print('reading '+keyword,self._silent)
fn=read_fn[keyword]
if keyword in macro_list.keys():
self._read_macro(infile,keyword)
precedingZoneKey = None
elif keyword in model_list.keys():
self._read_model(infile,keyword)
precedingZoneKey = None
else:
if keyword in ['zone','zonn']:
if 'rad' in line:
self._read_zonn_rad(infile)
else:
block = fn(infile)
precedingZoneKey = copy(precedingKey)
elif keyword in ['head','flxn']:
fn(infile,line)
precedingZoneKey = None
else:
fn(infile)
precedingZoneKey = None
self._sections.append(keyword)
precedingKey = keyword
elif keyword[0:3] in fdata_sections and keyword not in skip:
keyword = keyword[0:3]
pyfehm_print('reading '+keyword,self._silent)
fn=read_fn[keyword]
fn(infile)
self._sections.append(keyword)
precedingKey = keyword
elif line.startswith('stop'): more=False; continue
else:
# start recording a code block
foundKey = False
if not precedingZoneKey: block = []
else: precedingKey = precedingZoneKey
while not foundKey:
block.append(line)
line=infile.readline()
keyword=line[0:4].strip()
if keyword in fdata_sections and keyword not in skip:
foundKey = True
continue
elif keyword[0:3] in fdata_sections and keyword not in skip:
foundKey = True
continue
elif line.startswith('stop'):
foundKey = True
continue
self._unparsed_blocks.update(dict(((precedingKey,block),)))
continue
line=infile.readline()
infile.close()
self._add_boundary_zones()
return self
[docs] def write(self,filename='',writeSubFiles = True): #Writes data to a file.
'''Write fdata object to FEHM input file and fehmn.files file.
:param filename: Name of FEHM input file to write to.
:type filename: str
:param writeSubFiles: Boolean indicating whether macro and zone information, designated as contained within other input files, should be written out, regardless of its existence. Non-existant files will always be written out.
:type writeSubFiles: bool
'''
if writeSubFiles: self._writeSubFiles = writeSubFiles
if filename: self._path.filename=filename
if not self._path.filename: self._path.filename='default_INPUT.dat'
out_flag = self._write_prep()
if out_flag: return False
# ensure directory is created
if self.work_dir: wd = self.work_dir
else: wd = self._path.absolute_to_file
try:
os.makedirs(wd)
except:
pass
# open file
outfile = open(wd+os.sep+self._path.filename,'w')
outfile.write('# '+self.filename+'\n')
self._write_unparsed(outfile,'start')
if self.text: self._write_text(outfile); self._write_unparsed(outfile,'text')
if self.sol: self._write_sol(outfile); self._write_unparsed(outfile,'sol')
if self.nfinv: self._write_nfinv(outfile); self._write_unparsed(outfile,'nfinv')
if self.nobr: self._write_nobr(outfile); self._write_unparsed(outfile,'nobr')
if self.air != None: self._write_air(outfile); self._write_unparsed(outfile,'air')
if self.head: self._write_head(outfile); self._write_unparsed(outfile,'head')
if self.flxn: self._write_flxn(outfile); self._write_unparsed(outfile,'flxn')
if self.vapl: self._write_vapl(outfile); self._write_unparsed(outfile,'vapl')
if self.adif != None: self._write_adif(outfile); self._write_unparsed(outfile,'adif')
if self.flxo: self._write_flxo(outfile); self._write_unparsed(outfile,'flxo')
if len(self.zone)>1 and not self.sticky_zones:
self._write_zonn_all(outfile)
self._write_unparsed(outfile,'zone')
if self.cont.variables: self._write_cont(outfile); self._write_unparsed(outfile,'cont')
if self.hist.variables: self._write_hist(outfile); self._write_unparsed(outfile,'hist')
if self.gradlist: self._write_macro(outfile,'grad'); self._write_unparsed(outfile,'grad')
if self.pres: self._write_macro(outfile,'pres'); self._write_unparsed(outfile,'pres')
if self.ngas.dof != None: self._write_ngas(outfile); self._write_unparsed(outfile,'ngas')
if self.bounlist: self._write_boun(outfile); self._write_unparsed(outfile,'boun')
if self.flow: self._write_macro(outfile,'flow'); self._write_unparsed(outfile,'flow')
if self.hflx: self._write_macro(outfile,'hflx'); self._write_unparsed(outfile,'hflx')
if self.perm: self._write_macro(outfile,'perm'); self._write_unparsed(outfile,'perm')
if self.rock: self._write_macro(outfile,'rock'); self._write_unparsed(outfile,'rock')
if self.pporlist: self._write_model(outfile,'ppor'); self._write_unparsed(outfile,'ppor')
if self.cond: self._write_macro(outfile,'cond'); self._write_unparsed(outfile,'cond')
if self.vconlist: self._write_model(outfile,'vcon'); self._write_unparsed(outfile,'vcon')
if self.rlplist: self._write_model(outfile,'rlp'); self._write_unparsed(outfile,'rlp')
if self.rlpmlist: self._write_rlpm(outfile); self._write_unparsed(outfile,'rlpm')
if self.time: self._write_time(outfile); self._write_unparsed(outfile,'time')
if self.ctrl: self._write_ctrl(outfile); self._write_unparsed(outfile,'ctrl')
if self.iter: self._write_iter(outfile); self._write_unparsed(outfile,'iter')
if self.carb.iprtype!=0: self._write_carb(outfile); self._write_unparsed(outfile,'carb')
if self.strs.param['ISTRS']: self._write_strs(outfile); self._write_unparsed(outfile,'strs')
if self.trac._on: self._write_trac(outfile); self._write_unparsed(outfile,'trac')
outfile.write('stop\n')
outfile.close()
return True
[docs] def add(self,obj,overwrite=False): #Adds a new object to the file
'''Attach a zone, boundary condition or macro object to the data file.
:param obj: Object to be added to the data file.
:type obj: fzone, fmacro, fmodel, fboun
:param overwrite: Flag to overwrite macro if already exists for a particular zone.
:type overwrite: bool
'''
if isinstance(obj,fmacro): self._add_macro(obj,overwrite)
elif isinstance(obj,fmodel): self._add_model(obj)
elif isinstance(obj,fzone): self._add_zone(obj,overwrite)
elif isinstance(obj,fboun): self._add_boun(obj)
elif isinstance(obj,frlpm): self._add_rlpm(obj)
elif isinstance(obj,frlpm_table): self._add_rlpm(obj)
[docs] def delete(self,obj): #Deletes an object from the file
'''Delete a zone, boundary condition or macro object from the data file.
:param obj: Object to be deleted from the data file. Can be a list of objects.
:type obj: fzone, fmacro, fmodel, fboun, list
'''
if isinstance(obj,fmacro): self._delete_macro(obj)
if isinstance(obj,fmodel): self._delete_model(obj)
elif isinstance(obj,fzone): self._delete_zone(obj)
elif isinstance(obj,fboun): self._delete_boun(obj)
elif isinstance(obj,frlpm): self._delete_rlpm(obj)
elif isinstance(obj,frlpm_table): self._delete_rlpm(obj)
elif isinstance(obj,list):
for obji in copy(obj):
if isinstance(obji,fmacro): self._delete_macro(obji)
if isinstance(obji,fmodel): self._delete_model(obji)
elif isinstance(obji,fzone): self._delete_zone(obji)
elif isinstance(obji,fboun): self._delete_boun(obji)
elif isinstance(obji,frlpm): self._delete_rlpm(obji)
elif isinstance(obji,frlpm_table): self._delete_rlpm(obji)
[docs] def picklable(self):
"""Call before multi-processing simulations in Windows to ensure fdata is picklable.
"""
for zn in self.zonelist:
zn._nodelist = [nd._index for nd in zn._nodelist]
for nd in self.grid.nodelist:
nd._connected_nodes = [ndi._index for ndi in nd._connected_nodes]
nd._elements = [el._index for el in nd._elements]
for con in self.grid.connlist:
con._nodes = [con._nodes[0]._index,con._nodes[1]._index]
def _add_boundary_zones(self): #Automatically creates zones corresponding to x,y,z boundaries
x0,x1 = self.grid.xmin,self.grid.xmax
y0,y1 = self.grid.ymin,self.grid.ymax
x = np.sort(np.unique([nd._position[0] for nd in self.grid._nodelist]))
y = np.sort(np.unique([nd._position[1] for nd in self.grid._nodelist]))
if self.grid.dimensions == 2:
ks = [999,998,997,996,'XMIN','XMAX','YMIN','YMAX']
for k in ks:
if k in self.zone.keys(): self.delete(self.zone[k])
dx = (x[1]-x[0])/2.
zn = fzone(999,name='XMIN'); zn.rect([x0-0.1,y0-0.1],[x0+dx,y1+0.1])
self.add(zn,overwrite=True)
dx = (x[-1]-x[-2])/2.
zn = fzone(998,name='XMAX'); zn.rect([x1-dx,y0-0.1],[x1+0.1,y1+0.1])
self.add(zn,overwrite=True)
dy = (y[1]-y[0])/2.
zn = fzone(997,name='YMIN'); zn.rect([x0-0.1,y0-0.1],[x1+0.1,y0+dy])
self.add(zn,overwrite=True)
dy = (y[-1]-y[-2])/2.
zn = fzone(996,name='YMAX'); zn.rect([x0-0.1,y1-dy],[x1+0.1,y1+0.1])
self.add(zn,overwrite=True)
elif self.grid.dimensions == 3:
z0,z1 = self.grid.zmin,self.grid.zmax
z = np.sort(np.unique([nd.position[2] for nd in self.grid.nodelist]))
ks = [999,998,997,996,995,994,'XMIN','XMAX','YMIN','YMAX','ZMIN','ZMAX']
for k in ks:
if k in self.zone.keys(): self.delete(self.zone[k])
dx = (x[1]-x[0])/2.
zn = fzone(999,name='XMIN'); zn.rect([x0-0.1,y0-0.1,z0-0.1],[x0+dx,y1+0.1,z1+0.1])
self.add(zn,overwrite=True)
dx = (x[-1]-x[-2])/2.
zn = fzone(998,name='XMAX'); zn.rect([x1-dx,y0-0.1,z0-0.1],[x1+0.1,y1+0.1,z1+0.1])
self.add(zn,overwrite=True)
dy = (y[1]-y[0])/2.
zn = fzone(997,name='YMIN'); zn.rect([x0-0.1,y0-0.1,z0-0.1],[x1+0.1,y0+dy,z1+0.1])
self.add(zn,overwrite=True)
dy = (y[-1]-y[-2])/2.
zn = fzone(996,name='YMAX'); zn.rect([x0-0.1,y1-dy,z0-0.1],[x1+0.1,y1+0.1,z1+0.1])
self.add(zn,overwrite=True)
dz = (z[1]-z[0])/2.
zn = fzone(995,name='ZMIN'); zn.rect([x0-0.1,y0-0.1,z0-0.1],[x1+0.1,y1+0.1,z0+dz])
self.add(zn,overwrite=True)
dz = (z[-1]-z[-2])/2.
zn = fzone(994,name='ZMAX'); zn.rect([x0-0.1,y0-0.1,z1-dz],[x1+0.1,y1+0.1,z1+0.1])
self.add(zn,overwrite=True)
else:
pyfehm_print('ERROR: Unrecognized grid dimensionality',self._silent)
def _associate_incon(self): #Associates initial condition data with nodes
if not self._associate: return
names = ('T','P','S','S_co2l','S_co2g','co2aq')
vars = [self.incon.T,self.incon.P,self.incon.S,self.incon.S_co2l,
self.incon.S_co2g,self.incon.co2aq]
names = [name for name,var in zip(names,vars) if isinstance(var,np.ndarray)]
if not self._running: names = [name+'i' for name in names]
vars = np.array([var for var in vars if isinstance(var,np.ndarray)])
for nd,var in zip(self.grid.nodelist,vars.T):
for i,name in enumerate(names):
nd.__setattr__('_'+name,var[i])
names = ['strs','disp']
strs2D = False; strs3D = False
if isinstance(self.incon.strs_xx,np.ndarray):
if isinstance(self.incon.strs_zz,np.ndarray): strs3D = True
else: strs2D = True
for i,nd in enumerate(self.grid.nodelist):
if self._running:
if strs2D: nd._strs = [self.incon.strs_xx[i],self.incon.strs_yy[i],self.incon.strs_xy[i]]
elif strs3D: nd._strs = [self.incon.strs_xx[i],self.incon.strs_yy[i],self.incon.strs_zz[i],self.incon.strs_xy[i],self.incon.strs_yz[i],self.incon.strs_xz[i]]
if len(self.incon.disp_x) == 0: continue
if strs2D: nd._disp = [self.incon.disp_x[i],self.incon.disp_y[i]]
elif strs3D: nd._disp = [self.incon.disp_x[i],self.incon.disp_y[i],self.incon.disp_z[i]]
else:
if strs2D: nd._strsi = [self.incon.strs_xx[i],self.incon.strs_yy[i],self.incon.strs_xy[i]]
elif strs3D: nd._strsi = [self.incon.strs_xx[i],self.incon.strs_yy[i],self.incon.strs_zz[i],self.incon.strs_xy[i],self.incon.strs_yz[i],self.incon.strs_xz[i]]
if len(self.incon.disp_x) == 0: continue
if strs2D: nd._dispi = [self.incon.disp_x[i],self.incon.disp_y[i]]
elif strs3D: nd._dispi = [self.incon.disp_x[i],self.incon.disp_y[i],self.incon.disp_z[i]]
def _write_unparsed(self,outfile,key):
if not self.keep_unknown: return
if key in self._unparsed_blocks.keys():
block = self._unparsed_blocks[key]
for line in block:
if key == 'start': line = '# '+line
outfile.write(line)
def _read_adif(self,infile): #ADIF: Reads ADIF macro.
line = infile.readline().strip().split()
self.adif = float(line[0])
if self.adif in [333.,666.]: self.adif = int(self.adif)
def _write_adif(self,outfile): #Writes ADIF macro.
if self.adif:
outfile.write('adif\n')
outfile.write(str(self.adif)+'\n')
self._write_general_macro(outfile, 'adif')
def _read_air(self,infile): #AIR: Reads AIR/AIRWATER macro.
line = infile.readline().strip().split()
ico2d = float(line[0])
nums = infile.readline().strip().split()
assert len(nums) == 2, "ERROR bro"
self._air = [ico2d, float(nums[0]), float(nums[1])]
def _write_air(self,outfile): #Writes AIR/AIRWATER macro.
if self._air is not None:
outfile.write('air\n')
outfile.write('%i\n'%self._air[0])
outfile.write('%8.7f %8.7f\n'%(self._air[1], self._air[2]))
self._write_general_macro(outfile, 'air')
def set_air(self, ico2d, reference_temperature, reference_pressure):
self._air = [ico2d, reference_temperature, reference_pressure]
def _read_boun(self,infile): #BOUN: Reads BOUN macro.
line=infile.readline().strip()
new_bouns=[]
file_flag=False
# read models
while not (not line or line.startswith('end')):
if line.startswith('file'): file_flag = True; break # read file data
new_boun = fboun()
line=infile.readline().strip()
new_boun.type = line
line=infile.readline().strip()
nums = line.split()
N = int(nums[0])
new_boun.times = [float(num) for num in nums[1:]]
while len(new_boun.times) != N:
line=infile.readline().strip()
nums = line.split()
for num in nums: new_boun.times.append(float(num))
line=infile.readline().strip() # read next model
while not (line.startswith('model') or not line or line.startswith('end')):
var = [line,]
while len(var) != (len(new_boun.times)+1):
line=infile.readline().strip()
nums = line.split()
for num in nums:
var.append(float(num))
new_boun.variable.append(var)
line=infile.readline().strip() # read next model
new_bouns.append(new_boun)
if file_flag:
line=infile.readline().strip()
if not os.path.isfile(line):
# check if in subdirectory with input file
fname = self.filename.split(os.sep)
if len(fname)>0:
fn0 = ''
for fn in fname[:-1]: fn0 += fn
if not os.path.isfile(fn0+os.sep+line):
pyfehm_print('ERROR: cannot find macro file '+line,self._silent)
else:
macrofile = open(fn0+os.sep+line)
line = macrofile.readline().strip()
self._read_boun(macrofile)
macrofile.close()
else:
macrofile = open(line)
line = macrofile.readline().strip()
self._read_boun(macrofile)
macrofile.close()
else:
# read zone assignments
line=infile.readline().strip() # read next zone assignment
while line:
nums = line.split()
if (nums[0] == '1' and nums[1] == '0' and nums[2] == '0') or (int(nums[0])<0):
new_bouns[int(nums[-1])-1].zone.append(self.zone[_zone_ind(nums[0])])
else:
nds = (int(nums[0]),int(nums[1]),int(nums[2]))
new_bouns[int(nums[-1])-1].zone.append(nds)
line=infile.readline().strip() # read next zone assignment
for new_boun in new_bouns:
self._add_boun(new_boun)
def _write_boun(self,outfile): #Writes BOUN macro.
ws = _title_string('BOUNDARY CONDITIONS',72)
outfile.write(ws)
#if self.sticky_zones:
# self._write_zonn_one(outfile,[bm.zone for bm in self.bounlist if bm.zone.index != 0])
if self.sticky_zones:
allzns = []
for bm in self.bounlist:
for zn in bm.zone:
if zn.index != 0: allzns.append(zn)
self._write_zonn_one(outfile,allzns)
outfile.write('boun\n')
nm = 1
for boun in self.bounlist:
outfile.write('model '+str(nm)+'\n')
nm+=1
outfile.write(boun.type + '\n')
outfile.write(str(boun.n_times)+'\t')
cnt = 0
for t in boun.times:
outfile.write(str(t)+'\t')
cnt +=1
if cnt == 10: outfile.write('\n'); cnt = 0
if cnt != 0: outfile.write('\n')
#outfile.write('\n')
for var in boun.variable:
outfile.write(var[0]+'\n')
cnt = 0
for v in var[1:]:
outfile.write(str(v)+'\t')
cnt +=1
if cnt == 10: outfile.write('\n'); cnt = 0
if cnt != 0: outfile.write('\n')
#outfile.write('\n')
outfile.write('end\n')
nm = 1
for boun in self.bounlist:
if not boun.zone: nm+=1; continue
for zone in boun.zone:
if isinstance(zone,fzone):
ind = zone.index
if not ind: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-ind)+'\t'+'0\t0\t')
outfile.write(str(nm)+'\n')
else:
outfile.write(str(zone[0])+'\t'+str(zone[1])+'\t'+str(zone[2])+'\t')
outfile.write(str(nm)+'\n')
nm+=1
outfile.write('\n')
self._write_general_macro(outfile, 'boun')
def _add_boun(self,boun=fboun()): #Adds a BOUN model.
boun._parent = self
if isinstance(boun.zone,(int,tuple,str)): boun.zone = [boun.zone]
zns = []
for zn in boun.zone:
if isinstance(zn,tuple):
zns.append(tuple([int(ls) for ls in zn]))
elif isinstance(zn,int) or isinstance(zn,str):
if zn in self.zone.keys(): zns.append(self.zone[zn])
elif isinstance(zn,fzone):
zns.append(zn)
boun.zone = zns
self._bounlist.append(boun)
def _delete_boun(self,boun=fboun()):
self._bounlist.remove(boun)
def _read_carb(self,infile): #CARB: Reads CARB and associated macros.
from copy import deepcopy
line=infile.readline().strip()
nums = line.split()
self.carb.iprtype = int(nums[0])
line=infile.readline().strip()
while not (line.startswith('endcarb') or line.startswith('end carb') or line.startswith('co2end')):
if line.startswith('brine'):
self.carb.brine = 1
elif line.startswith('co2frac'):
self._read_macro(infile,'co2frac')
elif line.startswith('co2flow'):
self._read_macro(infile,'co2flow')
elif line.startswith('co2pres'):
self._read_macro(infile,'co2pres')
elif line.startswith('co2diff'):
self._read_macro(infile,'co2diff')
line=infile.readline().strip()
def _write_carb(self,outfile): #Writes CARB and associated macros.
ws = _title_string('CO2 MODULE',72)
outfile.write(ws)
outfile.write('restart\n\n')
if self.sticky_zones:
zns = []
for key in ['co2frac','co2flow','co2pres','co2diff']:
for m in self._allMacro[key]:
if isinstance(m.zone,fzone):
if m.zone.index : zns.append(m.zone)
elif isinstance(m.zone,list) and len(m.zone) != 0:
for zn in m.zone:
if zn.index : zns.append(zn)
self._write_zonn_one(outfile,list(set(zns)))
outfile.write('carb\n')
outfile.write(str(self.carb.iprtype)+'\n')
sz = copy(self.sticky_zones)
self.sticky_zones = False
if self.co2fraclist: self._write_macro(outfile,'co2frac')
if self.co2flowlist: self._write_macro(outfile,'co2flow')
if self.co2preslist: self._write_macro(outfile,'co2pres')
if self.co2difflist: self._write_macro(outfile,'co2diff')
if self.carb.brine: outfile.write('brine\n')
self.sticky_zones = copy(sz)
outfile.write('endcarb\n')
self._write_general_macro(outfile, 'carb')
def _read_cont(self,infile): #CONT: Reads CONT macro.
line=infile.readline().strip()
nums = line.split()
for i in range(4-len(nums)): nums.append(None)
self._cont = fcont()
if nums[0] in ['avs','avsx','fehm','free','surf','tec','sur']:
self.cont.format=nums[0]
self.cont.timestep_interval=int(float(nums[1]))
self.cont.time_interval=float(nums[2])
if nums[3] == None: self.cont.time_flag=False
else: self.cont.time_flag=True
line=infile.readline().strip()
while not line.startswith('end'):
gotVar = False
if line.startswith('zone'):
line=infile.readline().strip()
nums = line.split()
num_zones = int(float(nums[0]))
moreZones = True
while moreZones:
line=infile.readline().strip()
nums = line.split()
for num in nums: self.cont.zones.append(self.zone[int(num)])
if len(self.cont.zones) == num_zones: moreZones=False; line=infile.readline().strip()
for vars,ln in zip(contour_variables,[4,3,2,1]):
for var in vars:
if line[:ln]==var[:ln]:
gotVar = True
self.cont.variables.append([var])
if gotVar: break
if gotVar: break
line=infile.readline().strip()
def _write_cont(self,outfile): #Writes CONT macro.
ws = _title_string('CONTOUR OUTPUT REQUESTS',72)
self.cont.variables = list(flatten(self.cont.variables))
outfile.write(ws)
outfile.write('cont\n')
outfile.write(self.cont.format+'\t')
outfile.write(str(self.cont.timestep_interval)+'\t')
outfile.write(str(self.cont.time_interval)+'\t')
if self.cont.time_flag: outfile.write('time\t')
outfile.write('\n')
if self.cont.zones:
outfile.write('zone\n')
outfile.write(str(len(self.cont.zones))+'\n')
for zn in self.cont.zones: outfile.write(str(zn.index)+'\t')
outfile.write('\n')
# ensure list is unique
vars = list(flatten(self.cont.variables))
varsU = []
for var in vars:
if var not in varsU: varsU.append(var)
self.cont.variables = varsU
for var in self.cont.variables:
outfile.write(var+'\n')
outfile.write('end\n')
self._write_general_macro(outfile, 'cont')
def _read_ctrl(self,infile): #CTRL: Reads CTRL macro.
line=infile.readline().strip()
nums = line.split()
for i in range(5-len(nums)): nums.append(None)
for num, param in zip(nums,['max_newton_iterations_MAXIT','newton_cycle_tolerance_EPM','number_orthogonalizations_NORTH','max_solver_iterations_MAXSOLVE','acceleration_method_ACCM']):
if not num: self.ctrl[param] = num
else:
if param[-4:]=='ACCM':self.ctrl[param] = num
else: self.ctrl[param] = float(num)
line=infile.readline().strip()
nums = line.split()
for i in range(3-len(nums)): nums.append(None)
for num, param in zip(nums,['JA','JB','JC','order_gauss_elim_NAR']):
if not num: self.ctrl[param] = num
else: self.ctrl[param] = int(num)
line=infile.readline().strip()
line=infile.readline().strip()
nums = line.split()
for i in range(3-len(nums)): nums.append(None)
for num, param in zip(nums,['implicitness_factor_AAW','gravity_direction_AGRAV','upstream_weighting_UPWGT']):
if not num: self.ctrl[param] = num
else:
if param[-5:]=='UPWGT':self.ctrl[param] = float(num)
else: self.ctrl[param] = int(float(num))
line=infile.readline().strip()
nums = line.split()
for i in range(4-len(nums)): nums.append(None)
for num, param in zip(nums,['max_multiply_iterations_IAMM','timestep_multiplier_AIAA','min_timestep_DAYMIN','max_timestep_DAYMAX']):
if not num: self.ctrl[param] = num
else: self.ctrl[param] = float(num)
line=infile.readline().strip()
nums = line.split()
for i in range(2-len(nums)): nums.append(None)
for num, param in zip(nums,['geometry_ICNL','stor_file_LDA']):
if not num: self.ctrl[param] = num
else: self.ctrl[param] = int(num)
self.dtmin = self.ctrl['min_timestep_DAYMIN']
self.dtmax = self.ctrl['max_timestep_DAYMAX']
self.dtx = self.ctrl['timestep_multiplier_AIAA']
def _write_ctrl(self,outfile): #Writes CTRL macro.
ws = _title_string('SIMULATION CONTROL PARAMETERS',72)
outfile.write(ws)
outfile.write('ctrl\n')
if not self.ctrl['max_newton_iterations_MAXIT']==None: outfile.write(str(int(self.ctrl['max_newton_iterations_MAXIT']))+'\t')
if not self.ctrl['newton_cycle_tolerance_EPM']==None: outfile.write(str(self.ctrl['newton_cycle_tolerance_EPM'])+'\t')
if not self.ctrl['number_orthogonalizations_NORTH']==None: outfile.write(str(int(self.ctrl['number_orthogonalizations_NORTH']))+'\t')
if not self.ctrl['max_solver_iterations_MAXSOLVE']==None: outfile.write(str(int(self.ctrl['max_solver_iterations_MAXSOLVE']))+'\t')
if not self.ctrl['acceleration_method_ACCM']==None: outfile.write(str(self.ctrl['acceleration_method_ACCM'])+'\t')
outfile.write('\n')
if not self.ctrl['JA']==None: outfile.write(str(self.ctrl['JA'])+'\t')
if not self.ctrl['JB']==None: outfile.write(str(self.ctrl['JB'])+'\t')
if not self.ctrl['JC']==None: outfile.write(str(self.ctrl['JC'])+'\t')
if not self.ctrl['order_gauss_elim_NAR']==None: outfile.write(str(self.ctrl['order_gauss_elim_NAR'])+'\t')
outfile.write('\n')
outfile.write('\n')
if not self.ctrl['implicitness_factor_AAW']==None: outfile.write(str(self.ctrl['implicitness_factor_AAW'])+'\t')
if not self.ctrl['gravity_direction_AGRAV']==None: outfile.write(str(self.ctrl['gravity_direction_AGRAV'])+'\t')
if not self.ctrl['upstream_weighting_UPWGT']==None: outfile.write(str(self.ctrl['upstream_weighting_UPWGT'])+'\t')
outfile.write('\n')
if not self.ctrl['max_multiply_iterations_IAMM']==None: outfile.write(str(int(self.ctrl['max_multiply_iterations_IAMM']))+'\t')
if not self.ctrl['timestep_multiplier_AIAA']==None: outfile.write(str(self.ctrl['timestep_multiplier_AIAA'])+'\t')
if not self.ctrl['min_timestep_DAYMIN']==None: outfile.write(str(self.ctrl['min_timestep_DAYMIN'])+'\t')
if not self.ctrl['max_timestep_DAYMAX']==None: outfile.write(str(self.ctrl['max_timestep_DAYMAX'])+'\t')
outfile.write('\n')
if not self.ctrl['geometry_ICNL']==None: outfile.write(str(self.ctrl['geometry_ICNL'])+'\t')
if not self.ctrl['stor_file_LDA']==None: outfile.write(str(self.ctrl['stor_file_LDA'])+'\t')
outfile.write('\n')
self._write_general_macro(outfile, 'ctrl')
[docs] def print_ctrl(self): #Prints out variables contained in CTRL.
'''Display contents of CTRL macro.
'''
for k in self.ctrl.keys(): print k +': ' + str(self.ctrl[k])
def _read_hist(self,infile): #HIST: Reads HIST macro.
line=infile.readline().strip()
nums = line.split()
if len(nums)<2: nums.append(1)
if len(nums)<3: nums.append(1e30)
if nums[0] in ['csv','surf','tec']:
self.hist.format=nums[0]
self.hist.timestep_interval=int(nums[1])
self.hist.time_interval=float(nums[2])
line=infile.readline().strip()
while not line.startswith('end'):
gotVar = False
for var in history_variables:
if line.startswith(var):
self.hist.variables.append([var])
break
line=infile.readline()
def _read_histnode(self,infile): #Reads NODE macro.
line=infile.readline().strip()
nums = line.split()
if nums[0].isdigit():
node_num = int(nums[0])
more = True
newind = 0
numXYZ = 0
while more:
line=infile.readline().strip()
nums = line.split()
for num in nums:
if int(num)>0:
self.hist.nodelist.append(self.grid.node[int(num)])
else:
numXYZ += 1
newind += 1
if newind == node_num: more = False
for i in range(numXYZ):
line=infile.readline().strip()
nums = line.split()
self.hist.nodelist.append(self.grid.node_nearest_point([float(num) for num in nums]))
elif 'block' == nums[0]:
more = True
while more:
line=infile.readline().strip()
if not line: more = False; continue
nums = line.split()
nums_zone,nums_params = nums[:3],nums[3:]
self.hist.zonelist.append(self._macro_zone(nums_zone))
elif 'azone' == nums[0]:
more = True
while more:
line=infile.readline().strip()
if not line: more = False; continue
nums = line.split()
nums_zone,nums_params = nums[:3],nums[3:]
self.hist.azonelist.append(self._macro_zone(nums_zone))
def _read_flxz(self,infile): #Reads ZFLX macro.
line=infile.readline().strip()
# self._hist = fhist()
nums = line.split()
node_num = int(nums[0])
more = True
newind = 0
numXYZ = 0
while more:
line=infile.readline().strip()
nums = line.split()
for num in nums:
if self.zone.has_key(int(num)):
self.hist.zoneflux.append(self.zone[int(num)])
else:
self.hist.zoneflux.append(int(num))
_buildWarnings('WARNING: zone ' +num+' in FLXZ not defined.')
newind += 1
if newind == node_num: more = False
def _read_head(self,infile,line): #HEAD: Reads HEAD macro.
line = line.rstrip().split()
if len(line) == 2:
self.head=float(line[-1])
else:
self.head=True
def _write_head(self,outfile): #Writes HEAD macro.
if self.head is True:
outfile.write('head\n')
else:
outfile.write('head\t'+str(self.head)+'\n')
self._write_general_macro(outfile, 'head')
def _read_flxn(self,infile,line): #HEAD: Reads FLXN macro.
self.head=True
def _write_flxn(self,outfile): #Writes FLXN macro.
outfile.write('flxn\n')
self._write_general_macro(outfile, 'flxn')
def _write_hist(self,outfile): #Writes HIST macro.
if not self.hist.nodelist and not self.hist.zonelist and not self.hist.azonelist and not self.hist.zoneflux: _buildWarnings('WARNING: no zones or nodes specified for history output'); return
if not self.hist.variables: _buildWarnings('WARNING: no variables requested in hist')
ws = _title_string('HISTORY OUTPUT REQUESTS',72)
outfile.write(ws)
# ensure list is unique
vars = list(flatten(self.hist.variables))
varsU = []
for var in vars:
if var not in varsU: varsU.append(var)
self.hist.variables = varsU
znlist = []
if self.hist.zoneflux:
self.hist.zoneflux = list(flatten(self.hist.zoneflux))
zns = [] # convert names and indices to zone objects
for zn in self.hist.zoneflux:
if isinstance(zn,int) or isinstance(zn,str): zns.append(self.zone[zn])
elif isinstance(zn,fzone): zns.append(zn)
self.hist.zoneflux = zns
zns = []; znsInds = [] # remove duplicate zones
for zn in self.hist.zoneflux:
if zn.index in znsInds: continue
zns.append(zn); znsInds.append(zn.index)
self.hist.zoneflux = zns
self._write_zonn_one(outfile,self.hist.zoneflux)
outfile.write('flxz\n')
outfile.write(str(len(self.hist.zoneflux))+'\n')
cnt = 0
for zn in self.hist.zoneflux:
outfile.write(str(zn.index)+'\t')
cnt += 1
if cnt == 10: outfile.write('\n'); cnt = 0
if cnt != 0: outfile.write('\n')
self._write_unparsed(outfile,'flxz')
znlist = []
if self.hist.zonelist:
zns = []
for zn in self.hist.zonelist:
if isinstance(zn,int) or isinstance(zn,str): zns.append(self.zone[zn])
elif isinstance(zn,fzone): zns.append(zn)
self._write_zonn_one(outfile,zns)
outfile.write('node\n')
outfile.write('block\n')
for zn in zns:
if isinstance(zn,int) or isinstance(zn,str): zn = self.zone[zn]
if isinstance(zn,tuple):
outfile.write(str(zn[0])+'\t'+str(zn[1])+'\t'+str(zn[2])+'\t')
else:
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
outfile.write('\n')
outfile.write('\n')
if self.hist.azonelist:
zns = []
for zn in self.hist.azonelist:
if isinstance(zn,int) or isinstance(zn,str): zns.append(self.zone[zn])
elif isinstance(zn,fzone): zns.append(zn)
self._write_zonn_one(outfile,zns)
outfile.write('node\n')
outfile.write('azone\n')
for zn in zns:
if isinstance(zn,int) or isinstance(zn,str): zn = self.zone[zn]
if isinstance(zn,tuple):
outfile.write(str(zn[0])+'\t'+str(zn[1])+'\t'+str(zn[2])+'\t')
else:
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
outfile.write('\n')
outfile.write('\n')
self._write_unparsed(outfile,'node')
if self.hist.nodelist:
self.hist.nodelist = list(flatten(self.hist.nodelist))
nds = []; ndsInds = []
for nd in self.hist.nodelist:
if isinstance(nd,int):
if nd not in ndsInds:
nds.append(self.grid.node[nd])
ndsInds.append(nd)
elif isinstance(nd,fnode):
if nd.index not in ndsInds:
nds.append(nd)
ndsInds.append(nd.index)
self.hist.nodelist = nds
outfile.write('node\n')
outfile.write(str(len(self.hist.nodelist))+'\n')
cnt = 0
for nd in self.hist.nodelist:
outfile.write(str(nd.index)+'\t')
cnt += 1
if cnt == 10: outfile.write('\n'); cnt = 0
if cnt != 0: outfile.write('\n')
outfile.write('hist\n')
if self.hist.format:
outfile.write(self.hist.format+'\t')
outfile.write(str(self.hist.timestep_interval)+'\t')
outfile.write(str(self.hist.time_interval)+'\t')
outfile.write('\n')
vars = list(flatten(self.hist.variables))
for var in vars:
outfile.write(var+'\n')
outfile.write('end\n')
self._write_general_macro(outfile, 'hist')
def _read_flxo(self,infile): #FLXO: Reads FLXO macro.
line=infile.readline().strip()
nums = line.split()
if nums[0].isdigit():
node_num = int(nums[0])*2
more = True
newind = 0
numXYZ = 0
nds = []
ndsXYZ = []
while more:
line=infile.readline().strip()
nums = line.split()
for num in nums:
nds.append(int(num))
if int(num)<0:
numXYZ += 1
newind += 1
if newind == node_num: more = False
for i in range(numXYZ):
line=infile.readline().strip()
nums = line.split()
self.grid.node_nearest_point([float(num) for num in nums])
nds[np.where(nds<0)[0][0]] = self.grid.node_nearest_point([float(num) for num in nums]).index
nds = [self.grid.node[nd] for nd in nds]
for i1,i2 in zip(range(0,node_num,2),range(0,node_num,2)):
self.flxo.append((nds[i1],nds[i2]))
def _write_flxo(self,outfile): #Writes FLXO macro.
outfile.write('flxo\n')
outfile.write(str(len(self.flxo))+'\n')
for nd1,nd2 in self.flxo:
if isinstance(nd1,fnode): nd1 = nd1.index
if isinstance(nd2,fnode): nd2 = nd2.index
outfile.write(str(nd1)+'\t'+str(nd2)+'\n')
self._write_general_macro(outfile, 'flxo')
def _read_iter(self,infile): #ITER: Reads ITER macro.
line=infile.readline().strip()
nums = line.split()
for i in range(5-len(nums)): nums.append(None)
for num, param in zip(nums,['linear_converge_NRmult_G1','quadratic_converge_NRmult_G2','stop_criteria_NRmult_G3','machine_tolerance_TMCH','overrelaxation_factor_OVERF']):
if not num: self.iter[param] = num
else: self.iter[param] = float(num)
line=infile.readline().strip()
nums = line.split()
for i in range(5-len(nums)): nums.append(None)
for num, param in zip(nums,['reduced_dof_IRDOF','reordering_param_ISLORD','IRDOF_param_IBACK','number_SOR_iterations_ICOUPL','max_machine_time_RNMAX']):
if not num: self.iter[param] = num
else:
if not param.endswith('RNMAX'): self.iter[param] = int(num)
else: self.iter[param] = float(num)
def _write_iter(self,outfile): #Writes ITER macro.
ws = _title_string('SOLVER PARAMETERS',72)
outfile.write(ws)
outfile.write('iter\n')
if not self.iter['linear_converge_NRmult_G1']==None: outfile.write(str(self.iter['linear_converge_NRmult_G1'])+'\t')
if not self.iter['quadratic_converge_NRmult_G2']==None: outfile.write(str(self.iter['quadratic_converge_NRmult_G2'])+'\t')
if not self.iter['stop_criteria_NRmult_G3']==None: outfile.write(str(self.iter['stop_criteria_NRmult_G3'])+'\t')
if not self.iter['machine_tolerance_TMCH']==None: outfile.write(str(self.iter['machine_tolerance_TMCH'])+'\t')
if not self.iter['overrelaxation_factor_OVERF']==None: outfile.write(str(self.iter['overrelaxation_factor_OVERF'])+'\t')
outfile.write('\n')
if not self.iter['reduced_dof_IRDOF']==None: outfile.write(str(self.iter['reduced_dof_IRDOF'])+'\t')
if not self.iter['reordering_param_ISLORD']==None: outfile.write(str(self.iter['reordering_param_ISLORD'])+'\t')
if not self.iter['IRDOF_param_IBACK']==None: outfile.write(str(self.iter['IRDOF_param_IBACK'])+'\t')
if not self.iter['number_SOR_iterations_ICOUPL']==None: outfile.write(str(self.iter['number_SOR_iterations_ICOUPL'])+'\t')
if not self.iter['max_machine_time_RNMAX']==None: outfile.write(str(self.iter['max_machine_time_RNMAX'])+'\t')
outfile.write('\n')
self._write_general_macro(outfile, 'iter')
[docs] def print_iter(self): #Prints out variables contained in ITER.
'''Display contents of ITER macro.
'''
for k in self.iter.keys(): print k +': ' + str(self.iter[k])
def _read_nfinv(self,infile): #NFINV: Reads NFINV macro.
self.nfinv=True
def _write_nfinv(self,outfile): #Writes NFINV macro.
if self.nfinv:
outfile.write('nfinv\n')
self._write_general_macro(outfile, 'nfinv')
def _read_nobr(self,infile): #NOBR: Reads NOBR macro.
self.nobr=True
def _write_nobr(self,outfile): #Writes NOBR macro.
if self.nobr:
outfile.write('nobr\n')
self._write_general_macro(outfile, 'nobr')
def _read_rlpm(self,infile): #RLPM: Reads RLPM macro.
moreGroups=True
line=infile.readline().strip().split()
# first read the models
rlpms = []
while moreGroups:
group = line[1]
moreModels = True
relperms = []; capillarys = []
while moreModels:
line=infile.readline().strip().split()
if not line: moreModels=False;moreGroups=False; continue
elif line[0].startswith('end'): moreModels=False;moreGroups=False; continue
if line[0]=='group': moreModels=False; continue
if line[0] in rlpm_phases: relperms.append([line[0],line[1],line[2:]])
elif '/' in line[1]: capillarys.append([tuple(line[1].split('/')),rlpm_cap2[line[2]],line[3:]])
newrlpm = frlpm(group=group,relperm=relperms,capillary=capillarys)
rlpms.append(newrlpm)
rlpms = dict([(rlpm.group,rlpm) for rlpm in rlpms])
# next read the zones
moreZones=True
while moreZones:
line=infile.readline().strip().split()
if not line: moreZones=False; continue
rlpms[line[-1]].zone = self._macro_zone(line[:3])
self._rlpmlist = rlpms.values()
def _write_rlpm(self,outfile): #Writes RLPM macro.
ws = _title_string(macro_titles['rlpm'],72)
outfile.write(ws)
if self.sticky_zones:
allzns = []
for bm in self.rlpmlist:
if isinstance(bm.zone,fzone): bm.zone = [bm.zone,]
for zn in bm.zone:
if zn.index != 0: allzns.append(zn)
self._write_zonn_one(outfile,allzns)
outfile.write('rlpm\n')
self._rlpmlist.sort(key=lambda x: x.group)
table_number = 1
for rlpm in self._rlpmlist:
if isinstance(rlpm,frlpm):
outfile.write('group '+str(int(rlpm.group))+'\n')
for relperm in rlpm.relperm.values():
outfile.write(relperm.phase+'\t')
outfile.write(relperm.type+'\t')
for k in rlpm_dicts[relperm.type]:
outfile.write(str(relperm.param[k[0]])+'\t')
outfile.write('\n')
for capillary in rlpm.capillary.values():
outfile.write('cap\t')
outfile.write(capillary.phase[0]+'/'+capillary.phase[1]+'\t')
outfile.write(rlpm_cap1[capillary.type]+'\t')
for k in rlpm_dicts[capillary.type]:
outfile.write(str(capillary.param[k[0]])+'\t')
outfile.write('\n')
#outfile.write('\n')
elif isinstance(rlpm,frlpm_table):
outfile.write('group '+str(int(rlpm.group))+'\n')
# table number
outfile.write('table\t'+str(table_number)+'\t')
table_number +=1
# table width
wid = 3
if len(rlpm.capillary)>0: wid = 4
outfile.write(str(wid)+'\t'+rlpm.phase1[0]+'\t'+rlpm.phase2[0]+'\t')
if wid == 4:
outfile.write(rlpm.phase1[0]+'/'+rlpm.phase2[0])
outfile.write('\n')
if wid == 3:
for s, ph1,ph2 in zip(rlpm.saturation,rlpm.phase1[1],rlpm.phase2[1]):
outfile.write(str(s)+'\t'+str(ph1)+'\t'+str(ph2)+'\n')
elif wid == 4:
for s, ph1,ph2,cap in zip(rlpm.saturation,rlpm.phase1[1],rlpm.phase2[1],rlpm.capillary):
outfile.write(str(s)+'\t'+str(ph1)+'\t'+str(ph2)+'\t'+str(cap)+'\n')
outfile.write('\n')
for rlpm in self._rlpmlist:
for zone in rlpm.zone:
if isinstance(zone,fzone):
ind = zone.index
if not ind: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-ind)+'\t'+'0\t0\t')
outfile.write(str(rlpm.group)+'\n')
else:
outfile.write(str(zone[0])+'\t'+str(zone[1])+'\t'+str(zone[2])+'\t')
outfile.write(str(rlpm.group)+'\n')
outfile.write('\n')
self._write_general_macro(outfile, 'rlpm')
def _add_rlpm(self,rlpm=frlpm()):
rlpm._parent = self
if rlpm.group == None:
gps = [r.group for r in self._rlpmlist]
for i in range(1,np.max(gps)+2):
if i not in gps: rlpm.group = i; break
self._rlpmlist.append(rlpm)
def _delete_rlpm(self,rlpm=frlpm()):
self._rlpmlist.remove(rlpm)
def _read_sol(self,infile): #SOL: Reads SOL macro.
line=infile.readline().strip()
nums = line.split()
for i in range(2-len(nums)): nums.append(None)
for num, param in zip(nums,['coupling_NTT','element_integration_INTG']):
if not num: self.sol[param] = num
else: self.sol[param] = int(num)
def _write_sol(self,outfile): #Writes SOL macro.
ws = _title_string('SOLUTION TYPE',72)
outfile.write(ws)
outfile.write('sol\n')
if not self.sol['coupling_NTT']==None: outfile.write(str(self.sol['coupling_NTT'])+'\t')
if not self.sol['element_integration_INTG']==None: outfile.write(str(self.sol['element_integration_INTG'])+'\t')
outfile.write('\n')
self._write_general_macro(outfile, 'sol')
def _read_strs(self,infile): #STRS: Reads STRS and associated macros.
from copy import deepcopy
line=infile.readline().strip()
nums = line.split()
self.strs.param['ISTRS'] = int(nums[0])
self.strs.param['IHMS'] = int(nums[1])
try:
self.strs.param['porosity_factor'] = float(nums[2])
except:
self.strs.param['porosity_factor'] = None
line=infile.readline().strip()
while not line.startswith('stressend'):
if line.startswith('bodyforce'): # bodyforce boolean
if line.endswith('force'):
self._read_macro(infile,'bodyforce')
for m in self.bodyforcelist: m.subtype = 'force'
elif line.endswith('acceleration'):
self._read_macro(infile,'bodyforce')
for m in self.bodyforcelist: m.subtype = 'acceleration'
else:
self.strs.bodyforce = True
elif line.startswith('initcalc'): # initcalc boolean
self.strs.initcalc = 1
elif line.startswith('fem'): # fem boolean
self.strs.fem = 1
elif line.startswith('excess_she'): # reporting on excess shear stress?
line=infile.readline().strip()
nums = line.split()
for i in range(3-len(nums)): nums.append(None)
self.strs.excess_she['PAR1']=nums[0]
self.strs.excess_she['PAR2']=nums[1]
self.strs.excess_she['PAR3']=nums[2]
elif line.startswith('permmodel'): # read details of stress/permeability model
self._read_model(infile,'permmodel')
elif line.startswith('plastic'): # read details of stress/permeability model
self._read_model(infile,'plastic')
elif line.startswith('elastic'):
self._read_macro(infile,'elastic')
elif line.startswith('stressboun'):
self._read_macro(infile,'stressboun')
elif line.startswith('biot'):
self._read_macro(infile,'biot')
elif line.startswith('tolerance'):
line = infile.readline().strip()
nums = line.split()
self.strs.tolerance = float(nums[0])
elif line.startswith('zonn') or line.startswith('zone'):
self._read_zonn(infile)
line=infile.readline().strip()
def _write_strs(self,outfile): #Writes STRS and associated macros.
ws = _title_string('STRESS MODULE',72)
outfile.write(ws)
outfile.write('strs\n')
outfile.write(str(self.strs.param['ISTRS'])+'\t')
outfile.write(str(self.strs.param['IHMS'])+'\t')
if self.strs.param['porosity_factor'] is not None:
outfile.write(str(self.strs.param['porosity_factor'])+'\t')
outfile.write('\n')
if self.bodyforcelist: self._write_macro(outfile,'bodyforce')
elif self.strs.bodyforce: outfile.write('bodyforce\n')
if self.strs.initcalc: outfile.write('initcalc\n')
if self.strs.fem: outfile.write('fem\n')
if self.strs.excess_she['PAR1']:
outfile.write('excess_she\n')
outfile.write(str(self.strs.excess_she['PAR1'])+'\t')
if self.strs.excess_she['PAR2']: outfile.write(str(self.strs.excess_she['PAR2'])+'\t')
if self.strs.excess_she['PAR3']: outfile.write(str(self.strs.excess_she['PAR3'])+'\t')
outfile.write('\n')
if self.permmodellist: self._write_model(outfile,'permmodel')
if self.plasticmodellist: self._write_model(outfile,'plastic')
if self.stressbounlist: self._write_macro(outfile,'stressboun')
if self.elasticlist: self._write_macro(outfile,'elastic')
if self.biotlist: self._write_macro(outfile,'biot')
outfile.write('tolerance\n')
outfile.write(str(self.strs.tolerance)+'\n')
outfile.write('stressend\n')
self._write_general_macro(outfile, 'strs')
def _read_ngas(self,infile): #NGAS: Reads NGAS and associated macros.
line = infile.readline().strip().split()
self.ngas.dof = int(float(line[0]))
# read initial pressures
line = infile.readline().strip()
while line:
nums = line.split()
self.ngas.add_init_pres(self._macro_zone(nums[:3]),float(nums[-1]))
line = infile.readline().strip()
# read ncg pressures
line = infile.readline().strip()
while line:
nums = line.split()
self.ngas.add_ncg_pres(self._macro_zone(nums[:3]),float(nums[-1]))
line = infile.readline().strip()
# read sources
line = infile.readline().strip()
while line:
nums = line.split()
self.ngas.add_source(self._macro_zone(nums[:3]),float(nums[-1]))
line = infile.readline().strip()
def _write_ngas(self,outfile): #Writes NGAS and associated macros.
ws = _title_string('NONCONDENSIBLE GAS MODULE',72)
outfile.write(ws)
if self.sticky_zones:
zns = []
zns += self.ngas.init_pres.keys()
zns += self.ngas.ncg_pres.keys()
zns += self.ngas.source.keys()
zns2 = []
for zn in zns:
if isinstance(zn,tuple): zns2.append(zn)
elif isinstance(zn,(int,str)):
if zn in self.zone.keys():
zns2.append(self.zone[zn])
else: _buildWarnings('WARNING: no zone '+str(zn)+ ' found')
self._write_zonn_one(outfile,list(set(zns2)))
outfile.write('ngas\n')
outfile.write(str(int(self.ngas.dof))+'\n')
if 0 in self.ngas.init_pres.keys():
outfile.write('1\t0\t0\t'+str(self.ngas.init_pres[0])+'\n')
for k in self.ngas.init_pres.keys():
if k == 0: continue
if isinstance(k,tuple):
outfile.write(str(k[0])+'\t'+str(k[1])+'\t'+str(k[1])+'\t'+str(self.ngas.init_pres[k])+'\n')
elif isinstance(k, int):
outfile.write('-'+str(k)+'\t0\t0\t'+str(self.ngas.init_pres[k])+'\n')
outfile.write('\n')
if 0 in self.ngas.ncg_pres.keys():
outfile.write('1\t0\t0\t'+str(self.ngas.ncg_pres[0])+'\n')
for k in self.ngas.ncg_pres.keys():
if k == 0: continue
if isinstance(k,tuple):
outfile.write(str(k[0])+'\t'+str(k[1])+'\t'+str(k[1])+'\t'+str(self.ngas.ncg_pres[k])+'\n')
elif isinstance(k, int):
outfile.write('-'+str(k)+'\t0\t0\t'+str(self.ngas.ncg_pres[k])+'\n')
outfile.write('\n')
if 0 in self.ngas.source.keys():
outfile.write('1\t0\t0\t'+str(self.ngas.source[0])+'\n')
for k in self.ngas.source.keys():
if k == 0: continue
if isinstance(k,tuple):
outfile.write(str(k[0])+'\t'+str(k[1])+'\t'+str(k[1])+'\t'+str(self.ngas.source[k])+'\n')
elif isinstance(k, int):
outfile.write('-'+str(k)+'\t0\t0\t'+str(self.ngas.source[k])+'\n')
outfile.write('\n')
self._write_general_macro(outfile, 'ngas')
def _read_text(self,infile): #TEXT: Reads TEXT macro.
more=True
line=infile.readline().strip()
new_text = []
while more:
new_text.append(line)
line=infile.readline().strip()
if not line: more=False
self.text.append(new_text)
def _write_text(self,outfile): #Writes TEXT macro.
for text in self.text:
outfile.write('text\n')
for line in text: outfile.write(line)
outfile.write('\n\n')
self._write_general_macro(outfile, 'text')
def _read_trac(self,infile): #TRAC: Reads TRAC and associated macros.
self.trac._on = True
# group 1
nums=infile.readline().strip().split()
self.trac.param['init_solute_conc_ANO'] = float(nums[0])
self.trac.param['implicit_factor_AWC'] = float(nums[1])
self.trac.param['tolerance_EPC'] = float(nums[2])
self.trac.param['upstream_weight_UPWGTA'] = float(nums[3])
# group 2
nums=infile.readline().strip().split()
self.trac.param['solute_start_DAYCS'] = float(nums[0])
self.trac.param['solute_end_DAYCF'] = float(nums[1])
self.trac.param['flow_end_DAYHF'] = float(nums[2])
self.trac.param['flow_start_DAYHS'] = float(nums[3])
# group 3
line=infile.readline().strip()
nums = line.split()
self.trac.param['max_iterations_IACCMX'] = float(nums[0])
self.trac.param['timestep_multiplier_DAYCM'] = float(nums[1])
self.trac.param['initial_timestep_DAYCMM'] = float(nums[2])
self.trac.param['max_timestep_DAYCMX'] = float(nums[3])
self.trac.param['print_interval_NPRTTRC'] = float(nums[4])
# groups 4 and 5 (if applicable)
line=infile.readline().strip()
if line.startswith('tpor'):
nums=infile.readline().strip().split()
self.trac.transport_porosity = float(nums[-1])
line=infile.readline().strip()
line=infile.readline().strip()
# group 6
num_species = int(line.split()[0])
# group 7
line=infile.readline().strip()
if line.startswith('ldsp'):
self.trac.ldsp = True
line=infile.readline().strip()
# groups 8, 9 and 10
sorptionOnly = False
keepReading = True
if line.startswith('dspl') or line.startswith('dspv'):
sorptionOnly = True
# read models
while keepReading:
line=infile.readline().strip()
if not line: break
nums = line.split()
if not self.trac.ldsp:
self.trac.add_common_model(diffusion_model=int(float(nums[0])),
diffusion = float(nums[1]),
dispersion = [float(nums[2]),float(nums[3]),float(nums[4])])
else:
self.trac.add_common_model(diffusion_model=int(float(nums[0])),
diffusion = float(nums[1]),
dispersion = [float(nums[2]),float(nums[3]),None])
# read model-zone assignment
zns = []; inds = []
while keepReading:
line=infile.readline().strip()
if not line: break
nums = line.split()
zns.append(self._macro_zone(nums))
inds.append(int(float(nums[-1])))
for zn,ind in zip(zns,inds):
self.trac.common_modellist[ind-1].zone = zn
line=infile.readline().strip()
self.trac.common_modellist.sort(key=lambda x: x.zone.index)
# group 11 - 16
for i in range(num_species):
nums=line.split()
phase = int(float(nums[0]))
if phase:
models1,models2 = self._read_model_trac(infile,self.trac.ldsp)
'''
UNDER CONSTRUCTION !!!
'''
#nums=infile.readline().strip().split()
#adsorption_model=None
#adsorption=None
#diffusion_model=None
#diffusion=None
#dispersion=None
#adsorption_model = int(float(nums[0]))
#adsorption = [float(nums[1]),float(nums[2]),float(nums[3])]
#if not sorptionOnly:
# if len(nums) == 9: diffusion_model = int(float(nums[4])); nums = nums[5:]
# elif len(nums) == 8: diffusion_model = 0; nums = nums[4:]
# diffusion = float(nums[0])
# if self.trac.ldsp:
# dispersion = [float(nums[1]),float(nums[2]),None]
# else:
# dispersion = [float(nums[1]),float(nums[2]),float(nums[3])]
#
#line=infile.readline().strip()
#line=infile.readline().strip()
#line=infile.readline().strip()
#line=infile.readline().strip()
self.trac.add_species(phase,adsorption_model,adsorption,diffusion_model,diffusion,dispersion)
# read in initial concentrations
while keepReading:
if not line: break
nums = line.split()
self.trac.specieslist[-1].add_tracer_concentration(zone=self._macro_zone(nums),
initial_concentration=float(nums[-1]))
line=infile.readline().strip()
# read in injection concentrations
while keepReading:
line=infile.readline().strip()
if not line: break
self.trac.specieslist[-1].add_tracer_generator(zone=self._macro_zone(nums),
injection_concentration=float(nums[3]),time_start=float(nums[4]),time_end=float(nums[5]))
line=infile.readline().strip()
def _write_trac(self,outfile): #Writes TRAC and associated macros.
ws = _title_string('TRACKING MODULE',72)
outfile.write(ws)
if self.sticky_zones:
if self.trac.file:
zns = self.trac.zonelist
else:
zns = []
for sp in self.trac.specieslist:
zns += [m.zone for m in sp._tracer_concentrationlist if m.zone.index]
zns += [m.zone for m in sp._tracer_generatorlist if m.zone.index]
self._write_zonn_one(outfile,list(set(zns)))
if self.trac.file: # write trac in stupid mode
if not os.path.isfile(self.trac.file):
pyfehm_print('ERROR: cannot find trac file at location '+self.trac.file+'. Aborting...',self._silent)
adsf
# open the auxiliary file and write it
fp = open(self.trac.file,'rU')
lns = fp.readlines()
# quality control
for i,ln in enumerate(lns):
if ln.startswith('trac'): break
lns = lns[i:]
fp.close()
outfile.writelines(lns)
outfile.write('\n')
return
outfile.write('trac\n')
# group 1
outfile.write(str(self.trac.param['init_solute_conc_ANO'])+'\t')
outfile.write(str(self.trac.param['implicit_factor_AWC'])+'\t')
outfile.write(str(self.trac.param['tolerance_EPC'])+'\t')
outfile.write(str(self.trac.param['upstream_weight_UPWGTA'])+'\n')
# group 2
outfile.write(str(self.trac.param['solute_start_DAYCS'])+'\t')
outfile.write(str(self.trac.param['solute_end_DAYCF'])+'\t')
outfile.write(str(self.trac.param['flow_end_DAYHF'])+'\t')
outfile.write(str(self.trac.param['flow_start_DAYHS'])+'\n')
# group 3
outfile.write(str(int(self.trac.param['max_iterations_IACCMX']))+'\t')
outfile.write(str(self.trac.param['timestep_multiplier_DAYCM'])+'\t')
outfile.write(str(self.trac.param['initial_timestep_DAYCMM'])+'\t')
outfile.write(str(self.trac.param['max_timestep_DAYCMX'])+'\t')
outfile.write(str(int(self.trac.param['print_interval_NPRTTRC']))+'\n')
# groups 4 and 5 (if applicable)
if self.trac.transport_porosity != -1:
outfile.write('tpor\n')
outfile.write('1 0 0 '+str(self.trac.transport_porosity)+'\n')
outfile.write('\n')
# group 6
outfile.write(str(self.trac.number_species)+'\n')
# group 7
if self.trac.ldsp:
outfile.write('ldsp\n')
# groups 8, 9 and 10
sorptionOnly = False
if self.trac.common_modellist: sorptionOnly = True
keepReading = True
if sorptionOnly:
outfile.write('dspl\n')
for cm in self.trac.common_modellist:
outfile.write(str(int(cm.diffusion_model))+'\t')
outfile.write(str(cm.diffusion)+'\t')
outfile.write(str(cm.dispersion[0])+'\t')
outfile.write(str(cm.dispersion[1])+'\t')
if not self.trac.ldsp: outfile.write(str(cm.dispersion[2])+'\t')
outfile.write('\n')
outfile.write('\n')
for i,cm in enumerate(self.trac.common_modellist):
zn = cm.zone
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
outfile.write(str(i+1)+'\n')
outfile.write('\n')
# group 11 - 16
for i,sp in enumerate(self.trac.specieslist):
# group 11
outfile.write(str(int(sp.phase))+'\n')
if sp.phase:
outfile.write(str(int(sp.adsorption_model))+'\t')
outfile.write(str(int(sp.adsorption[0]))+'\t')
outfile.write(str(int(sp.adsorption[1]))+'\t')
outfile.write(str(int(sp.adsorption[2]))+'\t')
if not sorptionOnly:
outfile.write(str(int(sp.diffusion_model))+'\t')
outfile.write(str(sp.diffusion)+'\t')
outfile.write(str(sp.dispersion[0])+'\t')
outfile.write(str(sp.dispersion[1])+'\t')
if not self.trac.ldsp: outfile.write(str(sp.dispersion[2])+'\t')
outfile.write('\n')
outfile.write('\n')
outfile.write('1 0 0 1 \n')
outfile.write('\n')
for ic in sp._tracer_concentrationlist:
zn = ic.zone
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
outfile.write(str(ic.initial_concentration)+'\n')
outfile.write('\n')
for ic in sp._tracer_generatorlist:
zn = ic.zone
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
outfile.write(str(ic.injection_concentration)+'\n')
outfile.write(str(ic.time_start)+'\n')
outfile.write(str(ic.time_end)+'\n')
outfile.write('\n')
# concentration dependent density - only the first will be written
for i,sp in enumerate(self.trac.specieslist):
if sp.density_modifier:
poutfile.write('cden\n')
outfile.write(str(i+1)+'\n')
outfile.write(str(sp.density_modifier)+'\n')
outfile.write('\n')
break
self._write_general_macro(outfile, 'trac')
def _read_time(self,infile): #TIME: Reads TIME macro.
line=infile.readline()
nums = line.split()
for i in range(7-len(nums)): nums.append(None)
for num, param in zip(nums,['initial_timestep_DAY','max_time_TIMS','max_timestep_NSTEP','print_interval_IPRTOUT','initial_year_YEAR','initial_month_MONTH','initial_day_INITTIME']):
if not num: self.time[param] = num
else: self.time[param] = float(num)
line=infile.readline().strip()
while line.strip():
line = line.split('!')[0]
nums = line.split()
time = np.array([float(num) for num in nums])
self._times.append(time)
line=infile.readline().strip()
self.tf = self.time['max_time_TIMS']
self.dtn = self.time['max_timestep_NSTEP']
self.dti = self.time['initial_timestep_DAY']
def _write_time(self,outfile): #Writes TIME macro.
ws = _title_string('TIME STEPPING PARAMETERS',72)
outfile.write(ws)
outfile.write('time\n')
if not self.time['initial_timestep_DAY']==None: outfile.write(str(self.time['initial_timestep_DAY'])+'\t')
if not self.time['max_time_TIMS']==None: outfile.write(str(self.time['max_time_TIMS'])+'\t')
if not self.time['max_timestep_NSTEP']==None: outfile.write(str(int(self.time['max_timestep_NSTEP']))+'\t')
if not self.time['print_interval_IPRTOUT']==None: outfile.write(str(int(self.time['print_interval_IPRTOUT']))+'\t')
if not self.time['initial_year_YEAR']==None: outfile.write(str(self.time['initial_year_YEAR'])+'\t')
if not self.time['initial_month_MONTH']==None: outfile.write(str(self.time['initial_month_MONTH'])+'\t')
if not self.time['initial_day_INITTIME']==None: outfile.write(str(self.time['initial_day_INITTIME'])+'\t')
outfile.write('\n')
if self.times:
for time in self.times:
for t_cpt in time: outfile.write(str(t_cpt)+'\t')
outfile.write('\n')
outfile.write('\n')
self._write_general_macro(outfile, 'time')
[docs] def print_time(self): #Prints out variables contained in TIME.
'''Display contents of TIME macro.
'''
for k in self.time.keys(): print k +': ' + str(self.time[k])
[docs] def change_timestepping(self,at_time,new_dti=None,new_dtmax=None,new_dtx=None,new_implicitness=None,new_print_out=None):
''' Change timestepping during a simulation. Note, if time stepping arguments are omitted, FEHM will force output
to be written at the change time. The default for all optional arguments is no change.
:param at_time: Simulation time to change time stepping behaviour.
:type at_time: fl64
:param new_dti: Initial time step at change time.
:type new_dti: fl64
:param new_dtmax: New maximum time step after change time.
:type new_dtmax: fl64
:param new_dtx: New time step multiplier at change time.
:type new_dtx: fl64
:param new_implicitness: New implicitness factor at change time.
:type new_implicitness: fl64
:param new_print_out: New time step interval at which to print information.
:type new_print_out: int
'''
# load old parameters
self._output_times = []
if self.times:
DIT1,DIT2,DIT3,ITC,DIT4 = self.times[-1]
if DIT2>0: DIT2a = DIT2; DIT2b = -self.dtx
else: DIT2b = DIT2; DIT2a = None
else:
DIT2b = -self.dtx
DIT3 = self.ctrl['implicitness_factor_AAW']
ITC = self.time['print_interval_IPRTOUT']
DIT4 = self.dtmax
# assign new parameters
DIT1 = at_time
if new_dti and new_dtx:
_buildWarnings('WARNING: you cannot specify BOTH a new time step size and time step multiplier. Ignoring the new multiplier...')
new_dtx = None
if new_dti: DIT2 = new_dti
elif new_dtx: DIT2 = -new_dtx
else: DIT2 = DIT2b
if new_implicitness: DIT3 = new_implicitness
if new_print_out: ITC = new_print_out
if new_dtmax: DIT4 = new_dtmax
self.times.append([DIT1,DIT2,DIT3,ITC,DIT4])
def _read_vapl(self,infile): #VAPL: Reads VAPL macro.
self.vapl=True
def _write_vapl(self,outfile): #Writes VAPL macro.
if self.vapl:
outfile.write('vapl\n')
self._write_general_macro(outfile, 'vapl')
[docs] def new_zone(self,index=None,name=None,rect=None,nodelist=None,file=None,from_file = None,permeability=None,conductivity=None,density=None,
specific_heat=None,porosity=None,youngs_modulus=None,poissons_ratio=None,thermal_expansion=None,pressure_coupling=None,
Pi=None,Ti=None,Si=None,overwrite=False):
''' Create and assign a new zone. Material properties are optionally specified, new macros will be created if required.
:param index: Zone index.
:type index: int
:param name: Zone name.
:type name: str
:param rect: Two item list. Each item is itself a three item (or two for 2D) list containing [x,y,z] coordinates of zone bounding box.
:type rect: lst
:param nodelist: List of node objects or indices of zone. Only one of rect or nodelist should be specified (rect will be taken if both given).
:type nodelist: lst
:param file: Name of auxiliary file for zone
:type file: str
:param from_file: Name of auxiliary file in which to find zone information.
:type from_file: str
:param permeability: Permeability of zone. One float for isotropic, three item list [x,y,z] for anisotropic.
:type permeability: fl64, list
:param conductivity: Conductivity of zone. One float for isotropic, three item list [x,y,z] for anisotropic.
:type conductivity: fl64, list
:param density: Density of zone. If not specified, defaults will be used for specific heat and porosity.
:type density: fl64
:param specific_heat: Specific heat of zone. If not specified, defaults will be used for density and porosity.
:type specific_heat: fl64
:param porosity: Porosity of zone. If not specified, defaults will be used for density and specific heat.
:type porosity: fl64
:param youngs_modulus: Young's modulus of zone. If not specified, default will be used for Poisson's ratio.
:type youngs_modulus: fl64
:param poissons_ratio: Poisson's ratio of zone. If not specified, default will be used for Young's modulus.
:type poissons_ratio: fl64
:param thermal_expansion: Coefficient of thermal expansion for zone. If not specified, default will be used for pressure coupling term.
:type thermal_expansion: fl64
:param pressure_coupling: Pressure coupling term for zone. If not specified, default will be used for coefficient of thermal expansion.
:type pressure_coupling: fl64
:param Pi: Initial pressure in the zone. If not specified, default will be used for initial temperature and saturation calculated.
:type Pi: fl64
:param Ti: Initial temperature in the zone. If not specified, default will be used for initial pressure and saturation calculated.
:type Ti: fl64
:param Si: Initial saturation in the zone. If not specified, default will be used for initial pressure and the saturation temperature calculated.
:type Si: fl64
:param overwrite: If zone already exists, delete it and create the new one.
:type overwrite: bool
'''
if index is None and from_file is None: return
# from file zones
if from_file:
if not os.path.isfile(from_file):
print 'ERROR: no such zone file '+from_file
return
self._read_zonn_file(from_file)
# if neither rect nor nodelist specified, not enough information to create the zone
if not rect and not nodelist:
pyfehm_print('ERROR: either rect or nodelist must be specified',self._silent)
return
# if both rect and nodelist are specified, proceed with rect, print warning
if rect and nodelist:
_buildWarnings('WARNING: only one of rect or nodelist should be specified, proceeding with rect values')
if nodelist:
if isinstance(nodelist,fnode) or isinstance(nodelist,int): nodelist = [nodelist]
nds = []
for nd in nodelist:
if isinstance(nd,int): nds.append(self.grid.node[nd])
else: nds.append(nd)
nodelist = nds
# determine if zone already exists, delete or exit
if index in self.zone.keys():
if overwrite:
self.delete(self.zone[index])
else:
print 'ERROR: specified zone already exists, overwrite flag set to false'
return
# assemble zone
zn = fzone(index=index,name=name,file=file)
if rect:
zn.rect(rect[0],rect[1])
elif nodelist:
zn.type = 'nnum'
nds = []
for nd in nodelist:
if isinstance(nd,int):
nds.append(self.grid.node[nd])
elif isinstance(nd,fnode):
nds.append(nd)
zn.nodelist = nds
self.add(zn)
# add permeability property macros
if permeability:
if not (isinstance(permeability,(int,float)) or (isinstance(permeability,list) and len(permeability) == 3)):
pyfehm_print('ERROR: permeability must be a single float (isotropic) or three item list ([x,y,z] anisotropic',self._silent)
return
# unpack values
if isinstance(permeability,(int,float)):
kx,ky,kz = [permeability,permeability,permeability]
else:
kx,ky,kz = permeability
# assign
if index in self.perm.keys():
self.perm[index].param['kx'] = kx
self.perm[index].param['ky'] = ky
self.perm[index].param['kz'] = kz
else:
macro = fmacro('perm',zone=index,param=(('kx',kx),('ky',ky),('kz',kz)))
self.add(macro)
self.zone[index]._permeability = permeability
# add conductivity property macros
if conductivity:
if not (isinstance(conductivity,(int,float)) or (isinstance(conductivity,list) and len(conductivity) == 3)):
pyfehm_print('ERROR: conductivity must be a single float (isotropic) or three item list ([x,y,z] anisotropic',self._silent)
return
# unpack values
if isinstance(conductivity,(int,float)):
kx,ky,kz = [conductivity,conductivity,conductivity]
else:
kx,ky,kz = conductivity
# assign
if index in self.cond.keys():
self.cond[index].param['cond_x'] = kx
self.cond[index].param['cond_y'] = ky
self.cond[index].param['cond_z'] = kz
else:
macro = fmacro('cond',zone=index,param=(('cond_x',kx),('cond_y',ky),('cond_z',kz)))
self.add(macro)
self.zone[index]._conductivity = conductivity
# add rock property macros
if density or specific_heat or porosity:
if not density:
_buildWarnings('WARNING: No density specified, assigning default '+str(dflt.density))
density = dflt.density
if not specific_heat:
_buildWarnings('WARNING: No specific heat specified, assigning default '+str(dflt.specific_heat))
specific_heat = dflt.specific_heat
if not porosity:
_buildWarnings('WARNING: No porosity specified, assigning default '+str(dflt.porosity))
porosity = dflt.porosity
# assign
if index in self.rock.keys():
self.rock[index].param['density'] = density
self.rock[index].param['specific_heat'] = specific_heat
self.rock[index].param['porosity'] = porosity
else:
macro = fmacro('rock',zone=index,param=(('density',density),('specific_heat',specific_heat),('porosity',porosity)))
self.add(macro)
self.zone[index]._density = density
self.zone[index]._specific_heat = specific_heat
self.zone[index]._porosity = porosity
# add elastic property macros
if youngs_modulus or poissons_ratio:
if not youngs_modulus:
_buildWarnings('WARNING: No Youngs modulus specified, assigning default '+str(dflt.youngs_modulus))
youngs_modulus = dflt.youngs_modulus
if not poissons_ratio:
_buildWarnings('WARNING: No Poissons ratio specified, assigning default '+str(dflt.poissons_ratio))
poissons_ratio = dflt.poissons_ratio
# assign
if index in self.elastic.keys():
self.elastic[index].param['youngs_modulus'] = youngs_modulus
self.elastic[index].param['poissons_ratio'] = poissons_ratio
else:
macro = fmacro('elastic',zone=index,param=(('youngs_modulus',youngs_modulus),('poissons_ratio',poissons_ratio)))
self.add(macro)
self.zone[index]._youngs_modulus = youngs_modulus
self.zone[index]._poissons_ratio = poissons_ratio
# add stress coupling property macros
if thermal_expansion or pressure_coupling:
if not thermal_expansion:
_buildWarnings('WARNING: No coefficient of thermal expansion specified, assigning default '+str(dflt.thermal_expansion))
thermal_expansion = dflt.thermal_expansion
if not pressure_coupling:
_buildWarnings('WARNING: No pressure coupling term specified, assigning default '+str(dflt.pressure_coupling))
pressure_coupling = dflt.pressure_coupling
# assign
if index in self.biot.keys():
self.biot[index].param['thermal_expansion'] = thermal_expansion
self.biot[index].param['pressure_coupling'] = pressure_coupling
else:
macro = fmacro('biot',zone=index,param=(('thermal_expansion',thermal_expansion),('pressure_coupling',pressure_coupling)))
self.add(macro)
self.zone[index]._thermal_expansion = thermal_expansion
self.zone[index]._pressure_coupling = pressure_coupling
# add initial conditions macros
if Pi or Ti or Si:
if not Si and Pi and Ti:
if Ti < tsat(Pi)[0]: Si = 1.; si = 1
else: Si = 0.; si = 3
ti = Ti
elif not Si and Pi and not Ti:
_buildWarnings('WARNING: No initial temperature specified, assigning default '+str(dflt.Ti))
Ti = dflt.Ti
if Ti < tsat(Pi)[0]: Si = 1.; si = 1
else: Si = 0.; si = 3
ti = Ti
elif not Si and Ti and not Pi:
Pi = dflt.Pi
_buildWarnings('WARNING: No initial pressure specified, assigning default '+str(dflt.Pi))
if Ti < tsat(Pi)[0]: Si = 1.; si = 1
else: Si = 0.; si = 3
ti = Ti
elif Si and Ti and not Pi:
Pi = sat(Ti)[0]
pyfehm_print('NOTE: For supplied saturation '+str(Si)+' and temperature '+str(Ti)+', saturation pressure of '+str(Pi)+' will be used.',self._silent)
ti = Si
si = 2
elif Si and Pi:
if Ti:
_buildWarnings('WARNING: Ignoring Ti, using Pi to calculate saturation temperature.')
Ti = tsat(Pi)[0]
ti = Si
si = 2
elif Si and not Ti and not Pi:
Pi = dflt.Pi
Ti = tsat(Pi)[0]
_buildWarnings('WARNING: No initial pressure specified, assigning default '+str(dflt.Pi))
_buildWarnings('NOTE: For supplied saturation '+str(Si)+' and default pressure '+str(Pi)+', saturation temperature of '+str(Ti)+' will be used.')
ti = Si
si = 2
# assign
if index in self.pres.keys():
self.pres[index].param['pressure'] = Pi
self.pres[index].param['temperature'] = ti
self.pres[index].param['saturation'] = si
else:
macro = fmacro('pres',zone=index,param=(('pressure',Pi),('temperature',ti),('saturation',si)))
self.add(macro)
self.zone[index]._Pi = Pi
self.zone[index]._Ti = Ti
self.zone[index]._Si = Si
def general_macro(self, lines, insert_after, zonelist = []):
self._general_macrolist.append(fgeneral(lines, insert_after, zonelist, parent = self))
def _write_general_macro(self, outfile, macro):
for general in self._general_macrolist:
if general.insert_after == macro:
# write out zones if defined
if self.sticky_zones:
self._write_zonn_one(outfile,general.zonelist)
# write out macro lines
for line in general.lines:
outfile.write(line.rstrip()+'\n')
[docs] def run(self,input='',grid = '',incon='',exe=dflt.fehm_path,files=dflt.files,verbose = None, until=None,autorestart=0,use_paths=False,no_paths = False,write_files_only = False,diagnostic = False, clean = False, writeSubFiles=True):
'''Run an fehm simulation. This command first writes out the input file, *fehmn.files* and this incon file
if changes have been made. A command line call is then made to the FEHM executable at the specified path (defaults
to *fehm.exe* in the working directory if not specified).
:param input: Name of input file. This will be written out.
:type input: str
:param grid: Name of grid file. This will be written out.
:type grid: str
:param incon: Name of restart file.
:type incon: str
:param exe: Path to FEHM executable.
:type exe: str
:param files: List of additional files to output. Options include 'check', 'hist' and 'outp'.
:type files: lst[str]
:param until: Name of a function defined inside the script. The function returns a boolean indicating the simulation should be halted. See tutorial 4 for usage.
:type until: func
:param autorestart: Number of times FEHM should restart itself in attempting to find a solution.
:type autorestart: int
:param use_paths: Flag to indicate that PyFEHM should favour full paths in fehmn.files rather than duplication of source files.
:type use_paths: bool
:param write_files_only: Flag to indicate the PyFEHM should write out input, incon, grid, fehmn.files, etc. but should not execute a simulation.
:type write_files_only: bool
:param diagnostic: Flag to indicate PyFEHM should flash up a diagnostic window to monitor the simulation.
:type diagnostic: bool
:param clean: Delete files after simulation 'nop.temp'
:type clean: bool
:param writeSubFiles: Write out subfiles when running
:type clean: bool
'''
if verbose != None: self._verbose = verbose
if diagnostic: self._verbose = True
# set up and check path to executable
exe_path = fpath()
exe_path.filename = exe
if not os.path.isfile(exe_path.full_path): # if can't find the executable, halt
if not os.path.isfile(exe_path.full_path.split()[-1]): # Also check if more complicated command is being used (e.g. mpirun -n 1 -H host xfehm)
raise NameError('No executable at location '+exe)
return
# option to write input, grid, incon files to new names
if input: self._path.filename = input
if grid: self.grid._path.filename = grid
if incon: self.incon._path.filename = incon
# ASSEMBLE FILES IN CORRECT DIRECTORIES
if self.work_dir: wd = self.work_dir + os.sep
else: wd = os.getcwd() + os.sep
returnFlag = self.write(wd+self._path.filename,writeSubFiles=writeSubFiles) # ALWAYS write input file
if not returnFlag:
pyfehm_print('ERROR: writing files',self._silent)
return
self.files.input = self._path.filename
# 1. Copy everything to working directory
if not use_paths:
self.grid.write(wd+self.grid._path.filename) # SOMETIMES write grid file
self.files.grid = wd+self.grid._path.filename
if self.files._use_incon:
self.incon.write(wd+self.incon._path.filename) # SOMETIMES write incon file
self.files.incon = wd+self.incon._path.filename
if self.files._use_stor: # SOMETIMES copy stor file
temp_path = fpath()
temp_path.filename = self.files.stor
if os.path.isfile(temp_path.full_path):
try:
shutil.copy(temp_path.full_path,wd+temp_path.filename)
except: pass
else:
pyfehm_print('ERROR: cant find stor file at '+temp_path.full_path,self._silent)
return
else:
self.files.grid = self.grid._path.full_path
if self.files._use_incon:
if self.incon._writeOut:
self.incon.write(self.incon._path.full_path) # SOMETIMES write incon file
self.files.incon = self.incon._path.full_path
for file in files: # extra files to be written
if file == 'chk': self.files._use_check = True
if file == 'check': self.files._use_check = True
if file == 'hist': self.files._use_hist = True
if file == 'outp': self.files._use_outp = True
if self.ctrl['stor_file_LDA']: self.files._use_stor = True # stor file requested?
if no_paths:
self.files.grid = self.grid.filename
self.files.incon = self.incon.filename
self.files.write() # ALWAYS write fehmn.files
if write_files_only: return # return here if user requests only write out of files
self.files.exe = exe
# RUN SIMULATION
cwd = os.getcwd()
# summarize simulation
self.grid._summary()
if self.files.incon: self.incon._summary()
self._summary()
if self.work_dir: os.chdir(self.work_dir)
# remove restart file if left over from old simulation
if until and self.incon.time == None and os.path.isfile(self.files.rsto):
os.remove(self.files.rsto)
# run the simulation
breakAutorestart = False
for attempt in range(autorestart+1): # restart execution
if breakAutorestart: break
untilFlag = False
if diagnostic: self._diagnostic.refresh_nodes()
if until is None:
p = Popen(exe_path.full_path.split(),stdout=PIPE)
if diagnostic:
self._diagnostic.stdout = p.stdout
self._diagnostic.poll = p.poll
self._diagnostic.construct_viewer() # construct the diagnosis window
if self._verbose:
for line in iter(p.stdout.readline, b''):
print line.rstrip() # print remainder to screen
else:
p.communicate()
else:
p = Popen(exe_path.full_path.split())
self._running = True
while self._running: # loop for checking if stop condition is met
sleep(dflt.sleep_time) # wait
untilFlag = until(self)
p.poll() # check if run finished on its own
if p.returncode == 0: # IF run finshed on its own
self._running = False # break the loop
if untilFlag and self._running: # IF stop condition met
p.terminate() # kill the process
self._running = False # break the loop
breakAutorestart = True
if autorestart != 0:
self.incon.read(self.files.rsto) # read fin file for autorestart
if abs((self.incon.time - self.tf)/self.tf)<0.001: breakAutorestart = True
else:
breakAutorestart = True
if clean:
try: os.remove('nop.temp')
except: pass
try: os.remove('fort.97')
except: pass
if self.work_dir: os.chdir(cwd)
[docs] def paraview(self,exe = dflt.paraview_path,filename = 'temp.vtk',contour = None, history = None, show='kx',zones = 'none',diff = True,zscale = 1.,
spatial_derivatives = False, time_derivatives = False, nodes = False, wells = None):
'''Exports the model object to VTK and loads in paraview.
:param exe: Path to Paraview executable.
:type exe: str
:param filename: Name of VTK file to be output.
:type filename: str
:param contour: Contout output data object loaded using fcontour().
:type contour: fcontour
:param history: History output data object loaded using fhistory().
:type history: fhistory
:param show: Variable to show when Paraview starts up (default = 'kx').
:type show: str
:param zones: Zones to plot: 'user' = user-defined zones, 'all' = all zones except zone[0], 'none' (default), or list of zone objects.
:type zones: str
:param diff: Flag to request PyFEHM to also plot differences of contour variables (from initial state) with time.
:type diff: bool
:param zscale: Factor by which to scale z-axis. Useful for visualising laterally extensive flow systems.
:type zscale: fl64
:param spatial_derivatives: Calculate new fields for spatial derivatives of contour data.
:type spatial_derivatives: bool
:param time_derivatives: Calculate new fields for time derivatives of contour data. For precision reasons, derivatives are calculated with units of 'per day'.
:type time_derivatives: bool
:param nodes: Show node locations (default False).
:type nodes: bool
:param wells: Dictionary of well tracks ([x,y,z] column list), keyed by name.
:type wells: dict
'''
# check for empty contour object
self.write_vtk(filename=filename,contour=contour,diff=diff,zscale=zscale,spatial_derivatives=spatial_derivatives,time_derivatives=time_derivatives)
if history is not None:
from string import join
filename_csv = join(filename.split('.')[:-1],'.')+'.csv'
self.write_csv(filename=filename_csv,history=history,diff=diff,time_derivatives=time_derivatives)
self._vtk.show_zones=zones
if wells:
# check dictionary keys have no spaces, dashed
wells2 = []
for k in wells.keys():
knew = k.replace(' ','_')
knew = knew.replace('-','_')
wells2.append((knew,wells[k]))
self._vtk.write_wells(dict(wells2))
self._vtk.initial_display(show)
self._vtk.startup_script(nodes)
p = Popen(exe+' --script=pyfehm_paraview_startup.py',shell=(not WINDOWS))
[docs] def write_vtk(self, filename = 'temp.vtk',contour=None,diff = True,zscale = 1.,
spatial_derivatives = False, time_derivatives = False):
'''Exports the model object to VTK.
:param filename: Name of VTK file to be output.
:type filename: str
:param contour: Contout output data object loaded using fcontour().
:type contour: fcontour
:param diff: Flag to request PyFEHM to also plot differences of contour variables (from initial state) with time.
:type diff: bool
:param zscale: Factor by which to scale z-axis. Useful for visualising laterally extensive flow systems.
:type zscale: fl64
:param spatial_derivatives: Calculate new fields for spatial derivatives of contour data.
:type spatial_derivatives: bool
:param time_derivatives: Calculate new fields for time derivatives of contour data. For precision reasons, derivatives are calculated with units of 'per day'.
:type time_derivatives: bool
'''
if contour is not None:
if len(contour.variables) == 0: contour = None
# write vtk files for contour output data
self._vtk = fvtk(parent=self,filename=filename,contour=contour,diff=diff,zscale = zscale,spatial_derivatives = spatial_derivatives, time_derivatives = time_derivatives)
self._vtk.assemble()
fls = self._vtk.write()
def write_csv(self, filename = 'temp.csv',history=None,diff = True, time_derivatives = False):
'''Exports the fhistory object to CSV for reading into paraview.
:param filename: Name of CSV file to be output.
:type filename: str
:param history: History output data object loaded using fhistory().
:type history: fhistory
:param diff: Flag to request PyFEHM to also plot differences of contour variables (from initial state) with time.
:type diff: bool
:param time_derivatives: Calculate new fields for time derivatives of contour data. For precision reasons, derivatives are calculated with units of 'per day'.
:type time_derivatives: bool
'''
if history is not None:
if len(history.variables) == 0: history = None
# write vtk files for contour output data
self._vtk.csv = fcsv(parent=self,filename=filename,history=history,diff=diff,time_derivatives = time_derivatives)
self._vtk.csv.write()
def visit(self,exe = dflt.visit_path,filename = 'temp.vtk',contour = None, history = None, show='kx',zones = 'none',diff = True,zscale = 1.,
spatial_derivatives = False, time_derivatives = False, nodes = False, wells = None):
'''Exports the model object to VTK and loads in visit
:param exe: Path to VisIt executable.
:type exe: str
:param filename: Name of VTK file to be output.
:type filename: str
:param contour: Contout output data object loaded using fcontour().
:type contour: fcontour
:param history: History output data object loaded using fhistory().
:type history: fhistory
:param show: Variable to show when Paraview starts up (default = 'kx').
:type show: str
:param zones: Zones to plot: 'user' = user-defined zones, 'all' = all zones except zone[0], 'none' (default), or list of zone objects.
:type zones: str
:param diff: Flag to request PyFEHM to also plot differences of contour variables (from initial state) with time.
:type diff: bool
:param zscale: Factor by which to scale z-axis. Useful for visualising laterally extensive flow systems.
:type zscale: fl64
:param spatial_derivatives: Calculate new fields for spatial derivatives of contour data.
:type spatial_derivatives: bool
:param time_derivatives: Calculate new fields for time derivatives of contour data. For precision reasons, derivatives are calculated with units of 'per day'.
:type time_derivatives: bool
:param nodes: Show node locations (default False).
:type nodes: bool
:param wells: Dictionary of well tracks ([x,y,z] column list), keyed by name.
:type wells: dict
'''
# check for empty contour object
self.write_vtk(filename=filename,contour=contour,diff=diff,zscale=zscale,spatial_derivatives=spatial_derivatives,time_derivatives=time_derivatives)
if history is not None:
from string import join
filename_csv = join(filename.split('.')[:-1],'.')+'.csv'
self.write_csv(filename=filename_csv,history=history,diff=diff,time_derivatives=time_derivatives)
self._vtk.show_zones=zones
if wells:
# check dictionary keys have no spaces, dashed
wells2 = []
for k in wells.keys():
knew = k.replace(' ','_')
knew = knew.replace('-','_')
wells2.append((knew,wells[k]))
self._vtk.write_wells(dict(wells2))
self._vtk.initial_display(show)
#self._vtk.startup_script(nodes)
p = Popen(exe+' -o '+self._vtk.path.filename[:-4]+'*.vtk',shell=(not WINDOWS))
def _summary(self):
L = 62
s = ['']
s.append(' ****---------------------------------------------------------****')
line = ' **** FEHM input file \''+self.filename+'\' summary.'
for i in range(L-len(line)): line += ' '
s.append(line+'****')
s.append(' ****---------------------------------------------------------****')
lines = []
lines.append(' **** Zones:')
lines.append(' **** Material properties:')
lines.append(' **** Generators:')
for line in lines:
if line.startswith(' **'):
for i in range(L-len(line)): line += ' '
s.append(line+'****')
else:
prntStr = ' **** -'
keepGoing = True
line = line.split()
while keepGoing:
if not line:
for i in range(L-len(prntStr)): prntStr += ' '
s.append(prntStr+'****')
prntStr = ' **** '
break
if len(prntStr)<(L-len(line[0])):
prntStr += ' '+line[0]
line = line[1:]
else:
for i in range(L-len(prntStr)): prntStr += ' '
s.append(prntStr+'****')
prntStr = ' **** '
s.append(' ****---------------------------------------------------------****')
s.append('')
s = '\n'.join(s)
pyfehm_print(s,self._silent)
def _write_prep(self): #Determine if data object fit for writing
global buildWarnings
# WARNING: if cont output specifies pressure, but not state, then no pressure data will be written
hasPres = False
hasState = False
checkWarnings = []
for var in list(flatten(self.cont.variables)):
if var.startswith('p') and not var.startswith('po') and not var.startswith('pe'): hasPres = True
if var.startswith('l') or var.startswith('va'): hasState=True
if hasPres and not hasState: self.cont.variables.append(['liquid','vapor'])
# WARNING: stress perm module specified without calling excess_she
if self.permmodellist:
for pm in self.permmodellist:
if pm.index in [22,24,25] and self.strs.excess_she['PAR1']==None:
checkWarnings.append('Perm model specified without accompanying excess_she macro - assigning defaults.')
self.strs.excess_she['PAR1']=0.9
self.strs.excess_she['PAR2']=10.
self.strs.excess_she['PAR3']=1.
if 1 not in [pm.index for pm in self.permmodellist]:
new_permmodel = fmodel('permmodel',index=1)
new_permmodel.zonelist=self.zone[0]
self.add(new_permmodel)
for boun in self.bounlist: boun._check()
for zone in self.zonelist: zone._check()
for key in self._allMacro.keys():
for macro in self._allMacro[key]: macro._check()
# WARNING: conductivity not specified, FEHM simulation wont run - add default
if not self.cond.has_key(0):
checkWarnings.append('No global conductivity set. Setting default '+str(dflt.conductivity))
self.add(fmacro('cond',param=(('cond_x',dflt.conductivity),('cond_y',dflt.conductivity),('cond_z',dflt.conductivity))))
# WARNING: rock properties not specified, FEHM will run weirdly - add default
if not self.rock.has_key(0):
checkWarnings.append('No global rock properties set. Setting default density = '+str(dflt.density)+', specific heat = '+str(dflt.specific_heat)+', porosity='+str(dflt.porosity))
self.add(fmacro('rock',param=(('density',dflt.density),('specific_heat',dflt.specific_heat),('porosity',dflt.porosity))))
# WARNING: specifying zero pressure in co2pres has caused segfaults in the past
if self.co2preslist:
for m in self.co2preslist:
if m.param['pressure'] == 0:
checkWarnings.append('Zero pressure in CO2PRES can cause instability.')
break
# WARNING: if stress gradients have been specified and bodyforce applied
if self.incon._stressgradCalled and self.strs.bodyforce:
checkWarnings.append('When specifying stress gradients, bodyforce should be turned off.')
if (len(self.incon._strs_xx) != 0) and self.strs.bodyforce:
checkWarnings.append('Bodyforce should be turned off when doing stress restarts.')
# WARNING: if stress solution requested and second parameter in sol not -1
if self.strs.param['ISTRS'] == 1:
if self.sol['element_integration_INTG'] == 1:
checkWarnings.append('Gaussian integration scheme can cause spatial osciallations in pressure solution - try sol[\'element_integration_INTG\'] = 1 if this occurs.')
# WARNING: instability if using zoneflux and surf output
if self.hist.zoneflux and self.hist.format != 'tec':
checkWarnings.append('Use of history output format \''+self.hist.format+'\' may not be compatible with zoneflux output (fehm macro: flxz). Use \'tec\' if problems experienced.')
# WARNING: if a co2 relperm is specified without the carb macro, there will be issues
co2_rlpm_flag = False
for rlpm in self.rlpmlist:
for phase in rlpm.phases:
if phase.startswith('co2'): co2_rlpm_flag = True
if self.carb.iprtype == 0 and co2_rlpm_flag:
checkWarnings.append('Specification of co2 relperm relationship without invoking the CARB module may cause crashes.')
# ERROR: check to see no new dictionary keys have been defined
ctrlFlag = dict_key_check(self.ctrl,dflt.ctrl.keys(),'ctrl')
iterFlag = dict_key_check(self.iter,dflt.iter.keys(),'iter')
timeFlag = dict_key_check(self.time,dflt.time.keys(),'time')
solFlag = dict_key_check(self.sol,dflt.sol.keys(),'sol')
if ctrlFlag or iterFlag or timeFlag or solFlag:
return True
# print warnings
if checkWarnings or buildWarnings:
s = ''
for warning in buildWarnings:
s += warning +'\n'
s+='\n\n'
L = 62
s+= ' !!!!---------------------------------------------------------!!!!\n'
s+= ' !!!! Possible errors detected in input file. !!!!\n'
s+= ' !!!!---------------------------------------------------------!!!!\n'
for warning in checkWarnings:
stri = ' !!!! -'
keepGoing = True
warning = warning.split()
while keepGoing:
if not warning:
for i in range(L-len(stri)): stri += ' '
s+= stri+'!!!!\n'
stri = ' !!!! '
break
if len(stri)<(L-len(warning[0])):
stri += ' '+warning[0]
warning = warning[1:]
else:
for i in range(L-len(stri)): stri += ' '
s+= stri+'!!!!\n'
stri = ' !!!! '
s+= ' !!!!---------------------------------------------------------!!!!\n'
if self.work_dir: fp = open(self.work_dir+os.sep+'pyfehm.err','w')
else: fp = open('pyfehm.err','w')
fp.write(s)
fp.close()
pyfehm_print(s,self._silent)
checkWarnings = []
buildWarnings = []
return False
[docs] def temperature_gradient(self,filename,offset=0.,first_zone = 100,auxiliary_file=None,hydrostatic = 0,flip_depth_sign=False):
'''Assign initial temperature distribution to model based on supplied temperature profile.
:param filename: Name of a file containing temperature gradient data. File should be two columns, comma or space separated, with elevation in the first column and temperature (degC) in the second.
:type filename: str
:param offset: Vertical offset added to the elevation in temperature gradient data. Useful if model limits don't correspond to data.
:type offset: fl64
:param first_zone: Index of first zone created. Zone index will be incremented by 1 thereafter.
:type first_zone: int
:param auxiliary_file: Name of auxiliary file in which to store **PRES** macros.
:type auxiliary_file: str
:param hydrostatic: Pressure at top of well profile. PyFEHM will calculate hydrostatic pressures consistent with the temperature profile. If left blank, default pressures will be used.
:type hydrostatic: fl64
:param flip_depth_sign: If sign of depths in file does not match z coordinate in simulation, flip the sign.
:type flip_depth_sign: bool
'''
# check if file exists
if not os.path.isfile(filename):
pyfehm_print('ERROR: cannot find temperature gradient file \''+filename+'\'.',self._silent)
return
# determine uniqueness of z-coords - if less than 100, use zones, if more than 100, use nodes.
z = np.unique([nd.position[2] for nd in self.grid.nodelist])
if len(z) <= 100: zoneFlag = True
else: zoneFlag = False
# read temperature data, apply offset, extend or trim to vertical extent of model
tempfile = open(filename,'r')
ln = tempfile.readline()
tempfile.close()
commaFlag = False; spaceFlag = False
if len(ln.split(',')) > 1: commaFlag = True
elif len(ln.split()) > 1: spaceFlag = True
if not commaFlag and not spaceFlag:
pyfehm_print('ERROR: incorrect formatting for \''+filename+'\'. Expect first column depth (m) and second column temperature (degC), either comma or space separated.',self._silent)
return
if commaFlag: tempdat = np.loadtxt(filename,delimiter=',')
else: tempdat = np.loadtxt(filename)
zt = tempdat[:,0]; tt = tempdat[:,1]
zt = zt + offset
if flip_depth_sign: zt = -zt
if (zt[0]>zt[-1] and z[0]<z[-1]) or (zt[0]<zt[-1] and z[0]>z[-1]):
zt = np.flipud(zt)
tt = np.flipud(tt)
# calculate pressure to assign
if 0 in self.pres.keys():
p0 = self.pres[0].param['pressure']
else:
p0 = 1.
_buildWarnings('WARNING: no pressure information, assigning default of 1 MPa. These pressures will be overwritten if GRAD macro used.')
# assign zones or nodes
if zoneFlag:
ind = first_zone
x0,x1 = self.grid.xmin,self.grid.xmax
y0,y1 = self.grid.ymin,self.grid.ymax
T = np.interp(np.sort(z),np.sort(zt),tt[np.argsort(zt)])
if hydrostatic != 0:
P = fluid_column(z,Tgrad = filename, Tsurf = 25., Psurf = hydrostatic)[0][:,0]
else:
P = p0*np.ones((1,len(z)))[0]
if z[0]<z[-1]: P = np.flipud(P)
for zi,ti,pi in zip(z,T,P):
zn = fzone(index=ind)
zn.rect([x0-0.1,y0-0.1,zi-0.1],[x1+0.1,y1+0.1,zi+0.1])
self.add(zn)
self.add(fmacro('pres',zone=ind,file = auxiliary_file, param=(('pressure',pi),('temperature',ti),('saturation',1))))
ind +=1
else:
for nd,ti in zip(self.grid.nodelist,np.interp([nd.position[2] for nd in self.grid.nodelist],zt,tt)):
self.add(fmacro('pres',zone=(nd.index,nd.index,1),file = auxiliary_file, param=(('pressure',p0),('temperature',ti),('saturation',1))))
################## ZONE FUNCTIONS
def _read_zonn(self,infile,file=''): #ZONE: Reads ZONE or ZONN macro.
block = ['zone\n']
line=infile.readline().strip(); block.append(line+'\n')
if line[0:4]=='file':
line = infile.readline().strip(); block.append(line+'\n')
self._read_zonn_file(line)
else:
more = True
while more:
new_zone = fzone()
if file: new_zone.file=file
# assess whether zone has already been defined
zind = int(line.split()[0])
new_zone.index = zind
if line.rfind('#') != -1: new_zone.name = line[line.rfind('#')+1:].strip()
line=infile.readline().strip(); block.append(line+'\n')
new_zone.points = []
if line[0:4]=='list':
new_zone.type='list'
morePoints = True
while morePoints:
line=infile.readline().strip(); block.append(line+'\n')
pts = line.split()
if not pts: morePoints = False
else: new_zone.nodelist.append(self.grid.node_nearest_point([float(pt) for pt in pts]))
elif line[0:4] == 'nnum':
new_zone.type='nnum'
line=infile.readline().strip()
nums = line.split()
number_nodes = int(nums[0])
new_zone.nodelist = [None]*number_nodes
i = 0
for num in nums[1:]:
new_zone.nodelist[i] = self.grid.node[int(num)]
i +=1
while i!=number_nodes:
line=infile.readline().strip()
nums = line.split()
for num in nums:
new_zone.nodelist[i]=self.grid.node[int(num)]
i +=1
else:
new_zone.type='rect'
if self.ctrl['geometry_ICNL'] == 0: # 3-D geometry
numPts = 24 # number of points to define rect
else: # 2-D geometry
numPts = 8 # number of points to define rect
iPts = 0
pts = line.split()
allpts = []
for pt in pts: allpts.append(float(pt))
iPts += len(pts)
while iPts != numPts:
line = infile.readline().strip(); block.append(line+'\n')
pts = line.split()
for pt in pts: allpts.append(float(pt))
iPts += len(pts)
new_zone.points = np.array(list(flatten(allpts)))
if np.size(new_zone.points)==24:
if self.grid.dimensions == 3:
new_zone.points = list(new_zone.points.reshape(3,8))
else:
new_zone.points = list(new_zone.points[:16].reshape(2,8))
else: new_zone.points = list(new_zone.points.reshape(2,4))
# zone already defined, check if definitions match
if zind in self.zone.keys():
zn_old = self.zone[zind]
if zn_old.type != new_zone.type:
_buildWarnings('WARNING: zone '+str(zind)+' was defined earlier in the input file. PyFEHM assumes unique zone definitions. This zone will be ignored.')
elif new_zone.type == 'rect':
if self.ctrl['geometry_ICNL'] == 0:
x0n,x1n = np.min(new_zone.points[0]), np.max(new_zone.points[0])
y0n,y1n = np.min(new_zone.points[1]), np.max(new_zone.points[1])
z0n,z1n = np.min(new_zone.points[2]), np.max(new_zone.points[2])
x0o,x1o = np.min(zn_old.points[0]), np.max(zn_old.points[0])
y0o,y1o = np.min(zn_old.points[1]), np.max(zn_old.points[1])
z0o,z1o = np.min(zn_old.points[2]), np.max(zn_old.points[2])
if (x0n != x0o) or (x1n != x1o) or (y0n != y0o) or (y1n != y1o) or (z0n != z0o) or (z1n != z1o):
_buildWarnings('WARNING: zone '+str(zind)+' was defined earlier in the input file. PyFEHM assumes unique zone definitions. This zone will be ignored.')
else:
different_zone = False
if len(set(zn_old.nodelist).symmetric_difference(new_zone.nodelist)) != 0:
different_zone = True
if different_zone:
_buildWarnings('WARNING: zone '+str(zind)+' was defined earlier in the input file. PyFEHM assumes unique zone definitions. This zone will be ignored.')
if not zind in self.zone.keys(): self._add_zone(new_zone,overwrite=True)
line=infile.readline(); block.append(line+'\n')
if not line.strip(): more = False
return block
def _read_zonn_rad(self,infile,file=''): #ZONE: Reads ZONE or ZONN macro.
line=infile.readline().strip()
more = True
while more:
# assess whether zone has already been defined
zind = int(line.split()[0])
name = None
if line.rfind('#') != -1: name = line[line.rfind('#')+1:].strip()
if self.ctrl['geometry_ICNL']: return
r_pts = line=infile.readline().strip().split('#')[0]
r_pts = [float(pi) for pi in r_pts.split()]
z_pts = line=infile.readline().strip().split('#')[0]
z_pts = [float(pi) for pi in z_pts.split()]
r_min,r_max = np.min(r_pts),np.max(r_pts)
z_min,z_max = np.min(z_pts),np.max(z_pts)
pts = np.array([nd.position for nd in self.grid.nodelist])
r = np.sqrt(pts[:,0]**2+pts[:,1]**2)
z = pts[:,2]
inds = np.where((z>z_min)&(z<z_max)*(r>r_min)&(r<r_max))[0]
nds = [self.grid.nodelist[i] for i in inds]
self.new_zone(zind, name=name, nodelist = nds)
line=infile.readline()
if not line.strip(): more = False
def _read_zonn_file(self,file): #Reads ZONN from specified file.
zone_file = open(file,'r')
line = zone_file.readline().strip()
self._read_zonn(zone_file,file)
def _write_zonn_all(self,outfile): #Writes ZONE or ZONN macro.
ws = _title_string('ZONES',72)
outfile.write(ws)
# check for zones specified in external files - write these first
filezone = False
file_zns = []
file_nms = []
for zn in self.zonelist:
if zn.file:
filezone = True
file_zns.append(zn)
if zn.file not in file_nms: file_nms.append(zn.file)
if filezone:
for file_nm in file_nms:
outfile.write('zonn\n')
outfile.write('file\n')
outfile.write(file_nm+'\n')
# if filename does not exist, write file
if not os.path.isfile(file_nm):
zonefile = open(file_nm,'w')
zns = [zn for zn in self.zonelist if zn.file==file_nm]
for zn in zns: zn.file = ''
self._write_zonn_one(zonefile,zns)
for zn in zns: zn.file = file_nm
zonefile.write('stop\n')
zonefile.close()
outfile.write('zonn\n')
for zn in self.zonelist:
if zn.file: continue
if zn.index == 0: continue
outfile.write(str(zn.index))
if zn.name: outfile.write('\t\t#'+zn.name.strip())
outfile.write('\n')
if zn.type != 'rect':
outfile.write(str(zn.type)+'\n')
for ptarray in zn.points:
for pt in ptarray: outfile.write(str(pt)+'\t')
outfile.write('\n')
if zn.type == 'list': outfile.write('\n')
outfile.write('\n')
def _write_zonn_one(self,outfile,zns=[]): #Writes out a single zone when using sticky
if not zns: return
uniqueZns = []
uniqueZns_inds=[]
for zn in zns:
if zn.index not in uniqueZns_inds: uniqueZns_inds.append(zn.index); uniqueZns.append(zn)
zns = uniqueZns
# check for zones specified in external files - write these first
filezone = False
regzone = False
file_zns = []
file_nms = []
for zn in zns:
if isinstance(zn,tuple): continue
if zn.file:
filezone = True
file_zns.append(zn)
if zn.file not in file_nms: file_nms.append(zn.file)
else: regzone=True
if filezone:
for file_nm in file_nms:
outfile.write('zone\n')
outfile.write('file\n')
outfile.write(file_nm+'\n')
# if filename does not exist, write file
if not os.path.isfile(self.work_dir+os.sep+file_nm) or self._writeSubFiles:
if self.work_dir:
zonefile = open(self.work_dir+os.sep+file_nm,'w')
else:
zonefile = open(file_nm,'w')
zns = [zn for zn in self.zonelist if zn.file==file_nm]
for zn in zns: zn.file = ''
self._write_zonn_one(zonefile,zns)
for zn in zns: zn.file = file_nm
zonefile.write('stop\n')
zonefile.close()
return
if not regzone: return
outfile.write('zone\n')
for zn in zns:
if zn.file: continue
else:
if zn.index == 0: continue
outfile.write(str(zn.index))
if zn.name: outfile.write('\t\t#'+zn.name.strip())
outfile.write('\n')
if zn.type != 'rect':
outfile.write(str(zn.type)+'\n')
for ptarray in zn.points:
for pt in ptarray: outfile.write(str(pt)+'\t')
outfile.write('\n')
if zn.type == 'list': outfile.write('\n')
if not zn.file: outfile.write('\n')
def _add_zone(self,zone=fzone(),overwrite=False): #Adds a ZONE object.
# check if zone already exists
if isinstance(zone,fzone):
if zone.index in self.zone.keys():
if not overwrite:
_buildWarnings('WARNING: A zone with index '+str(zone.index)+' already exists. Zone will not be defined, use overwrite = True in add() to overwrite the old zone.')
return
else:
self.delete(self.zone[zone.index])
if zone.name in self.zone.keys():
if not overwrite:
_buildWarnings('WARNING: A zone with name \''+str(zone.name)+'\' already exists. Zone will not be defined, use overwrite = True in add() to overwrite the old zone.')
return
else:
self.delete(self.zone[zone.name])
zone._parent = self
if zone not in self._zonelist:
self._zonelist.append(zone)
self._associate_zone(zone)
def _associate_zone(self,zone): #Associates nodes contained within a ZONE, with that zone
if not self._associate: return
for nd in zone.nodelist:
nd._zone.update({zone.index:zone})
def _delete_zone(self,zone=fzone()):
if zone.index in [999,998,997,996,995,994]: return
self._zonelist.remove(zone)
def _is_zone(self,obj): #Corrects index zone specification to object
if isinstance(obj.zone,int):
if obj.zone in self._zone_indices:
obj.zone = self.zone[obj.zone]
else:
pyfehm_print('Error: zone ' + str(obj.zone) + ' does not exist.',self._silent)
return
return obj
def _get_info(self): #Prints out information about the data file
# grid dimensions
import itertools
print '***********************************************************************'
if self.filename:
print '***** '+self.filename+' *****' # file name
print '***********************************************************************'
if self.grid.filename: # grid properties
print 'Model domain: x = ['+str(self.grid.xmin) + ', ' + str(self.grid.xmax) + ']'
print ' y = ['+str(self.grid.ymin) + ', ' + str(self.grid.ymax) + ']'
print ' z = ['+str(self.grid.zmin) + ', ' + str(self.grid.zmax) + ']'
print ' nodes = ' +str(self.grid.number_nodes)
print ' '
else:
print '%%%%% no grid associated %%%%%'
if len(self.zonelist)==1: # zones
print 'No user defined zones'
elif len(self.zonelist)==2:
print 'One user defined zone: '+str(self.zonelist[-1].index)
else:
printStr = str(len(self.zonelist)-1) +' user defined zones: '+str(self.zonelist[1].index)
for zn in self.zonelist[2:]: printStr+=', '+str(zn.index)
print printStr+'\n'
if self.permlist: # permeablities
for perm in self.permlist:
if perm.zone.index==0: break
print 'Permeability: \n background - kx, ky, kz = ['+str(perm.param['kx'])+', '+str(perm.param['ky'])+ ', '+str(perm.param['kz'])+']'
for perm in self.permlist:
if perm.zone.index==0: continue
print ' zone '+str(perm.zone.index)+' - kx, ky, kz = ['+str(perm.param['kx'])+', '+str(perm.param['ky'])+ ', '+str(perm.param['kz'])+']'
print ' '
else:
print '%%%%% no permeability properties assigned %%%%%'
if self.condlist: # conductivities
for cond in self.condlist:
if cond.zone.index==0: break
print 'Conductivity: \n background - Kx, Ky, Kz = ['+str(cond.param['cond_x'])+', '+str(cond.param['cond_y'])+ ', '+str(cond.param['cond_z'])+']'
for cond in self.condlist:
if cond.zone.index==0: continue
print ' zone '+str(cond.zone.index)+' - Kx, Ky, Kz = ['+str(cond.param['cond_x'])+', '+str(cond.param['cond_y'])+ ', '+str(cond.param['cond_z'])+']'
print ' '
else:
print '%%%%% no conductivity properties assigned %%%%%'
if self.rocklist: # rock properties
for rock in self.rocklist:
if rock.zone.index==0: break
print ('Rock properties: \n background - density = '+str(rock.param['density'])+', specific heat = '
+str(rock.param['specific_heat'])+ ', porosity = '+str(rock.param['porosity']))
for rock in self.rocklist:
if rock.zone.index==0: continue
print (' zone '+str(rock.zone.index)+' - density = '+str(rock.param['density'])+', specific heat = '
+str(rock.param['specific_heat'])+ ', porosity = '+str(rock.param['porosity']))
print ' '
else:
print '%%%%% no rock properties assigned %%%%%'
if self.flowlist: # generators
prntStr = 'Generators: \n zone '
for flow in self.flowlist:
if isinstance(flow.zone,fzone):
prntStr += str(flow.zone.index) + ' - '
else:
prntStr += '('+str(flow.zone[0]) + ','+str(flow.zone[1]) + ','+str(flow.zone[2]) +') - '
prntStr += 'SKD = '+str(flow.param['rate']) + ', '
prntStr += 'EFLOW = '+str(flow.param['energy']) + ', '
prntStr += 'AIPED = '+str(flow.param['impedance'])
print prntStr
if flow.param['rate']>0:
if flow.param['energy']>0:
if flow.param['impedance']==0: print ' mass production at fixed rate of ' + str(abs(flow.param['rate']))+' kg/s'
else: print ' mass production against specified WHP of ' + str(abs(flow.param['rate'])) + ' MPa'
else:
if flow.param['energy']>0:
if flow.param['impedance']==0: print ' mass injection of '+str(flow.param['energy'])+' MJ/kg fluid at fixed rate of ' + str(abs(flow.param['rate']))+' kg/s'
else:print ' mass injection of '+str(flow.param['energy'])+' MJ/kg fluid against specified WHP of ' + str(abs(flow.param['rate']))+' MPa'
else:
if flow.param['impedance']==0: print ' mass injection of '+str(abs(flow.param['energy']))+' degC fluid at fixed rate of ' + str(abs(flow.param['rate']))+' kg/s'
else:print ' mass injection of '+str(abs(flow.param['energy']))+' degC fluid against specified WHP of ' + str(abs(flow.param['rate']))+' MPa'
prntStr = ' zone '
print ' '
if self.bounlist: # flow boundary conditions
print 'Flow boundary conditions: zone INCOMPLETE'
if not self.flowlist and not self.bounlist and not self.co2flowlist:
print '%%%%% no boundary conditions assigned %%%%%'
if self.carb.iprtype !=0: # co2 module
co2model = dict([(1,'Water only'),(2,'CO2 only'),(3,'CO2-water, no solubility'),
(4,'CO2-water, with solubility'),(5,'CO2-water-air, with solubility')])
print 'CO2 module: ' + co2model[self.carb.iprtype]
if self.co2flowlist:
prntStr = '....CO2 generators: zone '
for co2flow in self.co2flowlist:
prntStr += (str(co2flow.zone.index) + ' - SKTMP = '+str(co2flow.param['rate'])+', ESKTMP = '+str(co2flow.param['energy'])
+ ', AIPED = ' + str(co2flow.param['impedance']) + ', FLAG = '+str(co2flow.param['bc_flag']))
print prntStr
prntStr = ' : zone '
if self.co2fraclist:
for co2frac in self.co2fraclist:
if co2frac.zone.index==0: break
print ('....Initial CO2 fractions: \n background - water saturation = '+str(co2frac.param['water_rich_sat'])+', CO2 saturation = '+str(co2frac.param['co2_rich_sat'])
+', CO2 mass fraction = '+str(co2frac.param['co2_mass_frac'])+', CO2 initial salt concentration = '+str(co2frac.param['init_salt_conc']))
for co2frac in self.co2fraclist:
if co2frac.zone.index==0: continue
print (' zone - water saturation = '+str(co2frac.param['water_rich_sat'])+', CO2 saturation = '+str(co2frac.param['co2_rich_sat'])
+', CO2 mass fraction = '+str(co2frac.param['co2_mass_frac'])+', CO2 initial salt concentration = '+str(co2frac.param['init_salt_conc']))
if self.co2preslist:
phase = dict([(1, 'liquid'),(2,'two phase'),(3,'vapour'),(4,'super-critical')])
for co2pres in self.co2preslist:
if co2pres.zone.index==0: break
print ('....Initial CO2 conditions: \n background - pressure = '+str(co2pres.param['pressure'])+' MPa, temperature = '+str(co2pres.param['temperature'])
+' degC, phase = '+phase[co2pres.param['phase']])
for co2pres in self.co2preslist:
if co2pres.zone.index==0: continue
print (' zone - pressure = '+str(co2pres.param['pressure'])+' MPa, temperature = '+str(co2pres.param['temperature'])
+' degC, phase = '+phase[co2pres.param['phase']])
if self.strs.param['ISTRS']: # stress module
print 'Stress module:'
if self.elasticlist:
for elastic in self.elasticlist:
if elastic.zone.index==0: break
print '....Elastic properties: \n background - E = '+str(elastic.param['youngs_modulus'])+' MPa, nu = '+str(elastic.param['poissons_ratio'])
for elastic in self.elasticlist:
if elastic.zone.index==0: continue
print ' zone '+str(elastic.zone.index)+' - E = '+str(elastic.param['youngs_modulus'])+' MPa, nu = '+str(elastic.param['poissons_ratio'])
else:
print ' %%%%% no elastic properties assigned %%%%%'
if self.biotlist:
for biot in self.biotlist:
if biot.zone.index==0: break
print '....Thermo and poroelastic properties: \n background - thermal = '+str(biot.param['thermal_expansion'])+', pressure = '+str(biot.param['pressure_coupling'])
for biot in self.biotlist:
if biot.zone.index==0: continue
print ' zone '+str(biot.zone.index)+' - thermal = '+str(biot.param['thermal_expansion'])+', pressure = '+str(biot.param['pressure_coupling'])
else:
print ' %%%%% no pore pressure (biot) properties assigned %%%%%'
if self.stressbounlist:
print '....Stress boundary conditions: '
strsDirs = dict([(1,'x-dir'),(2,'y-dir'),(3,'z-dir')])
for strs_boun in self.stressbounlist:
prntStr = ' zone '
prntStr += str(strs_boun.zone.index) +' - '
if strs_boun.subtype=='lithograd':
prntStr += 'lithograd, '
prntStr += strsDirs[abs(strs_boun.param['direction'])]+', '
prntStr += 'stress grad = ' + str(strs_boun.param['value']) + ' MPa/m'
elif strs_boun.subtype == 'distributed':
prntStr += strs_boun.subtype +' force, '
prntStr += strsDirs[abs(strs_boun.param['direction'])]+', '
prntStr += str(strs_boun.param['value'])+ ' MPa'
elif strs_boun.subtype == 'lithostatic':
prntStr +='not done!'
else:
if strs_boun.param['direction']>0: prntStr += 'fixed disp, '
else: prntStr += 'fixed force, '
prntStr += strsDirs[abs(strs_boun.param['direction'])]+', '
prntStr += str(strs_boun.param['value'])+' '
if strs_boun.param['direction']>0: prntStr += 'm'
else: prntStr += 'MPa'
print prntStr
else:
print ' %%%%% no stress boundary conditions assigned %%%%%'
if self.permmodellist:
print '....Stress-permeability relationships:'
for perm_model in self.permmodellist:
if not perm_model.zone: continue
prntStr = ' model ' + str(perm_model.index)
if len(perm_model.zone)==1:
prntStr += ' - zone ' + str(perm_model.zone[0].index)
else:
prntStr += ' - zones '
for zn in perm_model.zone[:-1]: prntStr += str(zn.index) + ', '
prntStr += str(perm_model.zone[-1].index)
for par,unit in zip(permDicts[perm_model.index],permUnits[perm_model.index]):
if par in perm_model.param.keys():
prntStr +='\n '+par+' = '+str(perm_model.param[par])+' '+unit
if perm_model.index==1:
prntStr += '\n no permeability modification'
print prntStr
else:
print ' %%%%% no stress-permeability models specified %%%%%'
print ' '
if self.cont.variables: # contour output
print 'Contour output ('+self.cont.format+' format) requested for - '
#vars = list(itertools.chain(*self.cont.variables))
for var in self.cont.variables: print ' '+var
if self.hist.variables: # history output
print 'History output ('+self.hist.format+' format) requested for - '
vars = list(itertools.chain(*self.hist.variables))
for var in vars: print ' '+var
if not self.cont.variables and not self.hist.variables:
print '%%%%% no output requested %%%%%'
print '***********************************************************************'
what = property(_get_info)
################## MACRO FUNCTIONS
def _read_macro(self,infile,macroName,second=False): #MACRO: Reads general format macros
from copy import copy,deepcopy
more=True
file_flag=False
grad_flag=True
while more:
if macroName not in ['permmodel','rlp']:
m = fmacro(macroName)
line=infile.readline().strip()
if not line: more=False; continue
if line.startswith('file'): more=False; file_flag = True; continue # read file data
# params = copy(dict(macro_list[macroName]))
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvv exception for stressboun vvvvvvvvvvvvvvvvvvvvvvvvv
# assess boundary condition type #v
if macroName == 'stressboun': #v
m.subtype='fixed' #v
if line.startswith('distributed') or line.startswith('lithostatic') or line.startswith('lithograd'):
m.subtype=line.split()[0] #v
oldline = line; line=infile.readline().strip() #^
if m.subtype=='lithograd': #^
m.param['sdepth']=float(oldline.split()[1]) #^
m.param['gdepth']=float(oldline.split()[2]) #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvvvvv exception for grad vvvvvvvvvvvvvvvvvvvvvvvvvvvv
# turn boolean 'more' into countdown #v
if macroName == 'grad': #v
if grad_flag: #v
more = int(line.split()[0]) #v
grad_flag=False #^
line = infile.readline().strip() #^
more -= 1 #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
nums = line.split()
nums_zone,nums_params = nums[:3],nums[3:]
if macroName == 'grad': nums_zone,nums_params = nums[:1],nums[1:]
m.zone = self._macro_zone(nums_zone)
if second:
m.file=infile.name
for i,key in enumerate(macro_list[macroName]): m.param[key[0]] = float(nums_params[i])
self._add_macro(m,overwrite=True)
if file_flag:
line=infile.readline().strip()
if not os.path.isfile(line):
# check if in subdirectory with input file
fname = self._path.filename.split(os.sep)
if len(fname)>0:
fn0 = ''
for fn in fname[:-1]: fn0 += fn
if not os.path.isfile(fn0+os.sep+line):
pyfehm_print('ERROR: cannot find macro file '+line,self._silent)
else:
macrofile = open(fn0+os.sep+line)
line = macrofile.readline().strip()
self._read_macro(macrofile,macroName,second = True)
macrofile.close()
else:
macrofile = open(line)
line = macrofile.readline().strip()
self._read_macro(macrofile,macroName,second = True)
macrofile.close()
def _add_macro(self,macro,overwrite=False): #Adds the macro to data file
# check macro zone definition
macro._parent = self
if isinstance(macro.zone,list) and len(macro.zone)==0: macro.zone = self.zone[0] # assign everywhere zone
#elif isinstance(macro.zone,fnode): macro.zone = tuple([macro.zone.index, 1, 1])
elif isinstance(macro.zone,tuple): macro.zone = tuple([int(ls) for ls in macro.zone])
elif isinstance(macro.zone,(int,str)):
if macro.zone in self.zone.keys(): macro.zone = self.zone[macro.zone]
else: pyfehm_print('ERROR: Specified zone '+str(ind)+' for macro '+macro.type+' does not exist.',self._silent)
# check if macro already exists
exclusions = ['grad','stressboun']
if isinstance(macro.zone,fzone) and macro.type not in exclusions:
zn = macro.zone
keys = []
for m in self._allMacro[macro.type]:
if isinstance(m.zone,fzone):
keys.append((m.zone.index,m))
if m.zone.name: keys.append((m.zone.name,m))
elif isinstance(m.zone,tuple): keys.append((m.zone,m))
keys = dict(keys)
if zn.index in keys.keys():
if not overwrite:
_buildWarnings('WARNING: A '+macro.type+' macro for zone '+str(zn.index)+' already exists. Macro will not be defined, use overwrite = True in add() to overwrite the old macro.')
return
else:
self.delete(keys[zn.index])
keys = []
for m in self._allMacro[macro.type]:
if isinstance(m.zone,fzone):
keys.append((m.zone.index,m))
if m.zone.name: keys.append((m.zone.name,m))
elif isinstance(m.zone,tuple): keys.append((m.zone,m))
keys = dict(keys)
if zn.name in keys.keys():
if not overwrite:
_buildWarnings('WARNING: A macro for zone \''+str(zn.name)+'\' already exists. Macro will not be defined, use overwrite = True in add() to overwrite the old macro.')
return
else:
self.delete(keys[zn.name])
self._allMacro[macro.type].append(macro)
self._associate_macro(macro)
def _associate_macro(self,macro): #Associates macro properties with nodes
if not self._associate: return
if isinstance(macro.zone,list):
for zn in macro.zone:
for nd in zn.nodelist:
if macro.type =='rlp': nd._rlpmodel = macro.index
elif macro.type =='permmodel': nd._permmodel = macro.index
elif isinstance(macro.zone,tuple):
nd = self.grid.node[macro.zone[0]]
if macro.type == 'pres':
if macro.param['saturation'] == 1:
nd._Pi = macro.param['pressure']
nd._Ti = macro.param['temperature']
nd._Si = 1.
elif macro.param['saturation'] == 2:
nd._Pi = macro.param['pressure']
nd._Ti = tsat(macro.param['pressure'])
nd._Si = macro.param['temperature']
elif macro.param['saturation'] == 3:
nd._Pi = macro.param['pressure']
nd._Ti = macro.param['temperature']
nd._Si = 0.
elif macro.type == 'rock':
nd._density = macro.param['density']
nd._specific_heat = macro.param['specific_heat']
nd._porosity = macro.param['porosity']
elif macro.type == 'elastic':
nd._youngs_modulus = macro.param['youngs_modulus']
nd._poissons_ratio = macro.param['poissons_ratio']
elif macro.type == 'biot':
nd._thermal_expansion = macro.param['thermal_expansion']
nd._pressure_coupling = macro.param['pressure_coupling']
elif macro.type == 'cond':
nd._conductivity = np.array([macro.param['cond_x'],macro.param['cond_x'],macro.param['cond_x']])
elif macro.type == 'perm':
nd._permeability = np.array([macro.param['kx'],macro.param['ky'],macro.param['kz']])
elif isinstance(macro.zone,fzone) or isinstance(macro.zone,int) or isinstance(macro.zone,str):
zn = macro.zone
if isinstance(macro.zone,int) or isinstance(macro.zone,str): zn = self.zone[zn]
for nd in zn.nodelist:
# add generator properties
if macro.type =='flow':
addToNode = macro.param
nd._generator.update(addToNode)
if macro.type =='co2flow':
addToNode = {'co2rate':macro.param['rate'],'co2energy':macro.param['energy'],'co2impedance':macro.param['impedance'],'co2bc_flag':macro.param['bc_flag']}
nd._generator.update(addToNode)
zn._updateFlag = False
if macro.type == 'pres':
if macro.param['saturation'] == 1:
zn.Pi = macro.param['pressure']
zn.Ti = macro.param['temperature']
zn.Si = 1.
elif macro.param['saturation'] == 2:
zn.Pi = macro.param['pressure']
zn.Ti = tsat(macro.param['pressure'])
zn.Si = macro.param['temperature']
elif macro.param['saturation'] == 3:
zn.Pi = macro.param['pressure']
zn.Ti = macro.param['temperature']
zn.Si = 0.
elif macro.type == 'rock':
zn.density = macro.param['density']
zn.specific_heat = macro.param['specific_heat']
zn.porosity = macro.param['porosity']
elif macro.type == 'elastic':
zn.youngs_modulus = macro.param['youngs_modulus']
zn.poissons_ratio = macro.param['poissons_ratio']
elif macro.type == 'biot':
zn.thermal_expansion = macro.param['thermal_expansion']
zn.pressure_coupling = macro.param['pressure_coupling']
elif macro.type == 'cond':
zn.conductivity = np.array([macro.param['cond_x'],macro.param['cond_x'],macro.param['cond_x']])
elif macro.type == 'perm':
zn.permeability = np.array([macro.param['kx'],macro.param['ky'],macro.param['kz']])
zn._updateFlag = True
def _delete_macro(self,macro): #Deletes macro from data file
self._allMacro[macro.type].remove(macro)
def _macro_zone(self,nums): #Assigns zone to macro dictionary
# assumes object has members 'zone' and 'node'
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvvvvv exception for grad vvvvvvvvvvvvvvvvvvvvvvvvvvvv
if len(nums) == 1: #v
if nums[0].startswith('all'): return self.zone[0] #v
else: return self.zone[abs(int(nums[0]))] #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if (nums[0] == '1' and nums[1] == '0' and nums[2] == '0') or (int(float(nums[0]))<0):
k = _zone_ind(float(nums[0]))
if k in self.zone.keys(): return self.zone[k]
else: pyfehm_print('ERROR: zone '+str(k)+' has not been defined',self._silent); return None
else:
return (int(float(nums[0])),int(float(nums[1])),int(float(nums[2])))
def _write_macro(self,outfile,macroName): #Writes macro dictionary to output file
ws = _title_string(macro_titles[macroName],72)
printToFile = False
filemacros = []
textmacros = []
singlemacros = []
self._allMacro[macroName].sort(key=lambda x: x.zone.index)
keys = [k for k,nul in macro_list[macroName]]
for macro in self._allMacro[macroName]:
# check no additional parameters defined
if dict_key_check(macro.param,keys,'macro '+macro.type): raise KeyError('macro '+macro.type)
if macro.file == -1: printToFile = True; textmacros.append(macro)
elif macro.file: filemacros.append(macro)
else:
if macro._write_one_macro: singlemacros.append(macro)
else: textmacros.append(macro)
if not printToFile:
outfile.write(ws)
if self.sticky_zones:
zns = []
for macro in (textmacros+filemacros):
if isinstance(macro.zone,fzone):
if macro.zone.index : zns.append(macro.zone)
elif isinstance(macro.zone,list) and len(macro.zone) != 0:
for zn in macro.zone:
if zn.index : zns.append(zn)
self._write_zonn_one(outfile,zns)
if textmacros:
if macroName == 'bodyforce':
outfile.write(macroName+' '+textmacros[0].subtype+'\n')
else:
outfile.write(macroName+'\n')
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvvvvv exception for grad vvvvvvvvvvvvvvvvvvvvvvvvvvvv
if macroName == 'grad': outfile.write(str(len(self._allMacro[macroName]))+'\n')
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#for macro in self._allMacro[macroName]:
for macro in textmacros:
if macro.zone == 0: macro.zone = self.zone[0]
if printToFile:
if macro.file != - 1: continue
elif macro.file: continue
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvv exception for stressboun vvvvvvvvvvvvvvvvvvvvvvvvv
if macroName=='stressboun': #v
if macro.subtype != 'fixed': #v
outfile.write(macro.subtype+'\t') #v
if macro.subtype == 'lithograd': #^
outfile.write(str(macro.param['sdepth'])+'\t'+str(macro.param['gdepth'])+'\n')
else: outfile.write('\n') #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
zn = macro.zone
if isinstance(zn,tuple):
outfile.write(str(zn[0])+'\t'+str(zn[1])+'\t'+str(zn[2])+'\t')
else:
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvvvvv exception for grad vvvvvvvvvvvvvvvvvvvvvvvvvvvv
if macroName == 'grad': #v
if not zn.index: outfile.write('all\t') #v
else: outfile.write(str(-zn.index)+'\t') #v
macro.param['direction']=int(macro.param['direction']) #^
macro.param['variable']=int(macro.param['variable']) #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
else:
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
for key in macro_list[macroName]: outfile.write(str(macro.param[key[0]])+'\t')
if not (macroName == 'grad' and macro == self._allMacro[macroName][-1]):
outfile.write('\n')
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvv exception for stressboun vvvvvvvvvvvvvvvvvvvvvvvvv
if macroName=='stressboun' and macro != self._allMacro[macroName][-1]:#v
outfile.write('\nstressboun\n') #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
outfile.write('\n')
##########################################
##########################################
if singlemacros:
for macro in singlemacros:
self._write_zonn_one(outfile,[macro.zone])
if macroName == 'bodyforce':
outfile.write(macroName+' '+singlemacros[0].subtype+'\n')
else:
outfile.write(macroName+'\n')
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvvvvv exception for grad vvvvvvvvvvvvvvvvvvvvvvvvvvvv
if macroName == 'grad': outfile.write(str(len(self._allMacro[macroName]))+'\n')
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if macro.zone == 0: macro.zone = self.zone[0]
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvv exception for stressboun vvvvvvvvvvvvvvvvvvvvvvvvv
if macroName=='stressboun': #v
if macro.subtype != 'fixed': #v
outfile.write(macro.subtype+'\t') #v
if macro.subtype == 'lithograd': #^
outfile.write(str(macro.param['sdepth'])+'\t'+str(macro.param['gdepth'])+'\n')
else: outfile.write('\n') #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
zn = macro.zone
if isinstance(zn,tuple):
outfile.write(str(zn[0])+'\t'+str(zn[1])+'\t'+str(zn[2])+'\t')
else:
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvvvvv exception for grad vvvvvvvvvvvvvvvvvvvvvvvvvvvv
if macroName == 'grad': #v
if not zn.index: outfile.write('all\t') #v
else: outfile.write(str(-zn.index)+'\t') #v
macro.param['direction']=int(macro.param['direction']) #^
macro.param['variable']=int(macro.param['variable']) #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
else:
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
for key in macro_list[macroName]: outfile.write(str(macro.param[key[0]])+'\t')
if not (macroName == 'grad' and macro == self._allMacro[macroName][-1]):
outfile.write('\n')
outfile.write('\n')
##########################################
##########################################
if filemacros and not printToFile:
unique_fnames = []
for macro in filemacros:
if macro.file not in unique_fnames: unique_fnames.append(macro.file)
for file_nm in unique_fnames:
if macroName == 'bodyforce':
outfile.write(macroName+' '+filemacros[0].subtype+'\n')
else:
outfile.write(macroName+'\n')
outfile.write('file\n')
outfile.write(file_nm+'\n')
# if filename does not exist, write file
file_nm = file_nm.split(os.sep)[-1]
if not os.path.isfile(self.work_dir+os.sep+file_nm) or self._writeSubFiles:
if self.work_dir: macrofile = open(self.work_dir+os.sep+file_nm,'w')
else: macrofile = open(file_nm,'w')
macros = [macro for macro in self._allMacro[macroName] if macro.file==file_nm]
for macro in macros: macro.file = -1
self._write_macro(macrofile,macroName)
for macro in macros: macro.file = file_nm
macrofile.write('stop\n')
macrofile.close()
self._write_general_macro(outfile, macroName)
return True
def _get_macro(self,macro): #Constructs macro lists and dictionaries
tempDict = []
for macro in self._allMacro[macro]:
if isinstance(macro.zone,list):
tempDict.append((tuple(macro.zone),macro))
elif isinstance(macro.zone,int):
tempDict.append((macro.zone,macro))
else:
tempDict.append((macro.zone.index,macro))
if isinstance(macro.zone,fzone):
if macro.zone.name:
tempDict.append((macro.zone.name,macro))
return dict(tempDict)
################## MODEL FUNCTIONS
def _read_model(self,infile,modelName): #MODEL: Reads general format models
# redirect of special cases
if modelName == 'ppor': self._read_model_ppor(infile); return
if modelName == 'plastic': self._read_model_plastic(infile); return
line = infile.readline().strip()
models=[]
while line != '': # perm model specification
nums = line.split()
m = fmodel(modelName,index=int(nums[0]))
parVector = [float(num) for num in nums[1:]] # parameter values
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvv exception for rlp model 16 vvvvvvvvvvvvvvvvvvvvvvv
if m.type =='rlp' and m.index ==16: #v
parVector = parVector[:6]+parVector[10:12] #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if m.index in model_list[modelName].keys():
parList = model_list[modelName][m.index] # parameter names
else: # enumerate generic parameter names
parList = ['param'+str(i+1) for i in range(len(parVector))]
m.param = dict(zip(parList,parVector)) # make dictionary
models.append(m)
line = infile.readline().strip()
line = infile.readline().strip()
while line != '': # perm model zone assignments
nums = line.split()
models[int(nums[-1])-1].zonelist.append(self._macro_zone(nums))
line = infile.readline().strip()
for m in models: self._add_model(m)
def _read_model_plastic(self,infile):
# redirect of special cases
line = infile.readline().strip()
line = infile.readline().strip()
nums = line.split()
m = fmodel('plasticmodel',index=int(nums[0]))
m.zonelist = [0]
parVector = [float(num) for num in nums[1:]] # parameter values
if m.index in model_list['plasticmodel'].keys():
parList = model_list['plasticmodel'][m.index] # parameter names
else: # enumerate generic parameter names
parList = ['param'+str(i+1) for i in range(len(parVector))]
m.param = dict(zip(parList,parVector)) # make dictionary
self._add_model(m)
def _read_model_trac(self,infile,transverseFlag):
line = infile.readline().strip()
models1 = []
models2 = []
while line != '': # perm model specification
nums = line.split()
i1 = int(nums[0])
nums1 = nums[1:4]
if transverseFlag:
if len(nums) == 8: i2 = int(nums[4]); nums2 = nums[5:]
elif len(nums) == 7: i2 = 0; nums2 = nums[4:]
else:
if len(nums) == 9: i2 = int(nums[4]); nums2 = nums[5:]
elif len(nums) == 8: i2 = 0; nums2 = nums[4:]
# create adsorption model
m = fmodel('adsorption',index=i1)
parVector = [float(num) for num in nums1] # parameter values
if m.index in model_list[modelName].keys():
parList = model_list[modelName][m.index] # parameter names
else: # enumerate generic parameter names
parList = ['param'+str(i+1) for i in range(len(parVector))]
m.param = dict(zip(parList,parVector)) # make dictionary
models1.append(m)
# create diffusion model
m = fmodel('diffusion',index=i2)
parVector = [float(num) for num in nums2] # parameter values
if transverseFlag: parVector.append(None)
if m.index in model_list[modelName].keys():
parList = model_list[modelName][m.index] # parameter names
else: # enumerate generic parameter names
parList = ['param'+str(i+1) for i in range(len(parVector))]
m.param = dict(zip(parList,parVector)) # make dictionary
models2.append(m)
line = infile.readline().strip()
line = infile.readline().strip()
while line != '': # perm model zone assignments
nums = line.split()
models1[int(nums[-1])-1].zonelist.append(self._macro_zone(nums))
models2[int(nums[-1])-1].zonelist.append(self._macro_zone(nums))
line = infile.readline().strip()
return (models1,models2)
def _read_model_ppor(self,infile):
line = infile.readline().strip()
nums = line.split()
m = fmodel('ppor',index=int(nums[0]))
line = infile.readline().strip()
nums = line.split()
m.zonelist = [self._macro_zone(nums[:3])]
parVector = [float(num) for num in nums[3:]] # parameter values
if m.index in model_list['ppor'].keys():
parList = model_list['ppor'][m.index] # parameter names
else: # enumerate generic parameter names
parList = ['param'+str(i+1) for i in range(len(parVector))]
m.param = dict(zip(parList,parVector)) # make dictionary
self._add_model(m)
line = infile.readline().strip()
def _add_model(self,model):
model._parent = self
if isinstance(model.zonelist,list) and len(model.zonelist)==0: model.zonelist = [self.zone[0]] # assign everywhere zone
elif isinstance(model.zonelist,tuple): model.zonelist = [tuple([int(ls) for ls in model.zonelist])]
elif isinstance(model.zonelist,(int,str)):
if model.zonelist in self.zone.keys():
model.zonelist = [self.zone[model.zonelist]]
else:
pyfehm_print('ERROR: Specified zone '+str(model.zonelist)+' for model '+model.type+' does not exist.',self._silent)
return
elif isinstance(model.zonelist,fzone): model.zonelist = [model.zonelist]
newlist = []
for zn in model.zonelist:
if isinstance(zn,(tuple,fzone)): newlist.append(zn)
elif zn in self.zone.keys(): newlist.append(self.zone[zn])
else:
pyfehm_print('ERROR: Specified zone '+str(zn)+' for model '+model.type+' does not exist.',self._silent)
return
model.zonelist = newlist
self._allModel[model.type].append(model)
self._allModel[model.type].sort(key=lambda x: x.index)
self._associate_model(model)
def _associate_model(self,model):
'placeholder'
def _delete_model(self,model):
self._allModel[model.type].remove(model)
def _write_model(self,outfile,modelName):
# redirect of special cases
if modelName == 'ppor': self._write_model_ppor(outfile); return
if modelName == 'plastic': self._write_model_plastic(outfile); return
ws = _title_string(model_titles[modelName],72)
outfile.write(ws)
if self.sticky_zones:
zns = []
for model in self._allModel[modelName]:
if len(model.zonelist) != 0:
for zn in model.zonelist:
if zn.index : zns.append(zn)
self._write_zonn_one(outfile,zns)
outfile.write(modelName+'\n')
from operator import itemgetter
self._allModel[modelName].sort(key=lambda x: x.index)
for model in self._allModel[modelName]:
# check no additional parameters defined
if model.index in model_list[model.type].keys():
keys = model_list[modelName][model.index]
if dict_key_check(model.param,keys,'macro '+model.type): raise KeyError('model '+model.type)
if model.index in model_list[modelName].keys():
paramList = model_list[modelName][model.index] # parameter names
else: # enumerate generic parameter names
paramList = ['param'+str(i+1) for i in range(len(model.param.keys()))]
outfile.write(str(model.index)+' ')
#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#vvvvvvvvvvvvvvvvvvvv exception for rlp model 16 vvvvvvvvvvvvvvvvvvvvvvv
if model.type =='rlp' and model.index ==16: #v
paramList = (parVector[:6]+['write0','write0','write0','write0']+
parVector[-2:]+['write0','write0']) #^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^ end exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for key in paramList:
if key in model.param.keys():
outfile.write(str(model.param[key])+' ')
elif key =='write0':
outfile.write('0 ')
outfile.write('\n')
outfile.write('\n')
model_count = 1
for model in self._allModel[modelName]:
if model.zonelist == 0: model.zonelist = [self.zonelist[0],]
if not model.zonelist: macro_count+=1; continue
if isinstance(model.zonelist,fzone): model.zonelist = [model.zonelist,]
for zn in model.zonelist:
if isinstance(zn,tuple):
outfile.write(str(zn[0])+'\t'+str(zn[1])+'\t'+str(zn[2])+'\t')
else:
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
outfile.write(str(model_count)+'\n')
model_count+=1
outfile.write('\n')
self._write_general_macro(outfile, modelName)
def _write_model_ppor(self,outfile):
ws = _title_string(model_titles['ppor'],72)
outfile.write(ws)
if self.sticky_zones:
zns = []
for model in self._allModel['ppor']:
if len(model.zonelist) != 0:
for zn in model.zonelist:
if zn.index : zns.append(zn)
self._write_zonn_one(outfile,zns)
outfile.write('ppor'+'\n')
from operator import itemgetter
self._allModel['ppor'].sort(key=lambda x: x.index)
for model in self._allModel['ppor']:
if model.index in model_list['ppor'].keys():
paramList = model_list['ppor'][model.index] # parameter names
else: # enumerate generic parameter names
paramList = ['param'+str(i+1) for i in range(len(model.param.keys()))]
outfile.write(str(model.index)+'\n')
zn = model.zonelist[0]
if isinstance(zn,tuple):
outfile.write(str(zn[0])+'\t'+str(zn[1])+'\t'+str(zn[2])+'\t')
else:
if not zn.index: outfile.write(str(1)+'\t'+'0\t0\t')
else: outfile.write(str(-zn.index)+'\t'+'0\t0\t')
for key in paramList:
if key in model.param.keys():
outfile.write(str(model.param[key])+' ')
elif key =='write0':
outfile.write('0 ')
outfile.write('\n')
outfile.write('\n')
def _write_model_plastic(self,outfile):
ws = _title_string(model_titles['plasticmodel'],72)
outfile.write(ws)
from operator import itemgetter
self._allModel['plasticmodel'].sort(key=lambda x: x.index)
outfile.write('plastic\n')
outfile.write('1\n')
for model in self._allModel['plasticmodel']:
if model.index in model_list['plasticmodel'].keys():
paramList = model_list['plasticmodel'][model.index] # parameter names
else: # enumerate generic parameter names
paramList = ['param'+str(i+1) for i in range(len(model.param.keys()))]
outfile.write(str(model.index)+'\t')
for key in paramList:
if key in model.param.keys():
outfile.write(str(model.param[key])+' ')
elif key =='write0':
outfile.write('0 ')
outfile.write('\n')
def _get_model(self,model):
return dict([(m.index,m) for m in self._allModel[model]])
################## MACRO, ZONE, BOUN LISTS
def _get_pres(self): return self._get_macro('pres')
pres = property(_get_pres)
def _get_preslist(self): return self._allMacro['pres']
preslist = property(_get_preslist)
def _get_perm(self): return self._get_macro('perm')
perm = property(_get_perm)
def _get_permlist(self): return self._allMacro['perm']
permlist = property(_get_permlist)
def _get_rock(self): return self._get_macro('rock')
rock = property(_get_rock)
def _get_rocklist(self): return self._allMacro['rock']
rocklist = property(_get_rocklist)
def _get_cond(self): return self._get_macro('cond')
cond = property(_get_cond)
def _get_condlist(self): return self._allMacro['cond']
condlist = property(_get_condlist)
def _get_grad(self): return self._get_macro('grad')
grad = property(_get_grad)
def _get_gradlist(self): return self._allMacro['grad']
gradlist = property(_get_gradlist)
def _get_flow(self): return self._get_macro('flow')
flow = property(_get_flow)
def _get_flowlist(self): return self._allMacro['flow']
flowlist = property(_get_flowlist)
def _get_hflx(self): return self._get_macro('hflx')
hflx = property(_get_hflx)
def _get_hflxlist(self): return self._allMacro['hflx']
hflxlist = property(_get_hflxlist)
def _get_biot(self): return self._get_macro('biot')
biot = property(_get_biot)
def _get_biotlist(self): return self._allMacro['biot']
biotlist = property(_get_biotlist)
def _get_elastic(self): return self._get_macro('elastic')
elastic = property(_get_elastic)
def _get_elasticlist(self): return self._allMacro['elastic']
elasticlist = property(_get_elasticlist)
def _get_bodyforce(self): return self._get_macro('bodyforce')
bodyforce = property(_get_bodyforce)
def _get_bodyforcelist(self): return self._allMacro['bodyforce']
bodyforcelist = property(_get_bodyforcelist)
def _get_co2frac(self): return self._get_macro('co2frac')
co2frac = property(_get_co2frac)
def _get_co2fraclist(self): return self._allMacro['co2frac']
co2fraclist = property(_get_co2fraclist)
def _get_co2flow(self): return self._get_macro('co2flow')
co2flow = property(_get_co2flow)
def _get_co2flowlist(self): return self._allMacro['co2flow']
co2flowlist = property(_get_co2flowlist)
def _get_co2pres(self): return self._get_macro('co2pres')
co2pres = property(_get_co2pres)
def _get_co2preslist(self): return self._allMacro['co2pres']
co2preslist = property(_get_co2preslist)
def _get_co2diff(self): return self._get_macro('co2diff')
co2diff = property(_get_co2diff)
def _get_co2difflist(self): return self._allMacro['co2diff']
co2difflist = property(_get_co2difflist)
def _get_stressboun(self): return self._get_macro('stressboun')
stressboun = property(_get_stressboun)
def _get_stressbounlist(self): return self._allMacro['stressboun']
stressbounlist = property(_get_stressbounlist)
def _get_permmodel(self): return self._get_model('permmodel')
permmodel = property(_get_permmodel)
def _get_permmodellist(self): return self._allModel['permmodel']
permmodellist = property(_get_permmodellist)
def _get_plasticmodel(self): return self._get_model('plasticmodel')
plasticmodel = property(_get_plasticmodel)
def _get_plasticmodellist(self): return self._allModel['plasticmodel']
plasticmodellist = property(_get_plasticmodellist)
def _get_tpor(self): return self._get_macro('tpor')
tpor = property(_get_tpor)
def _get_tporlist(self): return self._allMacro['tpor']
tporlist = property(_get_tporlist)
def _get_rlp(self): return self._get_model('rlp')
rlp = property(_get_rlp)
def _get_rlplist(self): return self._allModel['rlp']
rlplist = property(_get_rlplist)
def _get_vcon(self): return self._get_model('vcon')
vcon = property(_get_vcon)
def _get_vconlist(self): return self._allModel['vcon']
vconlist = property(_get_vconlist)
def _get_ppor(self): return self._get_model('ppor')
ppor = property(_get_ppor)
def _get_pporlist(self): return self._allModel['ppor']
pporlist = property(_get_pporlist)
def _get__zone_indices(self): return [z.index for z in self.zonelist]
_zone_indices = property(_get__zone_indices)
def _get_flxo(self):
for i,node_pair in enumerate(self._flxo):
nd1,nd2 = node_pair
if isinstance(nd1,int): nd1 = self.grid.node[nd1]
if isinstance(nd2,int): nd2 = self.grid.node[nd2]
self._flxo[i] = (nd1,nd2)
return self._flxo
def _set_flxo(self,value): self._flxo = value
flxo = property(_get_flxo, _set_flxo) #: (*lst*) List containing two-item tuples of nodal pairs for which mass flow flux to be output.
def _get_zonelist(self): return self._zonelist
zonelist = property(_get_zonelist)#: (*lst[fzone]*) List of zone objects in the model.
def _get_zone(self):
return dict([[zn.index,zn] for zn in self.zonelist]+[[zn.name,zn] for zn in self.zonelist if zn.name])
zone = property(_get_zone)#: (*dict[fzone]*) Dictionary of zone objects, indexed by zone number or name.
def _get_bounlist(self): return self._bounlist
bounlist = property(_get_bounlist)#: (*lst[fzone]*) List of boundary condition objects in the model.
def _get_rlpmlist(self): return self._rlpmlist
rlpmlist = property(_get_rlpmlist)
def _get_rlpm(self): return dict([(rlpm.group,rlpm) for rlpm in self._rlpmlist])
rlpm = property(_get_rlpm)
def _get_filename(self): return self._path.filename
filename = property(_get_filename)#: (*str*) File name for reading and writing FEHM input text file.
def _get_gridfilename(self): return self.grid._path.filename
#def _set_gridfilename(self,value): self._gridfilename = value
gridfilename = property(_get_gridfilename) #: (*str*) File name of FEHM grid file.
def _get_inconfilename(self): return self._inconfilename
def _set_inconfilename(self,value): self._inconfilename = value
inconfilename = property(_get_inconfilename, _set_inconfilename) #: (*str*) File name of FEHM restart file.
def _get_verbose(self): return self._verbose
def _set_verbose(self,value):
if not(isinstance(value,bool) or value in [0,1]): pyfehm_print('Boolean values only',self._silent); return
if isinstance(value,int):
if value == 1: value = True
elif value == 0: value = False
self._verbose = value
verbose = property(_get_verbose,_set_verbose)#: (*bool*) Boolean signalling if simulation output to be printed to screen.
def _get_sticky_zones(self): return self._sticky_zones
def _set_sticky_zones(self,value): self._sticky_zones = value
sticky_zones = property(_get_sticky_zones, _set_sticky_zones) #: (*bool*) If ``True`` zone definitions will be written to the input file immediately before they are used inside a macro.
def _get_nfinv(self): return self._nfinv
def _set_nfinv(self,value): self._nfinv = value
nfinv = property(_get_nfinv, _set_nfinv) #: (*int*) Boolean integer calling for generation of finite element coefficients (not recommended, see macro **NFINV**).
def _get_nobr(self): return self._nobr
def _set_nobr(self,value): self._nobr = value
nobr = property(_get_nobr, _set_nobr) #: (*int*) Boolean integer calling for no breaking of connections between boundary condition nodes.
def _get_head(self): return self._head
def _set_head(self,value): self._head = value
head = property(_get_head, _set_head) #: (*int*,*fl64*) Boolean integer calling for head inputs (instead of pressure) in FEHM. If assigned a float, then all input heads will be incremented by this amount.
def _get_air(self): return self._air
def _set_air(self,value): self._air = value
air = property(_get_air, _set_air) #: (*lst*) Three item list assigning (1) integer specifying type of airwater model, (2) reference temperature, (3) reference pressure.
def _get_general_macrolist(self): return self._general_macrolist
general_macrolist = property(_get_general_macrolist) #: (*lst*) List of general macros. A general macro is one that has no explicit PyFEHM definition but is recognized by FEHM. General macros are written to the input file verbatim.
def _get_flxn(self): return self._flxn
def _set_flxn(self,value): self._flxn = value
flxn = property(_get_flxn, _set_flxn) #: (*int*,*fl64*) Boolean integer calling for non-zero source/sink fluxes to be output to file source_sink.flux
def _get_vapl(self): return self._vapl
def _set_vapl(self,value): self._vapl = value
vapl = property(_get_vapl, _set_vapl) #: (*int*) Boolean integer calling for vapor pressure lowering.
def _get_adif(self): return self._adif
def _set_adif(self,value): self._adif = value
adif = property(_get_adif, _set_adif) #: (*int*) Air-water diffusion coefficient, used in conjuction with macro **TRAC**.
def _get_iter(self): return self._iter
iter = property(_get_iter) #: (*dict[fl64,int]*) Iteration parameters (see macro **ITER**).
def _get_ctrl(self): return self._ctrl
ctrl = property(_get_ctrl) #: (*dict[fl64,int]*) Control parameters (see macro **CTRL**).
def _get_time(self): return self._time
time = property(_get_time) #: (*dict[fl64,int]*) Time stepping parameters (see macro **TIME**).
def _get_sol(self): return self._sol
sol = property(_get_sol) #: (*dict[fl64,int]*) Solution parameters (see macro **SOL**).
def _get_files(self): return self._files
files = property(_get_files) #: (*files*) Simulation execution object.
def _get_grid(self): return self._grid
grid = property(_get_grid) #: (*fgrid*) Grid object associated with the model.
def _get_cont(self): return self._cont
cont = property(_get_cont) #: (*fcont*) Contour output for the model.
def _get_strs(self): return self._strs
strs = property(_get_strs) #: (*fstrs*) Stress module object.
def _get_ngas(self): return self._ngas
ngas = property(_get_ngas) #: (*fngas*) Noncondensible gas module.
def _get_carb(self): return self._carb
carb = property(_get_carb) #: (*fcarb*) CO2 module object.
def _get_trac(self): return self._trac
trac = property(_get_trac) #: (*ftrac*) Species transport module object.
def _get_hist(self): return self._hist
hist = property(_get_hist) #: (*fhist*) History output for the model.
def _get_incon(self): return self._incon
incon = property(_get_incon) #: (*fincon*) Initial conditions (restart file) associated with the model.
def _get_storage(self): return self._storage
def _set_storage(self,value): self._storage = value
storage = property(_get_storage, _set_storage) #: (*fstorage*) Storage object. No restrictions on creating, setting attributes. Created attributes will not be used by PyFEHM.
def _get_work_dir(self):
if self._path.absolute_to_workdir: return self._path.absolute_to_workdir
else: return ''
def _set_work_dir(self,value):
self._path.update(value)
self.grid._path.update(value)
self.incon._path.update(value)
try:
os.makedirs(self.work_dir)
except:
pass
work_dir = property(_get_work_dir, _set_work_dir) #: (*str*) Directory in which to store files and run simulation.
def _get_tf(self): return self._tf
def _set_tf(self,value):
self._tf = value
self.time['max_time_TIMS'] = value
pyfehm_print('Maximum simulation time set to '+str(value)+' days.',self._silent)
tf = property(_get_tf, _set_tf) #: (*fl64*) Final simulation time (shortcut).
def _get_ti(self): return self._ti
def _set_ti(self,value):
self._ti = value
self.time['initial_day_INITTIME'] = value
self.time['initial_year_YEAR']=0.
self.time['initial_month_MONTH']=0.
pyfehm_print('Initial simulation time set to '+str(value)+ 'days.',self._silent)
ti = property(_get_ti, _set_ti) #: (*fl64*) Initial simulation time (shortcut), defaults to zero.
def _get_dti(self): return self._dti
def _set_dti(self,value):
self._dti = value
self.time['initial_timestep_DAY'] = value
pyfehm_print('Initial time step size set to '+str(value)+' days.',self._silent)
dti = property(_get_dti, _set_dti) #: (*fl64*) Initial time step size (shortcut).
def _get_dtmin(self): return self._dtmin
def _set_dtmin(self,value):
self._dtmin = value
self.ctrl['min_timestep_DAYMIN'] = value
pyfehm_print('Minimum time step size set to '+str(value)+' days.',self._silent)
dtmin = property(_get_dtmin, _set_dtmin) #: (*fl64*) Minimum time step size (shortcut).
def _get_dtmax(self): return self._dtmax
def _set_dtmax(self,value):
self._dtmax = value
self.ctrl['max_timestep_DAYMAX'] = value
pyfehm_print('Maximum time step size set to '+str(value)+' days.',self._silent)
dtmax = property(_get_dtmax, _set_dtmax) #: (*fl64*) Maximum time step size (shortcut).
def _get_dtn(self): return self._dtn
def _set_dtn(self,value):
value = int(value)
self._dtn = value
self.time['max_timestep_NSTEP'] = value
pyfehm_print('Maximum time step number set to '+str(value)+'.',self._silent)
dtn = property(_get_dtn, _set_dtn) #: (*int*) Maximum number of time steps (shortcut).
def _get_dtx(self): return self._dtx
def _set_dtx(self,value):
self._dtx = value
self.ctrl['timestep_multiplier_AIAA'] = value
pyfehm_print('Time step multiplier set to '+str(value)+'.',self._silent)
dtx = property(_get_dtx, _set_dtx) #: (*fl64*) Time step multiplier, acceleration (shortcut).
def _get_help(self): return self._help
def _set_help(self,value): self._help = value
help = property(_get_help, _set_help) #: (*fhelp*) Module for interactive assistance.
def _get_times(self): return self._times
def _set_times(self,value): self._times = value
times = property(_get_times, _set_times) #: array of additional timestepping information
def _get_output_times(self): return self._output_times
def _set_output_times(self,value):
self._output_times = value
if isinstance(self._output_times,(int,float)): self._output_times = [self._output_times]
if isinstance(self._output_times,(list,tuple)): self._output_times = np.array(self._output_times)
self._times = []
for t in self._output_times:
self.change_timestepping(t)
self._output_times = value
if len(self._output_times) == 0: return
if np.max(self._output_times)>self.tf:
_buildWarnings('WARNING: output requested for times after the simulation end time.')
output_times = property(_get_output_times, _set_output_times) #: (*lst*) List of times at which FEHM should produce output.
class fdiagdata(object):
"""Class for diagnostic data object."""
def __init__(self,parent,name,label,data,lim=[-1.e-9,1.e-9],label_color='k',linestyle='ko-',markersize=4,log=False):
self.parent = parent
self._silent = dflt.silent
self.name = name
self.data = data
self.label = label
self.lim = lim
self.log = log
self.label_color = label_color
self.linestyle = linestyle
self.markersize = markersize
self.defaulting()
def defaulting(self):
"""Some defaults to assign"""
# if its a node, default coloring, units
if self.name.startswith('nd'):
if self.label == 'pressure':
self.label = 'P / MPa'
self.label_color = 'b'
self.linestyle = 'bs-'
elif self.label == 'temperature':
self.label = 'T / degC'
self.label_color = 'r'
self.linestyle = 'r^-'
elif self.label == 'flow':
self.label = 'source / kg/s'
self.label_color = 'k'
self.linestyle = 'ko-'
def _get_time(self):
if len(self.data) == len(self.parent.time.data):
return self.parent.time.data
elif len(self.data) == (len(self.parent.time.data)-1):
return self.parent.time.data[1:]
elif len(self.data) == (len(self.parent.time.data)-2):
return self.parent.time.data[1:-1]
time = property(_get_time)
class fdiagax(object):
def __init__(self,parent):
self.parent = parent
self._silent = dflt.silent
self.slot0 = []
self.slot1 = []
self._plot0 = []
self._plot1 = []
self.bg0 = None
self.bg1 = None
self.residual_plot = None
self.largest_NR_plot1 = None
self.largest_NR_plot2 = None
self.largest_NR_plot3 = None
self.legend = False
def add_empty_plots(self):
for slot in self.slot0:
try:
fdd = self.parent.__getattribute__(slot)
ls = fdd.linestyle
ms = fdd.markersize
except: ls = 'ko-'; ms = 3
self._plot0.append(self.sub0.plot([],[],ls,ms=ms)[0])
if slot.startswith('residual') and not self.residual_plot:
tol = self.parent.parent.ctrl['newton_cycle_tolerance_EPM']
tol2 = self.parent.parent.iter['machine_tolerance_TMCH']
if tol2 < 0.: tol = abs(tol2)
self.residual_plot = self.sub0.plot(self.parent.time.lim,[tol,tol],'k:')[0]
self.largest_NR_plot1 = self.sub0.plot([],[],'b*',ms=8)[0]
self.largest_NR_plot2 = self.sub0.plot([],[],'r*',ms=8)[0]
self.largest_NR_plot3 = self.sub0.plot([],[],'k*',ms=8)[0]
for slot in self.slot1:
try:
fdd = self.parent.__getattribute__(slot)
ls = fdd.linestyle
ms = fdd.markersize
except: ls = 'ko-'; ms = 3
self._plot1.append(self.sub1.plot([],[],ls,ms=ms)[0])
if slot.startswith('residual') and not self.residual_plot:
tol = self.parent.parent.ctrl['newton_cycle_tolerance_EPM']
tol2 = self.parent.parent.iter['machine_tolerance_TMCH']
if tol2 < 0.: tol = abs(tol2)
self.residual_plot = self.sub1.plot(self.parent.time.lim,[tol,tol],'k:')[0]
self.largest_NR_plot1 = self.sub1.plot([],[],'b*',ms=8)[0]
self.largest_NR_plot2 = self.sub1.plot([],[],'r*',ms=8)[0]
self.largest_NR_plot3 = self.sub1.plot([],[],'k*',ms=8)[0]
def reset_bg(self):
# remove any plots
for plt in self._plot0+self._plot1: plt.set_data([],[])
self.parent.fig.canvas.draw()
replotResidual = False
if self.residual_plot:
self.residual_plot.set_data([],[])
replotResidual = True
# save bbox
self.bg0 = self.parent.fig.canvas.copy_from_bbox(self.sub0.bbox)
self.bg1 = self.parent.fig.canvas.copy_from_bbox(self.sub1.bbox)
# re add data
for slot in self.slot0:
self.plot0[slot].set_data(copy(self.parent.__getattribute__(slot).time),copy(self.parent.__getattribute__(slot).data))
for slot in self.slot1:
self.plot1[slot].set_data(copy(self.parent.__getattribute__(slot).time),copy(self.parent.__getattribute__(slot).data))
if replotResidual:
tol = self.parent.parent.ctrl['newton_cycle_tolerance_EPM']
tol2 = self.parent.parent.iter['machine_tolerance_TMCH']
if tol2 < 0.: tol = abs(tol2)
self.residual_plot.set_data(self.parent.time.lim,[tol,tol])
def redraw(self):
self.parent.fig.canvas.restore_region(self.bg0)
self.parent.fig.canvas.restore_region(self.bg1)
self.sub0.hold(True)
self.sub1.hold(True)
for plt in self._plot0:
self.sub0.draw_artist(plt)
for plt in self._plot1:
self.sub1.draw_artist(plt)
# show residuals
if self.largest_NR_plot1:
k = None
for k in self.slot0:
if k.startswith('residual'): ax = 0; break
for k in self.slot1:
if k.startswith('residual'): ax = 1; break
if k:
if ax:
self.sub1.draw_artist(self.largest_NR_plot1)
self.sub1.draw_artist(self.largest_NR_plot2)
self.sub1.draw_artist(self.largest_NR_plot3)
else:
self.sub0.draw_artist(self.largest_NR_plot1)
self.sub0.draw_artist(self.largest_NR_plot2)
self.sub0.draw_artist(self.largest_NR_plot3)
if self.legend: self.redraw_legend()
self.parent.fig.canvas.blit(self.sub0.bbox)
self.parent.fig.canvas.blit(self.sub1.bbox)
def make_legend(self):
if self.slot0 == []: return
self.legend = True
entries = zip([slot.split('_')[0] for slot in self.slot0],['k-','k--','k-.','k:'])
self.lp = [0.15,0.4,0.02,.02,.04,.02,.1]
self.lp[1] = self.lp[6]*(len(entries))
x,y = self.sub1.get_xlim()[0], self.sub1.get_ylim()[0]
dx,dy = np.diff(self.sub1.get_xlim()),np.diff(self.sub1.get_ylim())
self.legend_bg = Rectangle((x+.01*dx,y+.01*dy), dx*self.lp[0], dy*self.lp[1], color='white',zorder=200)
self.sub1.add_patch(self.legend_bg)
text_size = 'x-small'
x1,y1 = x+self.lp[2]*dx,y+(self.lp[1]-self.lp[3])*dy
ln_len = self.lp[4]
txt_gap = self.lp[5]
dyi = -self.lp[6]*dy
self.legend_plts = []
self.legend_txts = []
for entry in entries:
self.legend_plts.append(self.sub1.plot([x1,x1+ln_len*dx],[y1,y1],entry[1],zorder=300)[0])
self.legend_txts.append(self.sub1.text(x1+(ln_len+txt_gap)*dx,y1,entry[0],size=text_size,ha='left',va='center',zorder=300))
y1 = y1 + dyi
def redraw_legend(self):
x,y = self.sub1.get_xlim()[0], self.sub1.get_ylim()[0]
dx,dy = np.diff(self.sub1.get_xlim()),np.diff(self.sub1.get_ylim())
self.legend_bg.set_bounds(x+.01*dx,y+.01*dy,dx*self.lp[0], dy*self.lp[1])
x1,y1 = x+self.lp[2]*dx,y+(self.lp[1]-self.lp[3])*dy
ln_len = self.lp[4]
txt_gap = self.lp[5]
dyi = -self.lp[6] *dy
for plt,txt in zip(self.legend_plts,self.legend_txts):
plt.set_data([x1,x1+ln_len*dx],[y1,y1])
txt.set_position((x1+(ln_len+txt_gap)*dx,y1))
y1 = y1 + dyi
def _get_lim0(self):
lim0 = self.parent.__getattribute__(self.slot0[0]).lim
for slot in self.slot0:
lim = self.parent.__getattribute__(slot).lim
lim0[0] = np.min([lim0[0],lim[0]])
lim0[1] = np.max([lim0[1],lim[1]])
if slot.startswith('residual'):
if self.residual_plot:
tol = self.parent.parent.ctrl['newton_cycle_tolerance_EPM']
tol2 = self.parent.parent.iter['machine_tolerance_TMCH']
if tol2 < 0.: tol = abs(tol2)
if tol>lim0[1]:
lim0[1] = tol*10.
NRdat = self.parent.largest_NR.R_data
if len(NRdat)>0:
lim0[1] = np.max([lim0[1],np.max(NRdat)*10])
return lim0
ylim0 = property(_get_lim0)
def _get_lim1(self):
lim1 = self.parent.__getattribute__(self.slot1[0]).lim
for slot in self.slot1:
lim = self.parent.__getattribute__(slot).lim
lim1[0] = np.min([lim1[0],lim[0]])
lim1[1] = np.max([lim1[1],lim[1]])
if slot.startswith('residual'):
if self.residual_plot:
tol = self.parent.parent.ctrl['newton_cycle_tolerance_EPM']
tol2 = self.parent.parent.iter['machine_tolerance_TMCH']
if tol2 < 0.: tol = abs(tol2)
if tol>lim1[1]:
lim1[1] = tol*10.
NRdat = self.parent.largest_NR.R_data
if len(NRdat)>0:
lim1[1] = np.max([lim1[1],np.max(NRdat)*10])
return lim1
ylim1 = property(_get_lim1)
def _get_logflag0(self):
for slot in self.slot0:
if self.parent.__getattribute__(slot).log: return True
return False
logflag0 = property(_get_logflag0)
def _get_logflag1(self):
for slot in self.slot1:
if self.parent.__getattribute__(slot).log: return True
return False
logflag1 = property(_get_logflag1)
def _get_ylabel0(self):
for slot in self.slot0:
return self.parent.__getattribute__(slot).label
return ''
ylabel0 = property(_get_ylabel0)
def _get_ylabel1(self):
for slot in self.slot1:
return self.parent.__getattribute__(slot).label
return ''
ylabel1 = property(_get_ylabel1)
def _get_plot0(self): return dict(zip(self.slot0,self._plot0))
plot0 = property(_get_plot0)
def _get_plot1(self): return dict(zip(self.slot1,self._plot1))
plot1 = property(_get_plot1)
class fdiagNR(object):
def __init__(self,parent):
self.parent = parent
self._silent = dflt.silent
self.timestep = []
self.R1_data = []
self.R1_node = []
self.R2_data = []
self.R2_node = []
self.R3_data = []
self.R3_node = []
self.R_time = []
def new_node(self,data,node,type):
try: node = self.parent.parent.grid.node[node]
except: pass
if type == 1:
self.R1_data.append(data)
self.R1_node.append(node)
self.R_time.append(self.parent.time.data[-1])
elif type == 2:
self.R2_data.append(data)
self.R2_node.append(node)
elif type == 3:
self.R3_data.append(data)
self.R3_node.append(node)
else:
print 'Invalid type'
def redraw(self):
# search for plot of residuals, if found, plot as stars
k = None
for k in self.parent.axs.keys():
for slot in self.parent.axs[k].slot1:
if slot.startswith('residual'): ax=1;break
for slot in self.parent.axs[k].slot0:
if slot.startswith('residual'): ax=0;break
if k is None: return
nr1 = np.array([[t,r] for t,r,d in zip(self.R_time,self.R_data,self.R_type) if d == 1])
nr2 = np.array([[t,r] for t,r,d in zip(self.R_time,self.R_data,self.R_type) if d == 2])
nr3 = np.array([[t,r] for t,r,d in zip(self.R_time,self.R_data,self.R_type) if d == 3])
if len(nr1) != 0: self.parent.axs[k].largest_NR_plot1.set_data(nr1[:,0],nr1[:,1])
if len(nr2) != 0: self.parent.axs[k].largest_NR_plot2.set_data(nr2[:,0],nr2[:,1])
if len(nr3) != 0: self.parent.axs[k].largest_NR_plot3.set_data(nr3[:,0],nr3[:,1])
self.parent.axs[k].redraw()
def retext(self):
txt = self.text_R(0)
self.parent.texts[0].set_text(txt)
#cnts = bincount
#cnts = heapq.nlargest(2,np.unique())
c = Counter(np.array([nd.index for nd in self.R_node]))
cnts = c.most_common(2)
nd_max = cnts[0][0]
for i,nd in enumerate(self.R_node):
if nd.index == nd_max: break
txt = self.text_R(i)
txt2 = '(1) '+str(cnts[0][1])+' appearance'
if cnts[0][1]>1: txt2 += 's'
txt2 += ':\n'
txt = txt2+txt+'\n'
if len(cnts)>1:
nd_max = cnts[1][0]
for i,nd in enumerate(self.R_node):
if nd.index == nd_max: break
txt1 = self.text_R(i)
txt2 = '(2) '+str(cnts[1][1])+' appearance'
if cnts[1][1]>1: txt2 += 's'
txt2 += ':\n'
txt += txt2+txt1
self.parent.texts[1].set_text(txt)
txt = self.text_R(-1)
self.parent.texts[2].set_text(txt)
self.parent.fig.canvas.draw()
def text_R(self,i,more=False):
nd = self.R_node[i]
txt = 'EQ'+str(self.R_type[i])+', '
txt += 'R = %3.2E, '%self.R_data[i]
txt += 'nd = '+str(nd.index)+', '
txt += 'x = '+str(nd.position[0])+', '
txt += 'y = '+str(nd.position[1])+', '
txt += 'z = '+str(nd.position[2])
txt += '\n'
if len(nd.zonelist)>1:
txt += 'In zones: '
zns = copy(nd.zonelist)
zns.sort(key=lambda x: x.index)
for zn in zns:
txt += str(zn.index)
if zn.name: txt += ' ('+zn.name+')'
txt+= ', '
txt = txt[:-2]
else:
txt += 'Unzoned'
txt += '\n'
if more:
txt += 'Connected nodes: '
for ndi in nd.connected_nodes: txt += str(ndi.index)+', '
txt = txt[:-2]+'\n'
if isinstance(nd.permeability,(float,int)):
k = [nd.permeability,nd.permeability,nd.permeability]
else: k = nd.permeability
if k[0] == k[1] and k[0] == k[2]:
txt += 'k = '+str(k[0])+', '
else:
txt += 'k = ['+str(k[0])+', '+str(k[1])+', '+str(k[2])+'], '
txt += '\n'
txt += 'Pi = '+str(nd.Pi)+', Ti = '+str(nd.Ti)+', '
txt += '\n'
return txt
def _get_R_data(self):
return [np.max([r1,r2,r3]) for r1,r2,r3 in zip(self.R1_data,self.R2_data,self.R3_data)]
R_data = property(_get_R_data) #: (**)
def _get_R_node(self):
nd = []
for r1,n1,r2,n2,r3,n3 in zip(self.R1_data,self.R1_node,self.R2_data,self.R2_node,self.R3_data,self.R3_node):
if (r1>r2) and (r1>r3): nd.append(n1)
else:
if r2>r3: nd.append(n2)
else: nd.append(n3)
return nd
R_node = property(_get_R_node) #: (**)
def _get_R_type(self):
tp = []
for r1,r2,r3 in zip(self.R1_data,self.R2_data,self.R3_data):
if (r1>r2) and (r1>r3): tp.append(1)
else:
if r2>r3: tp.append(2)
else: tp.append(3)
return tp
R_type = property(_get_R_type) #: (**)
class fdiagnostic(object):
"""Class for FEHM real-time diagnosis
"""
def __init__(self,parent):
self.parent = parent
self._silent = dflt.silent
self.file_cv = None # write out information collected by diagnostic tool
self.file_nr = None # write out information collected by diagnostic tool
self.file_nd = None # write out information collected by diagnostic tool
self.nds = []
self.nd_vars = ['pressure','temperature','flow']
self.hide = False
self.silent = False
self.write = True
self._job = True
self.time = fdiagdata(parent=self,data=[0.],name='time',label='time / days')
self.timestep = fdiagdata(parent=self,data=[],name='timestep',label='time step / days',log=True)
self.total_mass = fdiagdata(parent=self,data=[],name='total_mass',label='Net water discharge / kg')
self.total_energy = fdiagdata(parent=self,data=[],name='total_energy',
label='Net energy discharge / MJ',label_color='r',linestyle='rs-')
self.residual1 = fdiagdata(parent=self,data=[],name='residual1',label='residuals (log10)',lim = [1.e-30,1.e-29],linestyle='b^-',log=True)
self.residual2 = fdiagdata(parent=self,data=[],name='residual2',label='residuals (log10)',lim = [1.e-30,1.e-29],linestyle='rs-',log=True)
self.residual3 = fdiagdata(parent=self,data=[],name='residual3',label='residuals (log10)',lim = [1.e-30,1.e-29],linestyle='ko-',log=True)
self.residual1.node = []
self.residual2.node = []
self.residual3.node = []
self.largest_NR = fdiagNR(parent=self)
self.mass_input_rate = fdiagdata(parent=self,data=[],name='mass_input_rate',label='mass input rate / kg s$^{-1}$')
self.mass_output_rate = fdiagdata(parent=self,data=[],name='mass_output_rate',label='mass discharge rate / kg s$^{-1}$')
self.enthalpy_input_rate = fdiagdata(parent=self,data=[],name='enthalpy_input_rate',label='enthalpy input rate / MJ s$^{-1}$')
self.enthalpy_output_rate = fdiagdata(parent=self,data=[],name='enthalpy_output_rate',label='enthalpy output rate / MJ s$^{-1}$')
self.mass_error = fdiagdata(parent=self,data=[],name='mass_error',label='mass conservation error')
self.energy_error = fdiagdata(parent=self,data=[],name='energy_error',label='energy conservation error')
self.rel_frame = 20. # maintain frame at 20x current size
self.ax0 = fdiagax(parent=self)
self.ax1 = fdiagax(parent=self)
self.ax2 = fdiagax(parent=self)
self.ax3 = fdiagax(parent=self)
self.ax0.slot0 = ['timestep']
self.ax0.slot1 = []
self.ax1.slot0 = ['total_mass']
self.ax1.slot1 = ['total_energy']
self.ax2.slot0 = ['residual1','residual2']
self.ax2.slot1 = []
self.ax3.slot0 = []
self.ax3.slot1 = []
self.node = dict([('water',None),('gas',None),('tracer1',None),('tracer2',None)])
def refresh_nodes(self):
ndN = len(self.parent.hist.nodelist)
varN = len(self.parent.hist.variables)
self.write_nd = False
if ndN == 0 or varN == 0: return
self.write_nd = True
if ndN > 0:
self.ax3.slot0.append('nd'+str(self.parent.hist.nodelist[0].index)+'_')
self.ax3.slot0[-1] += self.parent.hist.variables[0]
self.__setattr__(self.ax3.slot0[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot0[-1],label=self.parent.hist.variables[0]))
if varN > 1:
self.ax3.slot1.append('nd'+str(self.parent.hist.nodelist[0].index)+'_')
self.ax3.slot1[-1] += self.parent.hist.variables[1]
self.__setattr__(self.ax3.slot1[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot1[-1],label=self.parent.hist.variables[1]))
if ndN > 1:
self.ax3.slot0.append('nd'+str(self.parent.hist.nodelist[1].index)+'_')
self.ax3.slot0[-1] += self.parent.hist.variables[0]
self.__setattr__(self.ax3.slot0[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot0[-1],label=self.parent.hist.variables[0]))
ls = self.__getattribute__(self.ax3.slot0[-1]).linestyle
self.__getattribute__(self.ax3.slot0[-1]).linestyle = ls[:-1]+'--'
if varN > 1:
self.ax3.slot1.append('nd'+str(self.parent.hist.nodelist[1].index)+'_')
self.ax3.slot1[-1] += self.parent.hist.variables[1]
self.__setattr__(self.ax3.slot1[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot1[-1],label=self.parent.hist.variables[1]))
ls = self.__getattribute__(self.ax3.slot1[-1]).linestyle
self.__getattribute__(self.ax3.slot1[-1]).linestyle = ls[:-1]+'--'
if ndN > 2:
self.ax3.slot0.append('nd'+str(self.parent.hist.nodelist[2].index)+'_')
self.ax3.slot0[-1] += self.parent.hist.variables[0]
self.__setattr__(self.ax3.slot0[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot0[-1],label=self.parent.hist.variables[0]))
ls = self.__getattribute__(self.ax3.slot0[-1]).linestyle
self.__getattribute__(self.ax3.slot0[-1]).linestyle = ls[:-1]+'-.'
if varN > 1:
self.ax3.slot1.append('nd'+str(self.parent.hist.nodelist[2].index)+'_')
self.ax3.slot1[-1] += self.parent.hist.variables[1]
self.__setattr__(self.ax3.slot1[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot1[-1],label=self.parent.hist.variables[1]))
ls = self.__getattribute__(self.ax3.slot1[-1]).linestyle
self.__getattribute__(self.ax3.slot1[-1]).linestyle = ls[:-1]+'-.'
if ndN > 3:
self.ax3.slot0.append('nd'+str(self.parent.hist.nodelist[3].index)+'_')
self.ax3.slot0[-1] += self.parent.hist.variables[0]
self.__setattr__(self.ax3.slot0[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot0[-1],label=self.parent.hist.variables[0]))
ls = self.__getattribute__(self.ax3.slot0[-1]).linestyle
self.__getattribute__(self.ax3.slot0[-1]).linestyle = ls[:-1]+':'
if varN > 1:
self.ax3.slot1.append('nd'+str(self.parent.hist.nodelist[3].index)+'_')
self.ax3.slot1[-1] += self.parent.hist.variables[1]
self.__setattr__(self.ax3.slot1[-1],fdiagdata(parent=self,data=[],name=self.ax3.slot1[-1],label=self.parent.hist.variables[1]))
ls = self.__getattribute__(self.ax3.slot1[-1]).linestyle
self.__getattribute__(self.ax3.slot1[-1]).linestyle = ls[:-1]+':'
def split_line(self,line):
""" Splits a line from command output stream, returns list of string or floats.
"""
ln = line.rstrip().split()
ln2 = []
for lni in ln:
try: ln2.append(float(lni))
except: ln2.append(lni)
if len(ln2) == 0: return None
return ln2
def printout(self,ln):
if not self.silent: print ln
def parse_line(self):
line = self.stdout.readline()
if not line:
try:
if self.poll() is not None: return
except:
if self.poll == False: return
self.printout(line.rstrip())
ln = self.split_line(line)
if ln is not None:
if self.update_timestep(ln): pass
elif self.update_mass(ln): pass
elif self.update_energy(ln): pass
elif self.update_mass_input(ln): pass
elif self.update_mass_output(ln): pass
elif self.update_enthalpy_input(ln): pass
elif self.update_enthalpy_output(ln): pass
#elif self.update_node(ln): pass
elif self.update_node2(ln): pass
elif self.update_errors(ln): pass
elif self.update_residuals(ln): pass
elif self.update_largestNR(ln): pass
elif self.close_files(ln): pass
if not self.hide:
self._job = self.root.after(0,self.parse_line)
def handler(self):
self.root.quit()
self.root.destroy()
def construct_viewer(self):
""" Assembles axes on the screen.
"""
# set residual slots
if self.parent.carb.iprtype != 0:
self.ax2.slot0 = ['residual1','residual2','residual3']
elif self.parent.strs.param['ISTRS']==1:
self.ax2.slot0 = ['residual1','residual2','residual3']
else:
self.ax2.slot0 = ['residual1','residual2']
# create figure window
self.root = tk.Tk()
self.root.wm_title('PyFEHM diagnostic window: '+self.parent.filename)
self.root.protocol('WM_DELETE_WINDOW', self.handler)
if has_ctypes:
user32 = ctypes.windll.user32
screensize = np.array([user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)])
self.fig = plt.figure(figsize = screensize/120.,dpi = 100)
#mng = plt.get_current_fig_manager()
#mng.window.wm_geometry('+%04i+%04i'%(screensize[0]/20.,screensize[1]/20.))
else:
self.fig = plt.figure()
x1,x2 = 0.1, 0.55
y1,y2,y3 = 0.1,0.4,0.7
dx = 0.35
dy = 0.25
text_size = 9
# create the four twinned axes, generic naming convention
self.ax0.sub0 = plt.axes([x1,y3,dx,dy])
self.ax0.sub1 = self.ax0.sub0.twinx()
self.ax1.sub0 = plt.axes([x1,y2,dx,dy])
self.ax1.sub1 = self.ax1.sub0.twinx()
self.ax2.sub0 = plt.axes([x1,y1,dx,dy])
self.ax2.sub1 = self.ax2.sub0.twinx()
self.ax3.sub0 = plt.axes([x2,y1,dx,dy])
self.ax3.sub1 = self.ax3.sub0.twinx()
# set up text axes
self.txt = plt.axes([x2,y2,dx,dy+(y2-y1)])
self.txt.set_xticks([])
self.txt.set_yticks([])
self.txt.hold(True)
self.texts = []
self.txt.text(0.5,0.95,'Summary of largest N-R corrections',ha='center',va='center',weight='bold',size=12)
self.txt.text(0.03,0.85,'First..............................................................................',ha='left',va='center',weight='bold',fontstyle='italic',size=11)
self.txt.text(0.03,0.63,'Most frequent...............................................................',ha='left',va='center',weight='bold',fontstyle='italic',size=11)
self.txt.text(0.03,0.21,'Most recent..................................................................',ha='left',va='center',weight='bold',fontstyle='italic',size=11)
self.texts.append(self.txt.text(0.07,0.82,'',ha='left',va='top',size=10))
self.texts.append(self.txt.text(0.07,0.6,'',ha='left',va='top',size=10))
self.texts.append(self.txt.text(0.07,0.18,'',ha='left',va='top',size=10))
t1 = np.min([self.parent.dti*self.rel_frame,self.parent.tf])
self.time.lim=[0.,t1]
# initialise all axes limits/labels
for k in self.axs.keys():
ax = self.axs[k]
ax0 = self.axs[k].sub0
ax1 = self.axs[k].sub1
ax0.set_xlim(self.time.lim)
ax1.set_xlim(self.time.lim)
ax0.set_xlabel(self.time.label,size = text_size)
for t in ax0.get_xticklabels(): t.set_fontsize(text_size)
if not ax.slot0:
ax0.set_yticks([])
ax0.set_yticklabels([])
else:
c = Counter([self.__getattribute__(slot).label_color for slot in ax.slot0])
col = c.most_common(1)[0][0]
ax0.set_ylim(ax.ylim0)
ax0.set_ylabel(ax.ylabel0,size = text_size,color = col)
if ax.logflag0: ax0.set_yscale('log')
ax0.hold(True)
for t in ax0.get_yticklabels():
t.set_fontsize(text_size)
t.set_color(col)
if not ax.slot1:
ax1.set_yticks([])
ax1.set_yticklabels([])
else:
c = Counter([self.__getattribute__(slot).label_color for slot in ax.slot1])
col = c.most_common(1)[0][0]
ax1.set_ylim(ax.ylim1)
ax1.set_ylabel(ax.ylabel1,size = text_size,color = col)
if ax.logflag1: ax1.set_yscale('log')
ax1.hold(True)
for t in ax1.get_yticklabels():
t.set_fontsize(text_size)
t.set_color(col)
if k == '3': self.axs[k].make_legend()
self.canvas = FigureCanvasTkAgg(self.fig,master = self.root)
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.canvas.show()
for k in self.axs.keys():
self.axs[k].add_empty_plots()
self.axs[k].reset_bg()
self.root.after(0,self.parse_line)
self.root.mainloop()
def read_with_tcl(self):
while self.poll: self.parse_line()
self.close_files('timestep less than daymin')
def update_tlim(self):
if self.hide: return
if not self.time.data[-1]>self.time.lim[-1]: return
self.time.lim[1] = np.min([self.time.data[-1]*self.rel_frame,self.parent.tf])
for k in self.axs.keys():
self.axs[k].sub0.set_xlim(self.time.lim)
self.axs[k].sub0.hold(True)
self.axs[k].sub1.set_xlim(self.time.lim)
self.axs[k].sub1.hold(True)
def update_lim(self,name):
""" Redraw vertical axes if necessary. """
if self.hide: return
# rescale axes
ax = None
for k in self.axs.keys():
if name in self.axs[k].slot0: ax = 0; break
elif name in self.axs[k].slot1: ax = 1; break
if ax is None: return
lim0 = self.__getattribute__(name).lim
dat0 = self.__getattribute__(name).data
if not (np.min(dat0)<lim0[0] or np.max(dat0)>lim0[1]): return # check if update required
if ax:
dat0 = []
for slot in self.axs[k].slot1:
dat0 += list(self.__getattribute__(slot).data)
else:
dat0 = []
for slot in self.axs[k].slot0:
dat0 += list(self.__getattribute__(slot).data)
# calculate new limit
if not self.__getattribute__(name).log:
dmin = np.min(dat0)
dmax = np.max(dat0)
#if ax: dmid = (0.4*dmin+0.6*dmax)/2.
#else: dmid = (0.6*dmin+0.4*dmax)/2.
dmid = (dmin+dmax)/2.
drange = dmax-dmin
if dat0[-1]>lim0[1]:
self.__getattribute__(name).lim = [dmid-drange*1.1,dmid+drange*self.rel_frame/5.]
else:
self.__getattribute__(name).lim = [dmid-drange*self.rel_frame/5.,dmid+drange*1.1]
else:
dmin = np.min(np.log10(dat0))
dmax = np.max(np.log10(dat0))
dmid = (dmin+dmax)/2.
drange = dmax-dmin
if dat0[-1]>lim0[1]:
self.__getattribute__(name).lim = 10**(np.array([dmid-drange*1.1,dmid+drange*self.rel_frame/5.]))
else:
self.__getattribute__(name).lim = 10**(np.array([dmid-drange*self.rel_frame/5.,dmid+drange*1.1]))
if ax:
self.axs[k].sub1.set_ylim(self.axs[k].ylim1)
self.axs[k].sub1.hold(True)
else:
self.axs[k].sub0.set_ylim(self.axs[k].ylim0)
self.axs[k].sub0.hold(True)
self.axs[k].reset_bg()
def update_plot(self,name):
if self.hide: return
ax = None
for k in self.axs.keys():
if name in self.axs[k].slot0: ax = 0; break
elif name in self.axs[k].slot1: ax = 1; break
if ax is None: return
if ax:
self.axs[k].plot1[name].set_data(copy(self.__getattribute__(name).time),copy(self.__getattribute__(name).data))
else:
self.axs[k].plot0[name].set_data(copy(self.__getattribute__(name).time),copy(self.__getattribute__(name).data))
self.axs[k].redraw()
def close_files(self,ln):
cs1 = 'total code time(timesteps)'
cs2 = 'timestep less than daymin'
returnFlag = True
if all([(lni == chk) for lni, chk in zip(cs1.split(),ln)]): returnFlag = False
if all([(lni == chk) for lni, chk in zip(cs2.split(),ln)]): returnFlag = False
if returnFlag: return
if self.file_cv: self.file_cv.close()
if self.file_nr:
self.summarise_nr()
self.file_nr.close()
if self.file_nd: self.file_nd.close()
if self.poll is True: self.poll = False
def summarise_nr(self):
self.file_nr.write('\n')
self.file_nr.write('#############################################################################\n')
self.file_nr.write('############################# SUMMARY ###################################\n')
self.file_nr.write('#############################################################################\n')
self.file_nr.write('\n')
self.file_nr.write('FIRST largest N-R correction\n')
self.file_nr.write(self.largest_NR.text_R(0,True))
self.file_nr.write('\n')
self.file_nr.write('\n')
self.file_nr.write('MOST FREQUENT largest N-R corrections\n')
c = Counter(np.array([nd.index for nd in self.largest_NR.R_node]))
cnts = c.most_common(2)
nd_max = cnts[0][0]
for i,nd in enumerate(self.largest_NR.R_node):
if nd.index == nd_max: break
txt = self.largest_NR.text_R(i,True)
txt2 = '(1) '+str(cnts[0][1])+' appearance'
if cnts[0][1]>1: txt2 += 's'
txt2 += ':\n'
txt = txt2+txt+'\n'
if len(cnts)>1:
nd_max = cnts[1][0]
for i,nd in enumerate(self.largest_NR.R_node):
if nd.index == nd_max: break
txt1 = self.largest_NR.text_R(i,True)
txt2 = '(2) '+str(cnts[1][1])+' appearance'
if cnts[1][1]>1: txt2 += 's'
txt2 += ':\n'
txt += txt2+txt1
self.file_nr.write(txt)
self.file_nr.write('\n')
self.file_nr.write('\n')
self.file_nr.write('MOST RECENT largest N-R correction\n')
txt = self.largest_NR.text_R(-1,True)
self.file_nr.write(txt)
# create a zone for largestNR nodes in case of export to paraview
if 980 not in self.parent.zone.keys():
self.parent.new_zone(980,'largestNR',nodelist = self.largest_NR.R_node)
def write_NR(self):
if self.file_nr is None:
# first time step, open file
if self.parent.work_dir: wd = self.parent.work_dir+os.sep
else: wd=''
self.file_nr = open(wd+self.parent.files.root+'_NR.dgs','w')
self.file_nr.write('largest N-R correction, timestep %5i\n'%(len(self.timestep.data)+1))
self.file_nr.write(self.largest_NR.text_R(-1,True))
self.file_nr.write('\n')
self.file_nr.flush()
os.fsync(self.file_nr)
def write_timestep(self):
""" Writes data for a single time step to file.
"""
headers = ['timestep','time','total_mass','total_energy','residual1','residual2','residual3','mass_input_rate',
'mass_output_rate','enthalpy_input_rate','enthalpy_output_rate','mass_error','energy_error']
if self.file_cv is None:
if self.parent.work_dir: wd = self.parent.work_dir+os.sep
else: wd=''
# first time step, open files
self.file_cv = open(wd+self.parent.files.root+'_convergence.dgs','w',1)
# write headers
self.file_cv.write('index ')
N = 14
for h in headers:
if len(h)>N: h = h[:N]
self.file_cv.write('%14s'%h+'\t')
self.file_cv.write('\n')
return
if self.write_nd and not self.file_nd:
if self.parent.work_dir: wd = self.parent.work_dir+os.sep
else: wd=''
self.file_nd = open(wd+self.parent.files.root+'_node.dgs','w')
self.file_nd.write('index ')
nm = 'time(days)'
self.file_nd.write('%16s'%nm+' ')
for nd in self.nds:
for nm in self.nd_vars:
nm = 'nd_'+str(nd)+'_'+nm
if len(nm)>15: nm = nm[:15]
self.file_nd.write('%16s'%nm+' ')
self.file_nd.write('\n')
# write time step
self.file_cv.write('%5i '%len(self.timestep.data))
for h in headers:
try:
self.file_cv.write('% 8.7e '%(self.__getattribute__(h).data[-1]))
except:
self.file_cv.write('% 8.7e '%(0))
self.file_cv.write('\n')
self.file_cv.flush()
os.fsync(self.file_cv)
if self.file_nd:
self.file_nd.write('%5i '%len(self.timestep.data))
self.file_nd.write('% 10.9e '%self.time.data[-1])
for nd in self.nds:
for nm in self.nd_vars:
try: val=self.__getattribute__('nd'+str(nd)+'_'+nm).data[-1]
except: val=self.__getattribute__('nd'+str(nd)+'_'+nm)[-1]
self.file_nd.write('% 10.9e '%(val))
self.file_nd.write('\n')
self.file_nd.flush()
os.fsync(self.file_nd)
def update_timestep(self,ln):
""" Update the time step plot.
"""
check_string = 'Years Days Step Size'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
if self.write: self.write_timestep()
line = self.stdout.readline()
self.printout(line.rstrip())
ln = self.split_line(line)
yr, day, dt = ln
self.time.data.append(day)
self.timestep.data.append(dt)
if len(self.timestep.data) == 1:
pt = self.timestep.data[0]
self.timestep.lim = [pt-1.e-30,pt+1.e-30]
self.update_tlim()
self.update_lim('timestep')
self.update_plot('timestep')
return True
def update_mass(self,ln):
check_string = 'Net kg water discharge'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.total_mass.data.append(ln[-1])
if len(self.total_mass.data) == 1: return True
self.update_lim('total_mass')
self.update_plot('total_mass')
return True
def update_energy(self,ln):
check_string = 'Net MJ energy discharge'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.total_energy.data.append(ln[-1])
if len(self.total_energy.data) == 1: return True
self.update_lim('total_energy')
self.update_plot('total_energy')
return True
def update_mass_input(self,ln):
check_string = 'Water input this time step:'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.mass_input_rate.data.append(float(ln[-2][1:]))
self.update_lim('mass_input_rate')
self.update_plot('mass_input_rate')
return True
def update_mass_output(self,ln):
check_string = 'Water discharge this time step:'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.mass_output_rate.data.append(float(ln[-2][1:]))
self.update_lim('mass_output_rate')
self.update_plot('mass_output_rate')
return True
def update_enthalpy_input(self,ln):
check_string = 'Enthalpy input this time step:'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.enthalpy_input_rate.data.append(float(ln[-2][1:]))
self.update_lim('enthalpy_input_rate')
self.update_plot('enthalpy_input_rate')
return True
def update_enthalpy_output(self,ln):
check_string = 'Enthalpy discharge this time step:'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.enthalpy_output_rate.data.append(float(ln[-2][1:]))
self.update_lim('enthalpy_output_rate')
self.update_plot('enthalpy_output_rate')
return True
def update_node(self,ln):
check_string = 'Nodal Information (Water)'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.printout(self.stdout.readline().rstrip())
self.printout(self.stdout.readline().rstrip())
self.write_nd = True
keepReading = True
updates = []
while keepReading:
# get line to process
line = self.stdout.readline()
self.printout(line.rstrip() )
ln = self.split_line(line)
# check if line empty -> break
if ln == None: break
# parse line, store information
if len(ln) == 7:
nd,ndP,ndE,ndL,ndT,ndQ,ndQE = ln
elif len(ln) == 8:
nd,ndP,ndT,ndSw,ndSaq,ndS, ndQ,ndQE = ln
if int(nd) not in self.nds: self.nds.append(int(nd))
nd = str(int(nd))
for nm,ndI in zip(self.nd_vars,[ndP,ndT,ndQ]):
nm = 'nd'+nd+'_'+nm
try:
self.__getattribute__(nm).data.append(ndI)
updates.append(nm)
except:
try: self.__getattribute__(nm).append(ndI)
except: self.__setattr__(nm,[ndI])
for update in updates:
self.update_lim(update)
self.update_plot(update)
return True
def update_node2(self,ln):
check_string = 'Nodal Information'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
####### 1. check species type, e.g., water, gas, tracer
node_type = ln[-1]
if node_type == '(Water)': self.update_node_general('water')
elif node_type == '(Gas)': self.update_node_general('gas')
elif node_type == '(Tracer)':
ln = self.stdout.readline().rstrip()
self.printout(ln)
ln = ln.split()[-1]
self.update_node_general('tracer'+ln)
def update_node_general(self,type):
if self.node[type] == None:
self.node[type]={}
lns = [self.stdout.readline().rstrip()]
while lns[-1].split()[0] != 'Node': lns.append(self.stdout.readline().rstrip())
for ln in lns: self.printout(ln)
if type == 'water':
keys_read = ['P (MPa)','E (MJ)','L sat','Temp (C)','(kg/s)','(MJ/s)','perm (m2)','porosity','Kx W/(m K)','Pwv (MPa)','D*wv (m2/s)','ps_delta_rxn','density (kg/m3)']
keys_save = ['P','E','sat','T','Qm','Qe','perm','por','Kx','Pwv','D*wv','ps_delta','dens']
elif type == 'gas':
keys_read = ['Gas (MPa)','Pres (MPa)','(kg/s)','Residual']
keys_read2 = ['Capillary','Liquid']
keys_save = ['P_gas','Pres (MPa)','Qgas','residual']
keys_save2 = ['P_cap','P_liq']
elif type.startswith('tracer'):
keys_read = ['an','anl','anw','mol/s','residual']
keys_read2 = ['sinkint']
keys_save = ['an','anl','anw','Q','residual']
keys_save2 = ['sinkint']
key_pos = []
for kr,ks in zip(keys_read,keys_save):
if kr in lns[-1]:
if kr == 'Pres (MPa)':
for kr2,ks2 in zip(keys_read2,keys_save2):
if kr2 in lns[0]:
key_pos.append((ks2,len(lns[-1].split(kr2)[0])))
else:
key_pos.append((ks,len(lns[-1].split(kr)[0])))
if type.startswith('tracer'):
for kr2,ks2 in zip(keys_read2,keys_save2):
if kr2 in lns[0]:
key_pos.append((ks2,len(lns[-1].split(kr2)[0])))
key_pos.sort(key=lambda x: x[1])
ln = self.stdout.readline().rstrip()
self.printout(ln)
ln = ln.split()
while ln[0] != '-':
nd = int(ln[0])
vals = [float(lni) for lni in ln[1:]]
if nd not in self.node[type].keys():
self.node[type][nd] = dict([(k[0],[]) for k in key_pos])
for k,val in zip(key_pos,vals):
if k[0] not in self.node[type][nd].keys(): self.node[type][nd][k[0]] = []
self.node[type][nd][k[0]].append(val)
ln = self.stdout.readline().rstrip()
self.printout(ln)
ln = ln.split()
if len(ln) == 0: break
if not ln[0].isdigit():
while not ln[0]=='-':
ln = self.stdout.readline().rstrip()
self.printout(ln)
ln = ln.split()
self.update_node_general(type)
return
return
def update_errors(self,ln):
check_string = 'Conservation Errors:'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
self.mass_error.data.append(ln[2])
self.energy_error.data.append(ln[-2])
self.update_lim('mass_error')
self.update_plot('mass_error')
self.update_lim('energy_error')
self.update_plot('energy_error')
return True
def update_residuals(self,ln):
check_string = 'Largest Residuals'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
# get first residual
r = []
nd = []
line = self.stdout.readline()
self.printout(line.rstrip())
ln = self.split_line(line)
self.residual1.data.append(abs(ln[2]))
self.residual1.node.append(int(ln[4]))
if len(self.residual1.data) == 1:
pt = self.residual1.data[0]
self.residual1.lim = [pt-1.e-30,pt+1.e-30]
# get second residual
line = self.stdout.readline()
self.printout(line.rstrip())
ln = self.split_line(line)
self.residual2.data.append(abs(ln[2]))
self.residual2.node.append(int(ln[4]))
if len(self.residual2.data) == 1:
pt = self.residual2.data[0]
self.residual2.lim = [pt-1.e-30,pt+1.e-30]
# get third residual
line = self.stdout.readline()
self.printout(line.rstrip())
ln = self.split_line(line)
if ln[0] == 'EQ3':
self.residual3.data.append(abs(ln[2]))
self.residual3.node.append(int(ln[4]))
if len(self.residual3.data) == 1:
pt = self.residual3.data[0]
self.residual3.lim = [pt-1.e-30,pt+1.e-30]
# plot residuals on log axes
self.update_lim('residual1')
self.update_plot('residual1')
self.update_lim('residual2')
self.update_plot('residual2')
if ln[0] == 'EQ3':
self.update_lim('residual3')
self.update_plot('residual3')
def update_largestNR(self,ln):
check_string = '#### largest N-R corrections, timestep'
if len(check_string.split()) > len(ln): return False
if not all([(lni == chk) for lni, chk in zip(check_string.split(),ln)]): return False
line = self.stdout.readline()
self.printout(line.rstrip())
try: self.split_line(line)[2]
except: return
self.largest_NR.timestep.append(int(ln[-2]))
ln = self.split_line(line)
self.largest_NR.new_node(ln[2],int(ln[4]),1)
line = self.stdout.readline()
self.printout(line.rstrip())
ln = self.split_line(line)
self.largest_NR.new_node(ln[2],int(ln[4]),2)
line = self.stdout.readline()
self.printout(line.rstrip())
ln = self.split_line(line)
try:
self.largest_NR.new_node(ln[2],int(ln[4]),3)
except:
self.largest_NR.R3_data.append(1.e-30)
self.largest_NR.R3_node.append([])
if len(self.parent.grid.nodelist) ==0:
pass
else:
if self.write: self.write_NR()
self.largest_NR.redraw()
self.largest_NR.retext()
def _get_axs(self): return dict(zip(['0','1','2','3'],[self.ax0,self.ax1,self.ax2,self.ax3]))
axs = property(_get_axs)
[docs]class foutput(object):
from copy import deepcopy
def __init__(self,filename = None, input=None, grid = None, hide = True, silent=True, write = False):
self._filename = filename
self._silent = dflt.silent
if self._filename:
if input and grid:
diag = process_output(filename, hide = hide,silent = silent,input=input,grid=grid,write=write)
else:
diag = process_output(filename, hide = hide,silent=silent,write=write)
self._node = deepcopy(diag.node)
self._times = deepcopy(diag.time.data[1:])
def _get_node(self): return self._node
node = property(_get_node) #: (*dict*) Dictionary of node output, keyed first on component ('water','gas','tracer1'), then on node number, then on variable.
def _get_nodes(self):
for type in ['water','gas','tracer1']:
if self._node[type] == None: continue
nds = self._node[type].keys()
nds.sort()
return nds
return None
nodes = property(_get_nodes)
def _get_variables(self):
""" Get Variables
Returns the variables in the foutput object or returns None if no
variables. """
for type in ['water','gas','tracer1']:
for node in self.nodes:
if self._node[type][node] == None:
continue
vrbls = self._node[type][node].keys()
vrbls.sort()
return vrbls
return None
variables = property(_get_variables) #: (*lst*) List of variables available for the various components.
def _get_components(self):
cpts = []
for type in ['water','gas','tracer1','tracer2']:
if self._node[type] != None: cpts.append(type)
return cpts
components = property(_get_components) #: (*lst*) Component names for which nodal information available
def _get_times(self): return self._times
times = property(_get_times) #: (*ndarray*) Vector of output times.
def _get_filename(self): return self._filename
def _set_filename(self,value): self._filename = value
filename = property(_get_filename, _set_filename) #: (*str*) Name of output file
def _get_information(self):
print 'FEHM output file \''+self.filename+'\''
print ' call format: foutput.node[component][node][variable]'
prntStr = ' components: '
for cpt in self.components: prntStr += str(cpt)+', '
print prntStr[:-2]
prntStr = ' nodes: '
for nd in self.nodes: prntStr += str(nd)+', '
print prntStr[:-2]
what = property(_get_information) #:(*str*) Print out information about the fcontour object.