+importos
+importsubprocess
+importsys
+importglob
+importshutil
+fromtimeimporttime
+importnumpyasnp
+"""
+Functions for using FEHM in dfnWorks
+"""
+
+
+
+[docs]
+defcorrect_stor_file(self):
+"""Corrects volumes in stor file to account for apertures
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ Currently does not work with cell based aperture
+ """
+ # Make input file for C Stor converter
+ ifself.flow_solver!="FEHM":
+ error="Error. Incorrect flow solver requested\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ self.dump_hydraulic_values(format="FEHM")
+
+ self.stor_file=self.inp_file[:-4]+'.stor'
+ self.mat_file=self.inp_file[:-4]+'_material.zone'
+ withopen("convert_stor_params.txt","w")asf:
+ f.write("%s\n"%self.mat_file)
+ f.write("%s\n"%self.stor_file)
+ f.write("%s"%(self.stor_file[:-5]+'_vol_area.stor\n'))
+ f.write("%s\n"%self.aper_file)
+
+ t=time()
+ cmd=os.environ['CORRECT_STOR_EXE']+' convert_stor_params.txt'
+ failure=subprocess.call(cmd,shell=True)
+ iffailure>0:
+ error='ERROR: stor conversion failed\nExiting Program\n'
+ sys.stderr.write(error)
+ sys.exit(1)
+ elapsed=time()-t
+ print('--> Time elapsed for STOR file conversion: %0.3f seconds\n'%
+ elapsed)
+
+
+
+defcorrect_perm_for_fehm():
+""" FEHM wants an empty line at the end of the perm file
+ This functions adds that line return
+
+ Parameters
+ ----------
+ None
+
+ Returns
+ ---------
+ None
+
+ Notes
+ ------------
+ Only adds a new line if the last line is not empty
+ """
+ fp=open("perm.dat")
+ lines=fp.readlines()
+ fp.close()
+ # Check if the last line of file is just a new line
+ # If it is not, then add a new line at the end of the file
+ iflen(lines[-1].split())!=0:
+ print("--> Adding line to perm.dat")
+ fp=open("perm.dat","a")
+ fp.write("\n")
+ fp.close()
+
+
+
+[docs]
+defdfn_gen(self,output=True):
+''' Wrapper script the runs the dfnGen workflow:
+ 1) make_working_directory: Create a directory with name of job
+ 2) check_input: Check input parameters and create a clean version of the input file
+ 3) create_network: Create network. DFNGEN v2.0 is called and creates the network
+ 4) output_report: Generate a PDF summary of the DFN generation
+ 5) mesh_network: calls module dfnGen_meshing and runs LaGriT to mesh the DFN
+
+ Parameters
+ ----------
+ self :
+ DFN object
+ output : bool
+ If True, output pdf will be created. If False, no pdf is made
+ visual_mode : None
+ If the user wants to run in a different meshing mode from what is in params.txt, set visual_mode = True/False on command line to override meshing mode
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ Details of each portion of the routine are in those sections
+
+ '''
+ # Create Working directory
+ self.make_working_directory()
+ # Check input file
+ self.check_input()
+ # Create network
+ self.create_network()
+ ifoutput:
+ self.output_report()
+ # Mesh Network
+ self.mesh_network()
+ print('='*80)
+ print('dfnGen Complete')
+ print('='*80)
+
+
+
+
+[docs]
+defmake_working_directory(self,delete=False):
+''' Make working directory for dfnWorks Simulation
+
+ Parameters
+ ----------
+ self :
+ DFN object
+
+ delete : bool
+ If True, deletes the existing working directory. Default = False
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ If directory already exists, user is prompted if they want to overwrite and proceed. If not, program exits.
+ '''
+
+ ifnotdelete:
+ try:
+ os.mkdir(self.jobname)
+ exceptOSError:
+ ifos.path.isdir(self.jobname):
+ print('\nFolder ',self.jobname,' exists')
+ keep=input('Do you want to delete it? [yes/no] \n')
+ ifkeep=='yes'orkeep=='y':
+ print('Deleting',self.jobname)
+ shutil.rmtree(self.jobname)
+ print('Creating',self.jobname)
+ os.mkdir(self.jobname)
+ elifkeep=='no'or'n':
+ error="Not deleting folder. Exiting Program\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ error="Unknown Response. Exiting Program\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ error=f"Unable to create working directory {self.jobname}\n. Please check the provided path.\nExiting\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ ifnotos.path.isdir(self.jobname):
+ os.mkdir(self.jobname)
+ else:
+ try:
+ shutil.rmtree(self.jobname)
+ print('--> Creating ',self.jobname)
+ os.mkdir(self.jobname)
+ except:
+ error="ERROR deleting and creating directory.\nExiting\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ os.mkdir(self.jobname+'/dfnGen_output')
+ os.mkdir(self.jobname+'/dfnGen_output/radii')
+ os.mkdir(self.jobname+'/intersections')
+ os.mkdir(self.jobname+'/polys')
+ os.chdir(self.jobname)
+
+ print(f"Current directory is now: {os.getcwd()}")
+ print(f"Jobname is {self.jobname}")
+
+
+
+
+[docs]
+defcreate_network(self):
+''' Execute dfnGen
+
+ Parameters
+ ----------
+ self :
+ DFN object
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ After generation is complete, this script checks whether the generation of the fracture network failed or succeeded based on the existence of the file params.txt.
+ '''
+ print('--> Running DFNGEN')
+ os.chdir(self.jobname)
+ cmd=os.environ[
+ 'DFNGEN_EXE']+' '+'dfnGen_output/'+self.local_dfnGen_file[:
+ -4]+'_clean.dat'+' '+self.jobname
+
+ print(f"Running:\n>>{cmd}")
+ subprocess.call(cmd,shell=True)
+
+ ifos.path.isfile("params.txt"):
+ self.gather_dfn_gen_output()
+ self.assign_hydraulic_properties()
+ print('-'*80)
+ print("Generation Succeeded")
+ print('-'*80)
+ else:
+ error=f"Error. Unable to find 'params.txt' in current directory {os.getcwd}.\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+
+defparse_params_file(self,quiet=False):
+""" Reads params.txt file from DFNGen and parses information
+
+ Parameters
+ ---------
+ quiet : bool
+ If True details are not printed to screen, if False they area
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+ """
+ ifnotquiet:
+ print("\n--> Parsing params.txt")
+
+ fparams=open('params.txt','r')
+ # Line 1 is the number of polygons
+ self.num_frac=int(fparams.readline())
+ #Line 2 is the h scale
+ self.h=float(fparams.readline())
+ # Line 3 is the visualization mode: '1' is True, '0' is False.
+ self.visual_mode=int(fparams.readline())
+ # line 4 dudded points
+ self.dudded_points=int(fparams.readline())
+
+ # Dict domain contains the length of the domain in x,y, and z
+ self.domain={'x':0,'y':0,'z':0}
+ #Line 5 is the x domain length
+ self.domain['x']=(float(fparams.readline()))
+
+ #Line 5 is the x domain length
+ self.domain['y']=(float(fparams.readline()))
+
+ #Line 5 is the x domain length
+ self.domain['z']=(float(fparams.readline()))
+ self.r_fram=self.params['rFram']['value']
+
+ fparams.close()
+
+ ifnotquiet:
+ print("--> Number of Fractures: %d"%self.num_frac)
+ print(f"--> h: {self.h:0.2e} m")
+ ifself.visual_mode>0:
+ self.visual_mode=True
+ print("--> Visual mode is on")
+ else:
+ self.visual_mode=False
+ print("--> Visual mode is off")
+
+ print(f"--> Expected Number of dudded points: {self.dudded_points}")
+ print(f"--> X Domain Size {self.domain['x']} m")
+ print(f"--> Y Domain Size {self.domain['y']} m")
+ print(f"--> Z Domain Size {self.domain['z']} m")
+
+ self.x_min=-0.5*self.domain['x']
+ self.x_max=0.5*self.domain['x']
+
+ self.y_min=-0.5*self.domain['y']
+ self.y_max=0.5*self.domain['y']
+
+ self.z_max=0.5*self.domain['z']
+ self.z_min=-0.5*self.domain['z']
+
+ print("--> Parsing params.txt complete\n")
+
+
+defgather_dfn_gen_output(self):
+""" Reads in information about fractures and add them to the DFN object. Information is taken from radii.dat, translations.dat, normal_vectors.dat, and surface_area_Final.dat files. Information for each fracture is stored in a dictionary created by create_fracture_dictionary() that includes the fracture id, radius, normal vector, center, family number, surface area, and if the fracture was removed due to being isolated
+
+ Parameters
+ -----------
+ None
+
+ Returns
+ --------
+ fractures : list
+ List of fracture dictionaries with information.
+ Notes
+ ------
+ Both fractures in the final network and those removed due to being isolated are included in the list.
+
+ """
+ print("--> Parsing dfnWorks output and adding to object")
+ self.parse_params_file(quiet=False)
+
+ ## load radii
+ data=np.genfromtxt('dfnGen_output/radii_Final.dat',skip_header=2)
+ ## populate radius array
+ self.radii=np.zeros((self.num_frac,3))
+ # First Column is x, second is y, 3rd is max
+ ifself.num_frac==1:
+ data=np.array([data])
+
+ self.radii[:,:2]=data[:,:2]
+ foriinrange(self.num_frac):
+ self.radii[i,2]=max(self.radii[i,0],self.radii[i,1])
+
+ # gather fracture families
+ self.families=data[:,2].astype(int)
+
+ ## load surface area
+ self.surface_area=np.genfromtxt('dfnGen_output/surface_area_Final.dat',skip_header=1)
+ ## load normal vectors
+ self.normal_vectors=np.genfromtxt('dfnGen_output/normal_vectors.dat')
+ # Get fracture centers
+ centers=[]
+ withopen('dfnGen_output/translations.dat',"r")asfp:
+ fp.readline()# header
+ fori,lineinenumerate(fp.readlines()):
+ if"R"notinline:
+ line=line.split()
+ centers.append(
+ [float(line[0]),
+ float(line[1]),
+ float(line[2])])
+ self.centers=np.array(centers)
+
+ # Grab Polygon information
+ self.poly_info=np.genfromtxt('poly_info.dat')
+
+ # write polygon information to class
+ ifself.store_polygon_data==True:
+ self.grab_polygon_data()
+
+ ## create holder arrays for b, k, and T
+ self.aperture=np.zeros(self.num_frac)
+ self.perm=np.zeros(self.num_frac)
+ self.transmissivity=np.zeros(self.num_frac)
+
+ # gather indexes for fracture families
+ self.family=[]
+ ## get number of families
+ self.num_families=int(max(self.families))
+ foriinrange(1,self.num_families+1):
+ idx=np.where(self.families==i)
+ self.family.append(idx)
+
+ # get fracture_info
+ self.fracture_info=np.genfromtxt('dfnGen_output/fracture_info.dat',skip_header=1)
+
+ # get intersection_list
+ self.intersection_list=np.genfromtxt('dfnGen_output/intersection_list.dat',skip_header=1)
+
+ # get boundary_files
+ self.back=read_boundaries('dfnGen_output/back.dat')
+ self.front=read_boundaries('dfnGen_output/front.dat')
+ self.left=read_boundaries('dfnGen_output/left.dat')
+ self.right=read_boundaries('dfnGen_output/right.dat')
+ self.top=read_boundaries('dfnGen_output/top.dat')
+ self.bottom=read_boundaries('dfnGen/bottom.dat')
+
+defread_boundaries(file_path):
+'''Reads in boundary files, and corrects format is file is empty of length 1
+ Parameters
+ -----------
+ file_path : the path to boundary file
+
+ Returns
+ --------
+ array of values (or empty array if file is empty
+
+ Notes
+ ------
+ None
+ '''
+
+ ifos.path.isfile(file_path)andos.path.getsize(file_path)>0:
+ data=np.genfromtxt(file_path)
+ else:
+ data=np.array([])
+
+ try:
+ array_length=len(data)
+ except:
+ data=np.array([data])
+
+ returndata
+
+
+defassign_hydraulic_properties(self):
+'''Assigns hydraulic properties for each familiy and user defined fractures
+
+ Parameters
+ -----------
+ self : DFN object
+
+ Returns
+ --------
+ None
+
+ Notes
+ ------
+ None
+ '''
+
+ print("--> Assign hydraulic properties: Starting ")
+ ### Assign variables for fracture families
+ print("--> Assign hydraulic properties to fracture families : Starting ")
+ foriinrange(self.params['nFracFam']['value']):
+ hy_variable=self.fracture_families[i]['hydraulic_properties'][
+ 'variable']['value']
+ hy_function=self.fracture_families[i]['hydraulic_properties'][
+ 'function']['value']
+ hy_params=self.fracture_families[i]['hydraulic_properties'][
+ 'params']['value']
+
+ ifhy_variableisnotNone:
+ self.generate_hydraulic_values(hy_variable,
+ hy_function,
+ hy_params,
+ family_id=i+1)
+ print("--> Assign hydraulic properties to fracture families : Complete ")
+
+ ### Assign variables for user defined fractures and skip rejected fractures
+ ##Logic here, loop through user defined fractures
+ ## first check flag to insert
+ file_path='dfnGen_output/userFractureRejections.dat'
+ ifos.path.isfile(file_path)andos.path.getsize(file_path)>0:
+ try:
+ reject_fracs,frac_type=np.genfromtxt(file_path,delimiter=',',skip_header=1,unpack=True)
+ reject_fracs=np.array([reject_fracs])#needed for case with one rejected fracture, genfromtxt reads in float otherwise
+ frac_type=np.array([frac_type])
+ except:
+ print('--> No Rejected User Fractures, Ignore Following Warning.')
+ reject_fracs=np.array([])
+ frac_type=np.array([])
+ else:#if no fractures are rejected
+ print('--> No Rejected User Fractures, Ignore Following Warning.')
+ reject_fracs=np.array([])
+ frac_type=np.array([])
+
+ rect_reject=reject_fracs[(frac_type==-2)]#integer corresponds to fracture type
+ ell_reject=reject_fracs[(frac_type==-1)]
+ poly_reject=reject_fracs[(frac_type==-3)]
+
+ fracture_num=1#keep track of overall fracture number, and numbers for different types of fractures
+ frac_ell_num=1
+ frac_rect_num=1
+ frac_poly_num=1
+
+ ifself.params['insertUserRectanglesFirst']['value']==0:
+ print('--> Inserting User Ellipse Hydraulic Params First')
+ foriinrange(len(self.user_ell_params)):
+ forjinrange(self.user_ell_params[i]['nPolygons']):
+ iffrac_ell_numnotinell_reject:
+ print(f'--> Inserting User Ell Hydraulic Params {fracture_num}')
+ hy_prop_type=self.user_ell_params[i]['hy_prop_type']
+ value=self.user_ell_params[i][hy_prop_type][j]
+ print(f'{hy_prop_type} = {value}')
+ self.set_fracture_hydraulic_values(hy_prop_type,[fracture_num],
+ [value])
+ fracture_num+=1
+
+ frac_ell_num+=1
+
+ foriinrange(len(self.user_rect_params)):
+ forjinrange(self.user_rect_params[i]['nPolygons']):
+ iffrac_rect_numnotinrect_reject:
+ print(f'--> Inserting User Rect Hydraulic Params {fracture_num}')
+ hy_prop_type=self.user_rect_params[i]['hy_prop_type']
+ value=self.user_rect_params[i][hy_prop_type][j]
+ print(f'{hy_prop_type} = {value}')
+ self.set_fracture_hydraulic_values(hy_prop_type,[fracture_num],
+ [value])
+ fracture_num+=1
+
+ frac_rect_num+=1
+
+ foriinrange(len(self.user_poly_params)):
+ forjinrange(self.user_poly_params[i]['nPolygons']):
+ iffrac_poly_numnotinpoly_reject:
+ print(f'--> Inserting User Poly Hydraulic Params {fracture_num}')
+ hy_prop_type=self.user_poly_params[i]['hy_prop_type']
+ value=self.user_poly_params[i][hy_prop_type][j]
+ print(f'{hy_prop_type} = {value}')
+ self.set_fracture_hydraulic_values(hy_prop_type,[fracture_num],
+ [value])
+ fracture_num+=1
+
+ frac_poly_num+=1
+
+ else:
+ print('--> Inserting User Rectangle Hydraulic Params First')
+ foriinrange(len(self.user_rect_params)):
+ forjinrange(self.user_rect_params[i]['nPolygons']):
+ iffrac_rect_numnotinrect_reject:
+ print(f'--> Inserting User Rect Hydraulic Params {fracture_num}')
+ hy_prop_type=self.user_rect_params[i]['hy_prop_type']
+ value=self.user_rect_params[i][hy_prop_type][j]
+ print(f'{hy_prop_type} = {value}')
+ self.set_fracture_hydraulic_values(hy_prop_type,[fracture_num],
+ [value])
+ fracture_num+=1
+
+ frac_rect_num+=1
+
+ foriinrange(len(self.user_ell_params)):
+ forjinrange(self.user_ell_params[i]['nPolygons']):
+ iffrac_ell_numnotinell_reject:
+ print(f'--> Inserting User Ell Hydraulic Params {fracture_num}')
+ hy_prop_type=self.user_ell_params[i]['hy_prop_type']
+ value=self.user_ell_params[i][hy_prop_type][j]
+ print(f'{hy_prop_type} = {value}')
+
+ self.set_fracture_hydraulic_values(hy_prop_type,[fracture_num],
+ [value])
+ fracture_num+=1
+
+ frac_ell_num+=1
+
+ foriinrange(len(self.user_poly_params)):
+ forjinrange(self.user_poly_params[i]['nPolygons']):
+ iffrac_poly_numnotinpoly_reject:
+ print(f'--> Inserting User Poly Hydraulic Params {fracture_num}')
+ hy_prop_type=self.user_poly_params[i]['hy_prop_type']
+ value=self.user_poly_params[i][hy_prop_type][j]
+ print(f'{hy_prop_type} = {value}')
+ self.set_fracture_hydraulic_values(hy_prop_type,[fracture_num],
+ [value])
+ fracture_num+=1
+
+ frac_poly_num+=1
+
+ # self.dump_hydraulic_values()
+ print("--> Assign hydraulic properties: Complete ")
+
+
+[docs]
+defgrab_polygon_data(self):
+'''If flag self.store_polygon_data is set to True, the information stored in polygon.dat is written to a dictionary self.polygons.
+ To access the points that define an individual polygon, call self.polygons[f'poly{i}'] where i is a number between 1 and the number of defined polygons. This returns an array of coordinates in the format np.array([x1,y1,z1],[x2,y2,z2],...[xn,yn,zn])
+
+ Parameters
+ -----------
+ self : DFN object
+
+ Returns
+ --------
+ None
+
+ Notes
+ ------
+ None
+ '''
+
+ print("--> Loading Polygon information onto DFN object")
+ self.polygons={}
+
+ polygon_data=np.genfromtxt('dfnGen_output/polygons.dat',dtype=str,delimiter='dummy',skip_header=1)#weird format, so read data in as strings
+
+ ifself.num_frac==1:
+ polygon_data=np.array([polygon_data])
+
+
+ foriinrange(len(polygon_data)):
+ poly_dat=polygon_data[i]#read in data for one polygon
+ poly_dat=poly_dat.replace('}','')#get rid of weird characters
+ poly_dat=poly_dat.replace('{','')
+ poly_dat=poly_dat.replace(',','')
+ poly_dat=poly_dat.split()#convert string to list, and then array
+ poly_dat=np.array(poly_dat)
+ poly_dat=poly_dat.astype(float)
+ poly=[]
+ forjinrange(int(poly_dat[0])):#loop through and reformat individual coordinates
+ poly.append(poly_dat[3*j+1:3*j+4])
+ poly=np.array(poly)
+ self.polygons[f'fracture-{i+1}']=poly#store in dictionary
+ print('--> Data from polygons.dat stored on class in self.polygons\n')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/check_input.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/check_input.html
new file mode 100644
index 000000000..14fcd21bb
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/check_input.html
@@ -0,0 +1,229 @@
+
+
+
+
+
+ pydfnworks.dfnGen.generation.input_checking.check_input — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[docs]
+defprint_domain_parameters(self,print_all=False):
+""" Prints domain parameters to screen
+ Parameters
+ ------------
+ self : DFN Class Object
+ print_all : bool
+ If True, all parameters will be printed to screen, even those without a value. If False (default), only those with a value will be printed to screen.
+
+ Returns
+ ---------
+ None
+
+ """
+ print()
+ print('='*80)
+ print("--> dfnGen input parameters")
+ print('='*80)
+ print()
+ print("{:40s}{:}".format("Name","Value"))
+ print("{:40s}{:}".format("----------------------------",
+ "---------------"))
+ #print('-' * 60)
+ forkeyinself.params.keys():
+ value=self.params[key]['value']
+ ifprint_all:
+ print(f"{key:34s}{value}")
+ else:
+ ifvalue:
+ print(f"Name: {key:34s}Value: {value}")
+ print('='*80)
+
+
+
+
+[docs]
+defcheck_input(self,from_file=False):
+""" Checks input file for DFNGen to make sure all necessary parameters are defined. Then writes out a "clean" version of the input file
+
+ Input Format Requirements:
+ * Each parameter must be defined on its own line (separate by newline)
+ * A parameter (key) MUST be separated from its value by a colon ':' (ie. --> key: value)
+ * Values may also be placed on lines after the 'key'
+ * Comment Format: On a line containing // or / ``*``, nothing after ``*`` / or // will be processed but text before a comment will be processed
+
+ Parameters
+ ------------
+ self : DFN Class Object
+
+ Returns
+ ---------
+ None
+
+ Notes
+ -----
+ There are warnings and errors raised in this function. Warning will let you continue while errors will stop the run. Continue past warnings are your own risk.
+
+ From File feature is no longer maintained. Functions should be removed in the near future.
+ """
+ print()
+ print('='*80)
+ print("Checking Input File\n")
+ ## Needs to be a logic fork here for using input file
+ from_file=from_file#added call to function creat_dfn to set flag, default is false
+ iffrom_file:
+ # Copy input file
+ ifos.path.isfile(self.dfnGen_file):
+ try:
+ print(f"--> Copying input file: {self.dfnGen_file}")
+ shutil.copy(self.dfnGen_file,self.jobname)
+ print("--> Copying input file successful")
+ except:
+ error=f"Unable to copy dfnGen input file to working directory \n{self.dfnGen_file}\n Exiting"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ error=f"Input file \n{self.dfnGen_file} not found\n Exiting"
+ sys.stderr.write(error)
+ sys.exit(1)
+ input_file=self.local_dfnGen_file
+ output_file="dfnGen_output/"+self.local_dfnGen_file[:-4]+'_clean.dat'
+ print(f"--> Reading input file: {input_file}")
+ self.params=parse_input(input_file)
+
+ else:
+ output_file="dfnGen_output/"+self.local_dfnGen_file[:-4]+'_clean.dat'
+ self.params=self.write_fracture_families()
+ self.write_user_fractures_to_file()
+ print(f"--> Clean output file name: {output_file}")
+ verify_params(self.params)
+ dump_params(self.params,output_file)
+ print("\nChecking Input File Complete")
+ print('='*80)
+ print()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/fracture_family.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/fracture_family.html
new file mode 100644
index 000000000..e549be22d
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/fracture_family.html
@@ -0,0 +1,586 @@
+
+
+
+
+
+ pydfnworks.dfnGen.generation.input_checking.fracture_family — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.generation.input_checking.fracture_family
+importsys
+
+
+deffracture_family_dictionary():
+"""Creates a fracture family dictionary
+
+ Parameters
+ --------------
+ None
+
+ Returns
+ --------
+ family : dictionary
+ fracture family dictionary for specified family
+
+ Notes
+ ---------
+ See https://dfnworks.lanl.gov/dfngen.html#domain-parameters for more
+ information about parameters in this dictionary
+ """
+
+ family={
+ 'number':{
+ 'type':int,
+ 'value':None,
+ 'description':'ID number for the fracture family'
+ },
+ 'probability':{
+ 'type':
+ float,
+ 'value':
+ None,
+ 'description':
+ 'Probabiliy of occurence for the family of of stochastically generated fractures'
+ },
+ 'type':{
+ 'type':
+ bool,
+ 'value':{
+ 'rect':False,
+ 'ellipse':False
+ },
+ 'description':
+ 'Specifies whether the fracture family consists of rectangular or elliptical fractures'
+ },
+ 'layer':{
+ 'type':int,
+ 'value':0,
+ 'description':'Assign family to a layer in the domain'
+ },
+ 'region':{
+ 'type':int,
+ 'value':0,
+ 'description':'Assign family to a region in the domain'
+ },
+ 'p32':{
+ 'type':float,
+ 'value':None,
+ 'description':'Target fracture intensity'
+ },
+ 'aspect':{
+ 'type':float,
+ 'value':1,
+ 'description':'Aspect ratio of the fractures'
+ },
+ 'number_of_points':{
+ 'type':
+ int,
+ 'value':
+ None,
+ 'description':
+ 'Number of vertices defining the boundary of each elliptical fracture'
+ },
+ 'beta_distribution':{
+ 'type':
+ bool,
+ 'value':
+ False,
+ 'description':
+ 'Prescribe a rotation around each fractures normal vector, with the fracture centered on the x-y plane at the origin\nFalse:Uniform distribution on [0,2pi)\nTrue:Constant rotation specified by beta'
+ },
+ 'beta':{
+ 'type':
+ float,
+ 'value':
+ None,
+ 'description':
+ 'Value for constant angle of rotation around the normal vector'
+ },
+ #fisher distribution
+ 'fisher':{
+ 'type':
+ float,
+ 'value':{
+ 'theta':None,
+ 'phi':None,
+ 'strike':None,
+ 'dip':None,
+ 'trend':None,
+ 'plunge':None,
+ 'kappa':None
+ },
+ 'description':
+ '3 parameters theta, phi, and kappa for fisher distribution'
+ },
+ 'distribution':{
+ 'type':bool,
+ 'value':{
+ 'tpl':False,
+ 'log_normal':False,
+ 'exp':False,
+ 'constant':False
+ },
+ 'description':
+ 'Type of distibution fracture radii are sampled from'
+ },
+ 'tpl':{
+ 'type':float,
+ 'value':{
+ 'alpha':None
+ },
+ 'description':'Parameter for truncated power-law distibution'
+ },
+ 'log_normal':{
+ 'type':float,
+ 'value':{
+ 'mean':None,
+ 'std':None
+ },
+ 'description':'Parameters for log normal distribution'
+ },
+ 'exp':{
+ 'type':float,
+ 'value':{
+ 'mean':None
+ },
+ 'description':'Parameter for exponential distribution'
+ },
+ 'constant':{
+ 'type':float,
+ 'value':None,
+ 'description':'Constant sized fracture family radius'
+ },
+ 'min_radius':{
+ 'type':float,
+ 'value':None,
+ 'description':'Minimum radius created by distribution'
+ },
+ 'max_radius':{
+ 'type':float,
+ 'value':None,
+ 'description':'Maximum radius created by distribution'
+ },
+ 'hydraulic_properties':{
+ 'variable':{
+ 'type':
+ str,
+ 'value':
+ None,
+ 'description':
+ ' Acceptable values are aperture, permeability, and transmissivity'
+ },
+ 'function':{
+ 'type':
+ str,
+ 'value':
+ None,
+ 'description':
+ 'Acceptable values or correlated, semi-correlated, constant, and log-normal'
+ },
+ 'params':{
+ 'type':
+ dict,
+ 'value':
+ None,
+ 'description':
+ 'if correlated {"alpha":float, "beta":float},\nif semi-correlated {"alpha":float, "beta":float, "sigma":float},\nif constant {"mu":float},\nif log-normal {"mu":float,"sigma":float}'
+ }
+ }
+ }
+ returnfamily
+
+
+
+[docs]
+defprint_family_information(self,family_number):
+"""Creates a fracture family dictionary
+
+ Parameters
+ --------------
+ self : DFN object
+
+ family_number : the id of the fracture family information to be returned
+
+ Returns
+ --------
+ Prints fracture family parameters to terminal screen
+
+ Notes
+ ---------
+ None
+ """
+
+ iflen(self.fracture_families)>0:
+ family=self.fracture_families[family_number-1]
+ print(f"--> Family information for family # {family_number}")
+ forkeyinfamily.keys():
+ ifkey=='hydraulic_properties':
+ forsub_keyinfamily[key].keys():
+ print(
+ f"Name: {key} : {sub_key} : {family[key][sub_key]['value']}"
+ )
+ else:
+ print(f"Name: {key:40s}Value: {family[key]['value']}")
+ print()
+ else:
+ print("No Defined Fracture Families")
+ print()
+
+
+
+
+[docs]
+defadd_fracture_family(self,
+ shape,
+ distribution,
+ kappa,
+ family_number=None,
+ probability=None,
+ p32=None,
+ layer=0,
+ region=0,
+ number_of_points=8,
+ aspect=1,
+ beta_distribution=0,
+ beta=0,
+ theta=None,
+ phi=None,
+ strike=None,
+ dip=None,
+ trend=None,
+ plunge=None,
+ alpha=None,
+ log_mean=None,
+ log_std=None,
+ exp_mean=None,
+ constant=None,
+ min_radius=None,
+ max_radius=None,
+ hy_variable=None,
+ hy_function=None,
+ hy_params=None):
+"""Generates a fracture family
+
+ Parameters
+ --------------
+ self : DFN object
+
+ shape : 'rect' or 'ell' deines the fracture family shape
+
+ distribution : 'tpl', 'log_normal', 'exp', or 'constant' defines the sample distribution for the fracture radius
+
+ kappa : concentration param of the von Mises-Fisher distribution
+
+ family_number : fracutre family id. default = None
+
+ probability : probabily of a fracture belonging to this family. default = None. use if stopCondition = 0
+
+ p32 : fracture intensity for the family. default = None. use if stopCondition = 1
+ layer : assigns fracture family to a layer in the domain. default = 0
+
+ region : assigns fracture family to a region in the domain. default = 0
+
+ number_of_points : specifies the number of vertices defining th eboundary of each fracture. default = 8
+
+ aspect : the aspect ratio of the fractures. default = 1
+
+ beta_distribution : 0 (uniform distribtuion [0,2pi) or 1 (constant rotation specfied by ebeta) rotation of each fractures normal vector. default 0
+
+ beta : angle fo constant rotation. use if beta_distribution = 1. default = 0
+
+ theta : use if orientationOption = 0 (default). default = None
+
+ phi : use if orientationOption = 0 (default). default = None
+
+ trend : use if orientationOption = 1. default = None
+
+ plunge : use if orientationOption = 1. default = None
+
+ dip : use if orientationOption = 2. default = None
+
+ strike : use if orientationOption = 2. default = None
+
+ alpha : parameter for 'tpl'. default = None
+
+ log_mean : parameter for 'log_normal'. default = None
+
+ log_std : parameter for 'log_normal'. default = None
+
+ exp_mean : parameter for 'exp'. default = None
+
+ constant : parameter for 'constant'. default = None
+
+ min_radius : minimum fracture radius for 'tpl' 'log_normal' or 'exp'. default = None
+
+ max_radius : maximum fracture radius for 'tpl' 'log_normal' or 'exp'. default = None
+
+ hy_variable : hydraulic variable to assign values to. options are 'aperture', 'permeability', 'transmissivity',
+
+ hy_function : relationship between hydraulic variable and fracture radius. options are 'correlated', 'semi-correlated', 'constant', 'log-normal'
+
+ hy_params : parameters for the hydraulic function. see next lines for syntax and options
+ if 'correlated' --> {"alpha":value, "beta:value}
+ if 'semi-correlated' --> {"alpha":value, "beta":value, "sigma":value}
+ if 'constant' --> {"mu":value}
+ if 'log-normal' --> {"mu":value, "sigma":value}
+
+ Returns
+ --------
+ Populated fracture family dictionary for specified family
+
+ Notes
+ ---------
+ See https://dfnworks.lanl.gov/dfngen.html#domain-parameters for more
+ information about parameters
+ """
+
+ print("--> Adding new facture family")
+
+ family=fracture_family_dictionary()
+ ifshape=="rect":
+ family['type']['value']['rect']=True
+ family['number_of_points']['value']=4
+ elifshape=="ell":
+ family['type']['value']['ellipse']=True
+ family['number_of_points']['value']=number_of_points
+ else:
+ error=f"Unknown Fracture Type {shape}. Acceptable values are rect & ell. Exiting.\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ family['layer']['value']=layer
+ family['region']['value']=region
+
+ ifp32:
+ family['p32']['value']=p32
+ family['probability']['value']=p32
+ elifprobability:
+ family['probability']['value']=probability
+ else:
+ error=f"A value for p32 or probability must be provided. Exiting.\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ family['aspect']['value']=aspect
+
+ ## Orienation
+ family['beta_distribution']['value']=beta_distribution
+ family['beta']['value']=beta
+ family['fisher']['value']['theta']=theta
+ family['fisher']['value']['phi']=phi
+ family['fisher']['value']['strike']=strike
+ family['fisher']['value']['dip']=dip
+ family['fisher']['value']['trend']=trend
+ family['fisher']['value']['plunge']=plunge
+ family['fisher']['value']['kappa']=kappa
+
+ ## Set and check orientation option
+ # note orientationOption = 0 --> theta/phi
+ # orientationOption = 1 --> trend/plunge
+ # orientationOption = 2 --> stirke/dip
+ iftheta!=Noneandphi!=None:
+ ifself.params['orientationOption']['value']==None:
+ print('Setting orientationOption = 0 (theta/phi)')
+ self.params['orientationOption']['value']=0
+ ifself.params['orientationOption']['value']!=0:
+ error=f"0Each family must have only one of the pairs of parameters strike/dip, theta/phi or trend/plunge defined. Each family must have the same pair of parameters defined. \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ iftrend!=Noneandplunge!=None:
+ ifself.params['orientationOption']['value']==None:
+ print('Setting orientationOption = 1 (trend/plunge)')
+ self.params['orientationOption']['value']=1
+ ifself.params['orientationOption']['value']!=1:
+ error=f"1Each family must have only one of the pairs of parameters strike/dip, theta/phi or trend/plunge defined. Each family must have the same pair of parameters defined. \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ifstrike!=Noneanddip!=None:
+ ifself.params['orientationOption']['value']==None:
+ print('Setting orientationOption = 2 (strike/dip)')
+ self.params['orientationOption']['value']=2
+ ifself.params['orientationOption']['value']!=2:
+ error=f"2Each family must have only one of the pairs of parameters strike/dip, theta/phi or trend/plunge defined. Each family must have the same pair of parameters defined. \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ## Radius Distribution
+ ifdistribution=="tpl":
+ family['distribution']['value']['tpl']=True
+ ifalpha!=None:
+ family['tpl']['value']['alpha']=alpha
+ else:
+ error=f"Error. A value for alpha must be provided if family is tpl distribution. Exiting.\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ elifdistribution=="log_normal":
+ family['distribution']['value']['log_normal']=True
+ iflog_mean!=None:
+ family['log_normal']['value']['mean']=log_mean
+ else:
+ error=f"Error. A value for log_mean must be provided if family is log_normal distribution. Exiting. \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ iflog_std!=None:
+ family['log_normal']['value']['std']=log_std
+ else:
+ error=f"Error. A value for log_std must be provided if family is log_normal distribution. Exiting. \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ elifdistribution=="exp":
+ family['distribution']['value']['exp']=True
+ ifexp_mean!=None:
+ family['exp']['value']['mean']=exp_mean
+ else:
+ error=f"Error. A value for exp_mean must be provided if family is exp distribution. Exiting. \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ elifdistribution=="constant":
+ family['distribution']['value']['constant']=True
+ ifconstant!=None:
+ family['constant']['value']=constant
+ else:
+ error=f"Error. A value for constant must be provided if family is constant distribution. Exiting. \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ error=f"Error. Unknown Fracture Distribution {distribution}. Acceptable values are 'tpl', 'exp', 'log_normal', & 'constant'. Exiting.\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ifdistribution!="constant":
+ ifnotmin_radiusornotmax_radius:
+ error=f"Error. Minimum and Maximum radius must be provided unless using constant distribution. Exiting.\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ family['min_radius']['value']=min_radius
+ family['max_radius']['value']=max_radius
+
+ iffamily_number:
+ family['number']['value']=family_number
+ else:
+ family_number=len(self.fracture_families)+1
+
+ family['hydraulic_properties']['variable']['value']=hy_variable
+ family['hydraulic_properties']['function']['value']=hy_function
+ family['hydraulic_properties']['params']['value']=hy_params
+ ##Do we need exceptions? it will be checked in dfnflow
+
+ self.fracture_families.append(family)
+ self.print_family_information(family_number)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/user_defined_fracture_functions.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/user_defined_fracture_functions.html
new file mode 100644
index 000000000..8d431d8b2
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/input_checking/user_defined_fracture_functions.html
@@ -0,0 +1,747 @@
+
+
+
+
+
+ pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions
+importsys
+importos
+fromnumpyimportpi
+
+frompydfnworks.dfnGen.generation.input_checking.helper_functionsimportprint_error,print_warning
+
+defcheck_angle_option(angle_option,array):
+ forvalinarray:
+ ifangle_option=="radian":
+ ifval>2*pi:
+ print_warning(
+ "Value greater than 2 PI, angle option of radians has been selected"
+ )
+ elifangle_option=="degree":
+ ifval>360:
+ print_warning(
+ "Value greater than 2 PI, angle option of radians has been selected"
+ )
+
+
+
+[docs]
+defprint_user_fracture_information(self,shape,frac_number=None):
+""" Prints information about a user defined fracture to screen
+
+ Parameters
+ ----------------
+ self : DFN object
+
+ shape: string
+ The shape of the fracture options are 'rect', 'ell', and 'poly' - Required
+
+ fracture_number : int
+ Index of fracture. If none (default), then information about all user fractures of input shape are printed to screen
+
+ Returns
+ ---------------
+ None
+
+ Notes
+ --------------
+ None
+ """
+ print(f"\n--> User Fracture information")
+ ifshape=='rect':
+ iffrac_number:
+ fracture_dictionary=self.user_rect_params[frac_number]
+ else:
+ fracture_dictionaries=self.user_rect_params
+
+ elifshape=='ell':
+ iffrac_number:
+ fracture_dictionary=self.user_ell_params[frac_number]
+ else:
+ fracture_dictionaries=self.user_ell_params
+
+ elifshape=='poly':
+ fracture_dictionaries=self.user_poly_params
+
+ iffrac_number:
+ print(f"* Fracture Number {frac_number} *")
+ print("{:40s}{:}".format("Name","Value"))
+ print("{:40s}{:}".format("----------------------------",
+ "---------------"))
+ forkeyinfracture_dictionary.keys():
+ print(f"{key:40s}{fracture_dictionary[key]}")
+ else:
+ fori,fracture_dictionaryinenumerate(fracture_dictionaries):
+ print(f"* Fracture Number {i+1} *")
+ print("{:40s}{:}".format("Name","Value"))
+ print("{:40s}{:}".format("----------------------------",
+ "---------------"))
+ forkeyinfracture_dictionary.keys():
+ print(f"{key:40s}{fracture_dictionary[key]}")
+ print("\n")
+
+
+
+
+[docs]
+defadd_user_fract_from_file(self,
+ filename,
+ shape,
+ nPolygons,
+ by_coord=False,
+ aperture=None,
+ transmissivity=None,
+ permeability=None):
+""" Sets up paths for fractures defined in user input file. When inserting user fractures from file, hydraulic properties must be provided as a list of length nPolygons (number of fractures defined in the file)
+
+ Parameters
+ ----------------
+ filename : string
+ path to source file
+
+ shape: string
+ The shape of the fracture options are 'rect', 'ell', and 'poly' - Required
+
+ by_coord : boolean
+ True / False of file format for coordinate or general input
+
+ nPolygons : int
+ The number of polygons specified in the file
+
+ permeability : list or array
+ Permeabilities of the fractures
+
+ transmissivity : list or array
+ Fracture Tramsmissivities
+
+ aperture : list or array
+ Hydraulic apertures of the fracture
+
+ Returns
+ ---------------
+ None
+
+ Notes
+ --------------
+ Does not write the file, only sets up paths
+ ~/src/dfnworks-aidan/pydfnworks/pydfnworks/
+ """
+ fracture_dictionary={"shape":shape,"filename":filename}
+
+ hy_prop_type=determine_hy_prop_type(aperture,transmissivity,
+ permeability)
+ fracture_dictionary['aperture']=aperture
+ fracture_dictionary['transmissivity']=transmissivity
+ fracture_dictionary['permeability']=permeability
+ fracture_dictionary['hy_prop_type']=hy_prop_type
+ fracture_dictionary['nPolygons']=nPolygons
+
+
+ ifapertureisnotNone:
+ iflen(aperture)!=nPolygons:
+ print_error("Error. aperture list for user fractures from file is not the same length as nPolygons, please check input for add_user_fract_from_file\n")
+ iftransmissivityisnotNone:
+ iflen(transmissivity)!=nPolygons:
+ print_error("Error. transmissivity list for user fractures from file is not the same length as nPolygons, please check input for add_user_fract_from_file\n")
+ ifpermeabilityisnotNone:
+ iflen(permeability)!=nPolygons:
+ print_error("Error. aperture list for user fractures from file is not the same length as nPolygons, please check input for add_user_fract_from_file\n")
+
+
+ ifshape=='rect':
+ self.params['RectByCoord_Input_File_Path']['value']=filename
+ ifby_coord:
+ self.params['userRectByCoord']['value']=True
+ else:
+ self.params['userRectanglesOnOff']['value']=True
+ self.user_rect_params.append(fracture_dictionary)
+ frac_number=len(self.user_rect_params)
+ self.print_user_fracture_information('rect',frac_number-1)
+
+ elifshape=='ell':
+ self.params['EllByCoord_Input_File_Path']['value']=filename
+ ifby_coord:
+ self.params['userEllByCoord']['value']=True
+ else:
+ self.params['userEllipsesOnOff']['value']=True
+ self.user_ell_params.append(fracture_dictionary)
+ frac_number=len(self.user_ell_params)
+ self.print_user_fracture_information('ell',frac_number-1)
+ elifshape=='poly':
+ # user polygon
+ self.params['userPolygonByCoord']['value']=True
+ self.params['PolygonByCoord_Input_File_Path']['value']=filename
+ self.user_poly_params.append(fracture_dictionary)
+ self.print_user_fracture_information('poly')
+ else:
+ print_error(
+ "Error.user fracture shape is not specified correctly, options are 'rect', 'ell', or 'poly'\n"
+ )
+
+
+
+
+[docs]
+defadd_user_fract(self,
+ shape,
+ radii,
+ translation,
+ filename=None,
+ aspect_ratio=1,
+ beta=0,
+ angle_option='degree',
+ orientation_option='normal',
+ normal_vector=None,
+ trend_plunge=None,
+ dip_strike=None,
+ number_of_vertices=None,
+ permeability=None,
+ transmissivity=None,
+ aperture=None):
+"""
+ Specifies user defined fracture parameters for the DFN.
+
+ Parameters
+ -------------
+ shape: string
+ The desired shape of the fracture options are 'rect', 'ell', and 'poly' - Required
+
+ radii : float
+ 1/2 size of the fracture in meters - Required
+
+ translation : list of floats [3]
+ Fracture center
+
+ filename: string
+ The name of the user defined fracture file. Default is user_defined_{shape}.dat
+
+ aspect_ratio : float
+ Fracture aspect ratio
+
+ beta : float
+ Rotation angle around center of the fracture
+
+ angle_option : string
+ Angle option 'degree' or 'radian'. Default is degree
+
+ orientation_option : string
+ Choice of fracture orienation 'normal', 'trend_plunge', 'dip_strike'
+
+ normal_vector : list [3]
+ normal vector of the fracture
+
+ trend_plunge : list [2]
+ trend and plunge of the fracture
+
+ dip_strike : list [2]
+ dip and strike of the fracture
+
+ number_of_vertices : int
+ Number of vertices on the fracture boundary.
+
+ permeability : float
+ Permeability of the fracture
+
+ transmissivity : float
+ Fracture Tramsmissivity
+
+ aperture : float
+ Hydraulic aperture of the fracture
+
+ Returns
+ ---------
+ None - fracture dictionaries are attached to the DFN object
+
+ Notes
+ -------
+ Please be aware, the user fracture files can only be automatically written for
+ ellipses and rectangles not specified by coordinate.
+
+ See
+
+ https://dfnworks.lanl.gov/dfngen.html#user-defined-fracture-generation-parameters
+
+ for additional information
+
+ """
+
+ # if specifying details in the python driver file.
+ fracture_dictionary={"shape":shape}
+ fracture_dictionary['nPolygons']=1
+ # Check input parameters
+ iffilename:
+ fracture_dictionary['filename']=filename
+ else:
+ filename=self.jobname+f"/dfnGen_output/user_defined_{shape}.dat"
+ fracture_dictionary['filename']=filename
+
+ # Check radius is positive.
+ ifradii>0:
+ fracture_dictionary['Radii:']=radii
+ else:
+ print_error(
+ f"Error. Fracture radius must be positive. Value provided {radii}. Exiting."
+ )
+
+ # Check Aspect Ratio is positive
+ ifaspect_ratio>0:
+ fracture_dictionary['Aspect_Ratio:']=aspect_ratio
+ else:
+ print_error(
+ f"Error. Aspect Ratio must be positive. Value provided {aspect_ratio}. Exiting."
+ )
+
+ ## check beta Rotation in non-negative.
+ ifbeta>=0:
+ fracture_dictionary['Beta:']=beta
+ else:
+ print_error(
+ f"Error. Beta rotation must be non-negative (>0). Value provided {beta}. Exiting."
+ )
+
+ # Check Angle options
+ angle_options=['radian','degree']
+ ifangle_optioninangle_options:
+ fracture_dictionary['AngleOption:']=angle_option
+ else:
+ print_error(
+ f"Error. Unknown angle_option value provided: {angle_option}. Acceptable values are 'radian', 'degree'.\nExiting."
+ )
+
+ iflen(translation)==3:
+ fracture_dictionary['Translation:']=translation
+ else:
+ print_error(
+ f"Error. Fracture Translation (center) must have 3 elements, only {len(translation)} provided.\nValue provided: {translation}. Exiting"
+ )
+
+ ## Check orienations and consistency
+ iforientation_option=='normal':
+ fracture_dictionary['userOrientationOption:']=0
+ ifnormal_vector:
+ fracture_dictionary['Normal:']=normal_vector
+ else:
+ print_error(
+ "Error. Requested user fracture orienation 0, but normal vector was not provided. exiting."
+ )
+ iflen(normal_vector)!=3:
+ print_error(
+ f"Error. Normal vector must have 3 elements, only {len(normal_vector)} provided.\nNormal: {normal_vector}. Exiting"
+ )
+
+ eliforientation_option=='trend_plunge':
+ fracture_dictionary['userOrientationOption:']=1
+ iftrend_plunge:
+ fracture_dictionary['Trend_Plunge:']=trend_plunge
+ else:
+ print_error(
+ "Error. Requested user fracture orienation trend_plunge, but trend_plunge was not provided. exiting."
+ )
+
+ iflen(trend_plunge)!=2:
+ print_error(
+ f"Error. Trend/Plunge must have 2 elements, only {len(trend_plunge)} provided.\trend_plunge: {trend_plunge}. Exiting"
+ )
+
+ # Check is angles make sense given radians or degrees
+ print("--> Checking trend_plunge angles")
+ check_angle_option(angle_option,trend_plunge)
+
+ eliforientation_option=='dip_strike':
+ fracture_dictionary['userOrientationOption:']=2
+ ifdip_strike:
+ fracture_dictionary['Dip_Strike:']=dip_strike
+ else:
+ print_error(
+ "Error. Requested user fracture orienation dip_strike, but dip_strike was not provided. exiting."
+ )
+ iflen(dip_strike)!=2:
+ print_error(
+ f"Error. Dip/Strike must have 2 elements, only {len(dip_strike)} provided.\trend_plunge: {dip_strike}. Exiting"
+ )
+ else:
+ print_error(
+ f"Error. Unknown orientation_option provided. Value: {orientation_option}. Options are 'normal', 'trend_plunge', and 'dip_strike'. Exiting"
+ )
+
+ # Check is angles make sense given radians or degrees
+ print("--> Checking dip_strike angles")
+ check_angle_option(angle_option,dip_strike)
+
+ # hydraulic properties
+ hy_prop_type=determine_hy_prop_type(aperture,transmissivity,
+ permeability)
+ fracture_dictionary['aperture']=[aperture]
+ fracture_dictionary['transmissivity']=[transmissivity]
+ fracture_dictionary['permeability']=[permeability]
+ fracture_dictionary['hy_prop_type']=hy_prop_type
+
+ ## Logic for i/o
+ ifshape=='rect':
+ self.params['userRectanglesOnOff']['value']=True
+ self.params['UserRect_Input_File_Path']['value']=fracture_dictionary[
+ 'filename']
+ self.user_rect_params.append(fracture_dictionary)
+ frac_number=len(self.user_rect_params)
+ self.print_user_fracture_information('rect',frac_number-1)
+
+ elifshape=='ell':
+ ifnumber_of_vertices>2:
+ fracture_dictionary['Number_of_Vertices:']=number_of_vertices
+ else:
+ print_error(
+ f"Error. number_of_vertices must be greater than 2. VAlue provided: {number_of_vertices}. Exiting."
+ )
+
+ self.params['userEllipsesOnOff']['value']=True
+ self.params['UserEll_Input_File_Path']['value']=fracture_dictionary[
+ 'filename']
+
+ self.user_ell_params.append(fracture_dictionary)
+ frac_number=len(self.user_ell_params)
+ self.print_user_fracture_information('ell',frac_number-1)
+
+
+
+defwrite_user_fractures_to_file(self):
+"""Writes the user defined fracutres to a file if file is not already specified
+
+ Parameters
+ ------------
+ self : DFN object
+
+ Returns
+ ---------
+ None
+
+ Notes
+ -------
+ None
+ """
+
+ n_rects=len(self.user_rect_params)
+ n_ells=len(self.user_ell_params)
+
+ ifn_ells>0:
+ print(
+ f"--> Writing user defined ellispes to file {self.params['UserEll_Input_File_Path']['value']}"
+ )
+ withopen(self.params['UserEll_Input_File_Path']['value'],
+ 'w+')asell_file:
+ ell_file.write(f'nUserEll: {n_ells}\n\n')
+ orientation_option=self.user_ell_params[0][
+ 'userOrientationOption:']
+ forkeyinself.user_ell_params[0].keys():
+ ifkey=='userOrientationOption:':
+ value=self.user_ell_params[0][key]
+ ell_file.write(f'{key}{value}\n\n')
+ elifkey=='Normal:':
+ iforientation_option==0:
+ ell_file.write(f'{key}\n')
+ forjinrange(n_ells):
+ value=self.user_ell_params[j][key]
+ ifvalueisnotNone:
+ ell_file.write(f'{value}\n')
+ else:
+ error="user orientation option not specified correctly \n0:'normal'\n1:'trend_plunge'\n2:'dip_strike'\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ell_file.write('\n')
+
+ else:
+ continue
+
+ elifkey=='Trend_Plunge:':
+
+ iforientation_option==1:
+ ell_file.write(f'{key}\n')
+ forjinrange(n_ells):
+ value=self.user_ell_params[j][key]
+ ifvalueisnotNone:
+ ell_file.write(f'{value}\n')
+ else:
+ error="user orientation option not specified correctly \n0:'normal'\n1:'trend_plunge'\n2:'dip_strike'\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ell_file.write('\n')
+
+ else:
+ continue
+
+ elifkey=='Dip_Strike:':
+
+ iforientation_option==2:
+ ell_file.write(f'{key}\n')
+ forjinrange(n_ells):
+ value=self.user_ell_params[j][key]
+ ifvalueisnotNone:
+ ell_file.write(f'{value}\n')
+ else:
+ error="user orientation option not specified correctly \n0:'normal'\n1:'trend_plunge'\n2:'dip_strike'\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ell_file.write('\n')
+
+ else:
+ continue
+
+ elifkey=='filename'orkey=='shape':
+ continue
+ else:
+
+ ell_file.write(f'{key}\n')
+ forjinrange(n_ells):
+ value=self.user_ell_params[j][key]
+ ell_file.write(f'{value}\n')
+ ell_file.write('\n')
+
+ ifn_rects>0:
+
+ print(
+ f"--> Writing user defined rectangles to file {self.params['UserRect_Input_File_Path']['value']}"
+ )
+ withopen(self.params['UserRect_Input_File_Path']['value'],
+ 'w+')asrect_file:
+
+ rect_file.write(f'nUserRect: {n_rects}\n\n')
+
+ orientation_option=self.user_rect_params[0][
+ 'userOrientationOption:']
+
+ forkeyinself.user_rect_params[0].keys():
+
+ ifkey=='userOrientationOption:':
+
+ value=self.user_rect_params[0][key]
+ rect_file.write(f'{key}{value}\n\n')
+
+ elifkey=='Normal:':
+
+ iforientation_option==0:
+ rect_file.write(f'{key}\n')
+ forjinrange(n_rects):
+ value=self.user_rect_params[j][key]
+ ifvalueisnotNone:
+ rect_file.write(f'{value}\n')
+ else:
+ error="user orientation option not specified correctly \n0:'Normal'\n1:'Trend_Plunge'\n2:Dip_Strike'"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ rect_file.write('\n')
+
+ else:
+ continue
+
+ elifkey=='Trend_Plunge:':
+
+ iforientation_option==1:
+ rect_file.write(f'{key}\n')
+ forjinrange(n_rects):
+ value=self.user_rect_params[j][key]
+ ifvalueisnotNone:
+ rect_file.write(f'{value}\n')
+ else:
+ error="user orientation option not specified correctly \n0:'Normal'\n1:'Trend_Plunge'\n2:Dip_Strike'"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ rect_file.write('\n')
+
+ else:
+ continue
+
+ elifkey=='Dip_Strike:':
+
+ iforientation_option==2:
+ rect_file.write(f'{key}\n')
+ forjinrange(n_rects):
+ value=self.user_rect_params[j][key]
+ ifvalueisnotNone:
+ rect_file.write(f'{value}\n')
+ else:
+ error="user orientation option not specified correctly \n0:'Normal'\n1:'Trend_Plunge'\n2:Dip_Strike'"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ rect_file.write('\n')
+
+ else:
+ continue
+
+ elifkey=='filename':
+ continue
+ elifkey=='shape':
+ continue
+
+ else:
+
+ rect_file.write(f'{key}\n')
+ forjinrange(n_rects):
+ value=self.user_rect_params[j][key]
+ rect_file.write(f'{value}\n')
+ rect_file.write('\n')
+
+
+defdetermine_hy_prop_type(aperture,transmissivity,permeability):
+"""Determines the type of user defined hydraulic property based on user inupt
+
+ Parameters
+ -------------
+ aperture : None or Float
+ transmissivity : None or Float
+ permeability : None or Float
+
+ Returns
+ ---------
+ The hydraulic property type. Exactly one of the three parameters must be a float or an exception will be thrown
+ Notes
+ -------"""
+
+ #Determine Hydraulic Property type
+
+ hy_prop_type=None
+
+ ifapertureisnotNone:
+ hy_prop_type='aperture'
+
+ iftransmissivityisnotNone:
+ ifhy_prop_type!=None:
+ error="\nPlease specify exactly one of the following for user defined fracture: aperture, transmissivity, permeability\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ hy_prop_type='transmissivity'
+
+ ifpermeabilityisnotNone:
+ ifhy_prop_type!=None:
+ error="\nPlease specify exactly one of the following for user defined fracture: aperture, transmissivity, permeability\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ hy_prop_type='permeability'
+
+ ifhy_prop_type==None:
+ error="\nPlease specify exactly one of the following for user defined fracture: aperture, transmissivity, permeability\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ returnhy_prop_type
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/output_report/gen_output.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/output_report/gen_output.html
new file mode 100644
index 000000000..970168f55
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/output_report/gen_output.html
@@ -0,0 +1,245 @@
+
+
+
+
+
+ pydfnworks.dfnGen.generation.output_report.gen_output — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.generation.output_report.gen_output
+"""
+ :filename: gen_output.py
+ :synopsis: Main driver for dfnGen output report
+ :version: 1.0
+ :maintainer: Jeffrey Hyman
+ :moduleauthor: Jeffrey Hyman <jhyman@lanl.gov>
+"""
+
+importos
+importmatplotlib
+
+matplotlib.use("Agg")
+importmatplotlib.pylabasplt
+frommatplotlibimportrc
+
+rc('text',usetex=True)
+
+importpydfnworks.dfnGen.generation.generator
+frompydfnworks.dfnGen.generation.output_report.gather_informationimport*
+frompydfnworks.dfnGen.generation.output_report.plot_fracture_orientationsimportplot_fracture_orientations
+frompydfnworks.dfnGen.generation.output_report.plot_fracture_radiiimportplot_fracture_radii
+frompydfnworks.dfnGen.generation.output_report.plot_fracture_centersimportplot_fracture_centers
+frompydfnworks.dfnGen.generation.output_report.plot_fram_informationimportplot_fram_information
+frompydfnworks.dfnGen.generation.output_report.plot_intersection_lengthsimportplot_intersection_lengths
+frompydfnworks.dfnGen.generation.output_report.make_pdfimportmake_pdf
+
+
+defsetup_output_directory(params):
+""" Create working dictionary for plots. There is one directory for the entire network information and one for each family.
+
+ Parameters
+ ------------
+ params : dictionary
+ Output report dictionary containing general parameters. See output_report for more details
+
+ Returns
+ ---------
+ None
+
+ Notes
+ --------
+ None
+
+
+ """
+
+ ifnotos.path.isdir(params["output_dir"]):
+ os.mkdir(params["output_dir"])
+ ifnotos.path.isdir(f"{params['output_dir']}/network"):
+ os.mkdir(f"{params['output_dir']}/network")
+ foriinrange(1,params["num_families"]+1):
+ ifnotos.path.isdir(f"{params['output_dir']}/family_{i}"):
+ os.mkdir(f"{params['output_dir']}/family_{i}")
+
+
+
+[docs]
+defoutput_report(self,verbose=True,output_dir="dfnGen_output_report"):
+""" Creates a PDF output report for the network created by DFNGen. Plots of the fracture lengths, locations, orientations are produced for each family. Files are written into "output_dir/family_{id}/". Information about the whole network are also created and written into "output_dir/network/"
+
+ Parameters
+ ----------
+ self : object
+ DFN Class object
+ verbose : bool
+ Toggle for the amount of information printed to screen. If true, progress information printed to screen
+ output_dir : string
+ Name of directory where all plots are saved
+
+ Returns
+ --------
+ None
+
+ Notes
+ ---------
+ Final output report is named "jobname"_output_report.pdf
+ User defined fractures (ellipses, rectangles, and polygons) are not supported at this time.
+
+
+ """
+ cwd=os.getcwd()
+ print("="*80)
+ print('Creating Report of DFN generation')
+ print("="*80+"\n")
+ print('--> Gathering Network Information')
+ # Create a list of dictionaries with information about fracture family
+ families=get_family_information()
+ # Create a list of dictionaries with information about fracture
+ fractures=get_fracture_information()
+ # Combine information of the families and fractures, e.g., which fracture are in each family, and create a dictionary with parameters used throughout the output report
+ families,fractures,params=combine_family_and_fracture_information(
+ families,fractures,self.num_frac,self.domain)
+ params,families=parse_dfn_output(params,families)
+
+ params["verbose"]=verbose
+ params["jobname"]=self.local_jobname
+ params["output_dir"]=output_dir
+
+ setup_output_directory(params)
+
+ # Create Plots
+ iflen(families)>0:
+ print('--> Plotting Information')
+ plot_fracture_centers(params,families,fractures)
+ plot_fracture_radii(params,families,fractures)
+ plot_fracture_orientations(params,families,fractures)
+ plot_fram_information(params)
+ # # Combine plots into a pdf
+ make_pdf(params,families,fractures)
+ print(
+ f"--> Output report is written into {self.local_jobname}_output_report.pdf\n"
+ )
+
+ else:
+ print(
+ "--> There are no stochastic families. An output PDF will not be generated.\n"
+ )
+
+ # Return to main directory
+ print("="*80)
+ print("Creating Report of DFN generation complete")
+ print("="*80+"\n")
+ os.chdir(self.jobname)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/stress.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/stress.html
new file mode 100644
index 000000000..e6d3630e0
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/generation/stress.html
@@ -0,0 +1,276 @@
+
+
+
+
+
+ pydfnworks.dfnGen.generation.stress — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.meshing.add_attribute_to_mesh
+importsubprocess
+fromnumpyimportgenfromtxt,zeros,savetxt
+importsys
+importos
+
+frompydfnworks.dfnGen.meshing.mesh_dfn.mesh_dfn_helperimportrun_lagrit_script
+frompydfnworks.generalimporthelper_functionsashf
+
+
+defcreate_variable_file(variable,variable_file,matid_file="materialid.dat"):
+"""
+ Creates a node based file for variables
+
+ Parameters
+ -----------
+ variable : string
+ name of variable
+ variable_file : string
+ name of file containing variable files. Must be a single column where each line corresponds to that fracture number.
+ matid_file : string
+ name of materialid file produced by large. Normally produced by run_meshing.
+
+ Returns
+ ----------
+ variable_file_by_node : string
+ name of file containing node based values of the variable
+
+ """
+
+ print(f"--> Making {variable} by node file")
+ values=genfromtxt(variable_file,skip_header=0,usecols=(-1))
+ ifnotos.path.isfile(matid_file):
+ hf.print_error(f"Cannot locate the file '{matid_file}")
+
+ nodes=genfromtxt(matid_file,skip_header=3).astype(int)
+ value_by_node=zeros(len(nodes))
+ fori,ninenumerate(nodes):
+ value_by_node[i]=values[n-1]
+ variable_file_by_node=f"{variable}_by_node.dat"
+ savetxt(variable_file_by_node,value_by_node)
+ print("--> Complete")
+ returnvariable_file_by_node
+
+
+defcreate_lagrit_append_script(variable,variable_file,mesh_file_in,
+ mesh_file_out):
+"""
+ Creates a LaGriT script to append the attribute to the mesh
+
+ Parameters
+ -----------
+ variable : string
+ name of variable
+ variable_file : string
+ name of file containing variable files. Must be a single column where each line corresponds to that node number in the mesh
+ mesh_file_in : string
+ Name of source mesh file
+ mesh_file_out : string
+ Name of Target mesh file
+ Returns
+ ----------
+ lagrit_file : string
+ Name of LaGriT output file
+ """
+ print("--> Making LaGriT script")
+ lagrit_script=f'''
+read / {mesh_file_in} / mo1
+cmo / addatt / mo1 / {variable} / vdouble / scalar / nnodes
+cmo / setatt / mo1 / {variable} / 1 0 0 / 1
+cmo / readatt / mo1 / {variable} / 1, 0, 0 / {variable_file}
+dump / {mesh_file_out} / mo1
+finish
+'''
+
+ lagrit_file=f"add_{variable}_to_mesh.lgi"
+ withopen(lagrit_file,"w")asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+ print("--> Complete")
+ returnlagrit_file
+
+
+
+[docs]
+defadd_variable_to_mesh(self,
+ variable,
+ variable_file,
+ mesh_file_in,
+ mesh_file_out=None,
+ node_based=False):
+"""
+ Adds a variable to the nodes of a mesh. Can be either fracture (material) based
+ or node based.
+
+ Parameters
+ -----------
+ self : object
+ DFN Class
+ variable : string
+ name of variable
+ variable_file : string
+ name of file containing variable files. Must be a single column where each line corresponds to that node number in the mesh
+ mesh_file_in : string
+ Name of source mesh file
+ mesh_file_out : string
+ Name of Target mesh file. If no name if provide, mesh_file_in will be used
+ node_based : bool
+ Set to True if variable_file contains node-based values, Set to False
+ if variable_file provide fracture based values
+
+ Returns
+ ----------
+ lagrit_file : string
+ Name of LaGriT output file
+ """
+
+ # Check input files
+ ifnotos.path.isfile(variable_file):
+ hf.print_error(
+ f"Error -- in function 'add_variable_to_mesh'. The file {variable_file} is not in current directory. Please check the filename."
+ )
+
+ ifnotos.path.isfile(mesh_file_in):
+ hf.print_error(
+ f"Error -- in function 'add_variable_to_mesh'. The mesh file {mesh_file_in} is not in current directory. Please check the filename."
+ )
+
+ # if an output mesh file is not provided, set target mesh to be the source mesh.
+ ifmesh_file_outisNone:
+ mesh_file_out=mesh_file_in
+
+ print(
+ f"--> Adding attribute in {variable_file} to mesh file {mesh_file_in}.\n--> Output writting into {mesh_file_out}"
+ )
+
+ ifnode_based:
+ print(f"--> Expecting node-based values")
+ lagrit_file=create_lagrit_append_script(variable,variable_file,
+ mesh_file_in,mesh_file_out)
+ else:
+ variable_file_by_node=create_variable_file(variable,variable_file)
+ lagrit_file=create_lagrit_append_script(variable,
+ variable_file_by_node,
+ mesh_file_in,mesh_file_out)
+
+ run_lagrit_script(lagrit_file)
+
+ print(
+ f"--> Complete: Adding attribute in {variable_file} to mesh file {mesh_file_in}.\n--> Output writting into {mesh_file_out}"
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/dfm/mesh_dfm.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/dfm/mesh_dfm.html
new file mode 100644
index 000000000..8a1b2be42
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/dfm/mesh_dfm.html
@@ -0,0 +1,1214 @@
+
+
+
+
+
+ pydfnworks.dfnGen.meshing.dfm.mesh_dfm — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.meshing.dfm.mesh_dfm
+"""
+.. module:: mesh_dfm.py
+ :synopsis: meshing driver for conforming DFM
+.. moduleauthor:: Jeffrey Hyman <jhyman@lanl.gov>
+
+"""
+
+importos
+importsys
+importshutil
+importglob
+
+# pydfnworks Modules
+frompydfnworks.dfnGen.meshing.mesh_dfnimportmesh_dfn_helperasmh
+
+defsetup_mesh_dfm_directory(jobname,dirname):
+""" Setup working directory for meshing the DFM.
+
+ Parameters
+ ----------------
+ jobname : string
+ path to DFN working directory
+ dirname : string
+ name of working directory
+
+ Returns
+ --------------
+ None
+
+ Notes
+ -------------
+ None
+ """
+ path=jobname+os.sep+dirname
+ try:
+ os.mkdir(path)
+ os.chdir(path)
+ except:
+ shutil.rmtree(path)
+ os.mkdir(path)
+ os.chdir(path)
+
+
+ print(f"--> Working directory is now {os.getcwd()}")
+ # Make symbolic links to required files
+ try:
+ os.symlink(jobname+os.sep+"full_mesh.inp","full_mesh.inp")
+ except:
+ error=f"Error. Unable to make symbolic link to full_mesh.inp file for DFM meshing from {jobname}.\nExitting program."
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ print("--> Setting up DFM meshing directory complete")
+
+
+
+deftranslate_mesh(x1,x2):
+"""
+ Moves reduced_mesh.inp from center at x1 to x2
+
+ Parameters
+ ---------------
+ x1 : list
+ floats x-0, y-1, z-2 - current center
+
+ x2 : list
+ floats x-0, y-1, z-2 - requisted center
+ Returns
+ --------------
+ None
+
+ """
+
+ lagrit_script=f"""
+read / avs / full_mesh.inp / MODFN
+trans / 1 0 0 / {x1[0]}{x1[1]}{x1[2]} / {x2[0]}{x2[1]}{x2[2]}
+cmo / printatt / MODFN / -xyz- / minmax
+dump / full_mesh.inp / MODFN
+finish
+"""
+ withopen('translate_mesh.lgi','w')asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+ mh.run_lagrit_script("translate_mesh.lgi")
+
+
+defcreate_domain(domain,h):
+""" Gather domain information.
+
+ Parameters
+ ----------
+ domain : dict
+ Domain size dictionary from DFN object
+ h : float
+ Meshing length scale from DFN object
+
+ Returns
+ -------
+ num_points : int
+ Number of points on side of the domain
+ box_domain : dict
+ dictionary of domain min/max for x,y,z
+
+ Notes
+ ------
+ Exits program is too many points are in domain.
+ Assuming that
+
+ """
+ box_domain={"x0":None,"x0":None,
+ "y0":None,"y1":None,
+ "z0":None,"z1":None
+ }
+
+ # Extent of domain
+ box_domain['x0']=-0.5*domain['x']
+ box_domain['x1']=0.5*domain['x']
+
+ box_domain['y0']=-0.5*domain['y']
+ box_domain['y1']=0.5*domain['y']
+
+ box_domain['z0']=-0.5*domain['z']
+ box_domain['z1']=0.5*domain['z']
+
+ # Mesh size in matrix
+ l=h/2
+ # # Number of points in each direction in matrix
+ # num_points = domain['x'] / l + 1
+ # if num_points**3 > 1e8:
+ # error = f"Error: Too many elements for DFM meshing.\nValue {num_points**3}\nMaximum is 1e8\nExiting Program"
+ # sys.stderr.write(error)
+ # sys.exit(1)
+
+ num_points_x=domain['x']/l+1
+ num_points_y=domain['y']/l+1
+ num_points_z=domain['z']/l+1
+ ifnum_points_x*num_points_y*num_points_z>1e8:
+ error=f"Error: Too many elements for DFM meshing.\nValue {num_points_x*num_points_y*num_points_z}\nMaximum is 1e8\nExiting Program"
+ sys.stderr.write(error)
+ sys.exit(1)
+ returnbox_domain,num_points_x,num_points_y,num_points_z
+
+defdfm_driver(num_points_x,num_points_y,num_points_z,num_fracs,h,box_domain,psets):
+""" This function creates the main lagrit driver script, which calls the other lagrit scripts.
+
+ Parameters
+ ----------
+ num_points : int
+ Number of points on side of the domain
+ num_fracs : int
+ Number of Fractures in the DFN
+ h : float
+ meshing length scale
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+ """
+ floop=""
+ forifracinrange(1,num_fracs+1):
+ ififrac<num_fracs:
+ floop+=f"facets_f{ifrac}.table &\n"
+ else:
+ floop+=f"facets_f{ifrac}.table &\n"
+ floop+="left.table &\n"
+ floop+="right.table &\n"
+ floop+="front.table &\n"
+ floop+="back.table &\n"
+ floop+="top.table &\n"
+ floop+="bottom.table"
+
+ lagrit_script=f"""#
+# dfm_mesh_fracture_driver.lgi
+# dfm_box_dimensions.mlgi
+# dfm_build_background_mesh.mlgi
+# dfm_extract_fracture_facets.mlgi
+# dfm_extract_facets.mlgi
+#
+# extract_fracture_facets.mlgi must be customized for the number of fractures in the DFN
+#
+# This is the dfnWorks DFN mesh
+#
+define / INPUT / full_mesh.inp
+read / avs / INPUT / mo_dfn
+cmo / DELATT / mo_dfn / dfield
+cmo / DELATT / mo_dfn / b_a
+cmo / DELATT / mo_dfn / numbnd
+cmo / DELATT / mo_dfn / if_numb
+#
+# Diagnostics on fracture mesh extents and resolution
+#
+cmo / printatt / mo_dfn / -xyz- / minmax
+quality
+quality/edge_min
+quality/edge_max
+#
+# Define a resolution for the background mesh. This assumes the DFN
+# triangulation is uniform resolution triangles. No attempt is made
+# to adapt the volume mesh resolution to the DFN triangle resolution.
+#
+define / NPX / {num_points_x}
+# define / NPXM1 / {num_points_x-1}
+define / NPY / {num_points_y}
+# define / NPYM1 / {num_points_y-1}
+define / NPZ / {num_points_z}
+# define / NPZM1 / {num_points_z-1}
+define / VERTEX_CLOSE / {h/4}
+#
+define / MO_BACKGROUND / mo_background
+infile dfm_box_dimensions.mlgi
+infile dfm_build_background_mesh.mlgi
+#
+# Remove all vertices of the tet mesh that fall withing a circumsphere of a fracture triangle.
+#
+addmesh / excavate / mo_tmp / MO_BACKGROUND / mo_dfn
+cmo / delete / MO_BACKGROUND
+#
+# Merge the vertices of the excavated tet mesh with the DFN vertices
+#
+cmo / create / mo_dfm / / / tet
+copypts / mo_dfm / mo_tmp
+cmo / delete / mo_tmp
+compute / distance_field / mo_dfm / mo_dfn / df_vertex
+cmo / select / mo_dfm
+pset / pdel / attribute / df_vertex / 1 0 0 / le VERTEX_CLOSE
+rmpoint / pset get pdel / inclusive
+rmpoint / compress
+cmo / DELATT / mo_dfm / df_vertex
+copypts / mo_dfm / mo_dfn
+#
+cmo / setatt / mo_dfm / imt / 1 0 0 / 1
+cmo / setatt / mo_dfm / itp / 1 0 0 / 0
+cmo / select / mo_dfm
+connect
+cmo / setatt / mo_dfm / itetclr / 1 0 0 / 1
+resetpts / itp
+quality
+#
+#compute / signed_distance_field / mo_dfm / mo_dfn / df_sign_dfm_dfn
+#
+# crush_thin_tets / mo_dfm / 0.25 / 1 0 0
+dump / avs / dfm_tet_mesh.inp / mo_dfm
+dump / lagrit / dfm_tet_mesh.lg / mo_dfm
+dump / exo / dfm_tet_mesh.exo / mo_dfm
+
+cmo / delete / mo_dfm
+cmo / delete / mo_dfn
+#
+cmo / status / brief
+#
+infile dfm_extract_fracture_facets.mlgi
+infile dfm_diagnostics.mlgi
+#
+# Delete this !!!!
+# Hardcoded facesets on boundaries for Alex EES17
+cmo / select / mo_dfm
+extract / surfmesh / 1 0 0 / mo_surf / mo_dfm / external
+cmo / addatt / mo_surf / id_side / vint / scalar / nelements
+cmo / select / mo_surf
+settets / normal
+cmo / copyatt / mo_surf mo_surf / id_side itetclr
+cmo / printatt / mo_surf / id_side / minmax
+cmo / DELATT / mo_surf / itetclr0
+cmo / DELATT / mo_surf / idnode0
+cmo / DELATT / mo_surf / idelem0
+cmo / DELATT / mo_surf / facecol
+cmo / DELATT / mo_surf / itetclr1
+cmo / DELATT / mo_surf / idface0
+#
+cmo / copy / mo_tmp / mo_surf
+cmo / select / mo_tmp
+eltset / e_bottom / id_side / eq / 1
+eltset / e_delete / not / e_bottom
+rmpoint / element / eltset get e_delete
+rmpoint / compress
+cmo / DELATT / mo_tmp / id_side
+dump / avs2 / bottom.table / mo_tmp / 0 0 0 2
+cmo / delete / mo_tmp
+#
+cmo / copy / mo_tmp / mo_surf
+cmo / select / mo_tmp
+eltset / e_top / id_side / eq / 2
+eltset / e_delete / not / e_top
+rmpoint / element / eltset get e_delete
+rmpoint / compress
+cmo / DELATT / mo_tmp / id_side
+dump / avs2 / top.table / mo_tmp / 0 0 0 2
+cmo / delete / mo_tmp
+#
+cmo / copy / mo_tmp / mo_surf
+cmo / select / mo_tmp
+eltset / e_right / id_side / eq / 3
+eltset / e_delete / not / e_right
+rmpoint / element / eltset get e_delete
+rmpoint / compress
+cmo / DELATT / mo_tmp / id_side
+dump / avs2 / right.table / mo_tmp / 0 0 0 2
+cmo / delete / mo_tmp
+#
+cmo / copy / mo_tmp / mo_surf
+cmo / select / mo_tmp
+eltset / e_back / id_side / eq / 4
+eltset / e_delete / not / e_back
+rmpoint / element / eltset get e_delete
+rmpoint / compress
+cmo / DELATT / mo_tmp / id_side
+dump / avs2 / back.table / mo_tmp / 0 0 0 2
+cmo / delete / mo_tmp
+#
+cmo / copy / mo_tmp / mo_surf
+cmo / select / mo_tmp
+eltset / e_left / id_side / eq / 5
+eltset / e_delete / not / e_left
+rmpoint / element / eltset get e_delete
+rmpoint / compress
+cmo / DELATT / mo_tmp / id_side
+dump / avs2 / left.table / mo_tmp / 0 0 0 2
+cmo / delete / mo_tmp
+#
+cmo / copy / mo_tmp / mo_surf
+cmo / select / mo_tmp
+eltset / e_front / id_side / eq / 6
+eltset / e_delete / not / e_front
+rmpoint / element / eltset get e_delete
+rmpoint / compress
+cmo / DELATT / mo_tmp / id_side
+dump / avs2 / front.table / mo_tmp / 0 0 0 2
+cmo / delete / mo_tmp
+#
+"""
+
+ ifpsets:
+ eps=h/4
+ lagrit_script+=f"""
+cmo / select / mo_dfm
+cmo / printatt / mo_dfm / -xyz- / minmax
+
+pset/ pleft / geom / xyz / 1, 0, 0 / &
+{box_domain['x0']-eps}{box_domain['y0']}{box_domain['z0']} / {box_domain['x0']+eps}{box_domain['y1']}{box_domain['z1']} / 0,0,0
+pset/ pright / geom / xyz / 1, 0, 0 / &
+{box_domain['x1']-eps}{box_domain['y0']}{box_domain['z0']} / {box_domain['x1']+eps}{box_domain['y1']}{box_domain['z1']} / 0,0,0
+
+pset / pfront / geom / xyz / 1, 0, 0 / &
+{box_domain['x0']}{box_domain['y0']-eps}{box_domain['z0']} / {box_domain['x1']}{box_domain['y0']+eps}{box_domain['z1']} / 0,0,0
+pset / pback / geom / xyz / 1, 0, 0 / &
+{box_domain['x0']}{box_domain['y1']-eps}{box_domain['z0']} / {box_domain['x1']}{box_domain['y1']+eps}{box_domain['z1']} / 0,0,0
+
+pset / pbottom / geom / xyz / 1, 0, 0 / &
+{box_domain['x0']}{box_domain['y0']}{box_domain['z0']-eps} / {box_domain['x1']}{box_domain['y1']}{box_domain['z0']+eps}/ 0,0,0
+pset / ptop / geom / xyz / 1, 0, 0 / &
+{box_domain['x0']}{box_domain['y0']}{box_domain['z1']-eps} / {box_domain['x1']}{box_domain['y1']}{box_domain['z1']+eps} / 0,0,0
+
+# corners of the mesh 1
+pset / p_tmp / inter / pleft pbottom
+pset / p_corner_lfb / inter / p_tmp pfront
+pset / p_tmp / delete
+
+pset / pbottom / not / pbottom p_corner_lfb
+pset / pleft / not / pleft p_corner_lfb
+pset / pfront / not / pfront p_corner_lfb
+
+
+cmo / addatt / mo_dfm / p_corner_lfb / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_lfb / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_lfb /pset,get,p_corner_lfb / 1
+
+# corners of the mesh 2
+pset / p_tmp / inter / pright pbottom
+pset / p_corner_rfb / inter / p_tmp pfront
+pset / p_tmp / delete
+
+pset / pbottom / not / pbottom p_corner_rfb
+pset / pright / not / pright p_rfp_corner
+pset / pfront / not / pfront p_corner_rfb
+
+cmo / addatt / mo_dfm / p_corner_rfb / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_rfb / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_rfb /pset,get,p_corner_rfb / 1
+
+# corners of the mesh 3
+pset / p_tmp / inter / pleft ptop
+pset / p_corner_lft / inter / p_tmp pfront
+
+pset / pbottom / not / pbottom p_corner_lft
+pset / pleft / not / pleft p_corner_lft
+pset / pfront / not / pfront p_corner_lft
+pset / p_tmp / delete
+
+cmo / addatt / mo_dfm / p_corner_lft / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_lft / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_lft /pset,get,p_corner_lft / 1
+
+# corners of the mesh 4
+pset / p_tmp / inter / pright ptop
+pset / p_corner_rft / inter / p_tmp pfront
+pset / p_tmp / delete
+
+pset / ptop / not / ptop p_corner_rft
+pset / pright / not / pright p_corner_rft
+pset / pfront / not / pfront p_corner_rft
+
+
+
+cmo / addatt / mo_dfm / p_corner_rft / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_rft / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_rft /pset,get,p_corner_rft / 1
+
+
+
+### back face
+
+# corners of the mesh 1
+pset / p_tmp / inter / pleft pbottom
+pset / p_corner_lbb / inter / p_tmp pback
+pset / p_tmp / delete
+
+
+# corners of the mesh 2
+pset / p_tmp / inter / pright pbottom
+pset / p_corner_rbb / inter / p_tmp pback
+pset / p_tmp / delete
+
+
+# corners of the mesh 3
+pset / p_tmp / inter / pleft ptop
+pset / p_corner_lbt / inter / p_tmp pback
+pset / p_tmp / delete
+
+
+# corners of the mesh 4
+pset / p_tmp / inter / pright ptop
+pset / p_corner_rbt / inter / p_tmp pback
+pset / p_tmp / delete
+
+########
+
+cmo / addatt / mo_dfm / p_corner_rbt / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_rbt / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_rbt /pset,get,p_corner_rbt / 1
+
+cmo / addatt / mo_dfm / p_corner_lbt / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_lbt / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_lbt /pset,get,p_corner_lbt / 1
+
+
+cmo / addatt / mo_dfm / p_corner_lbb / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_lbb / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_lbb /pset,get,p_corner_lbb / 1
+
+cmo / addatt / mo_dfm / p_corner_rbb / vint / scalar / nnodes
+cmo/setatt / mo_dfm / p_corner_rbb / 1,0,0 / 0
+cmo/setatt / mo_dfm / p_corner_rbb /pset,get,p_corner_rbb / 1
+
+## clean up PSETS TO MESH
+pset / pbottom / not / pbottom p_corner_lbb
+pset / pleft / not / pleft p_corner_lbb
+pset / pback / not / pback p_corner_lbb
+
+pset / pbottom / not / pbottom p_corner_rbb
+pset / pright / not / pright p_corner_rbb
+pset / pback / not / pback p_corner_rbb
+
+pset / ptop / not / ptop p_corner_lbt
+pset / pleft / not / pleft p_corner_lbt
+pset / pback / not / pback p_corner_lbt
+
+pset / ptop / not / ptop p_corner_rbt
+pset / pright / not / pright p_corner_rbt
+pset / pback / not / pback p_corner_rbt
+
+
+pset / pbottom / not / pbottom p_corner_lfb
+pset / pleft / not / pleft p_corner_lfb
+pset / pfront / not / pfront p_corner_lfb
+
+pset / pbottom / not / pbottom p_corner_rfb
+pset / pright / not / pright p_corner_rfb
+pset / pfront / not / pfront p_corner_rfb
+
+pset / ptop / not / ptop p_corner_lft
+pset / pleft / not / pleft p_corner_lft
+pset / pfront / not / pfront p_corner_lft
+
+pset / ptop / not / ptop p_corner_rft
+pset / pright / not / pright p_corner_rft
+pset / pfront / not / pfront p_corner_rft
+
+
+
+### edges #####
+
+pset / p_edge_lb / inter / pleft pbottom
+pset / pbottom / not / pbottom p_edge_lb
+pset / pleft / not / pleft p_edge_lb
+
+pset / p_edge_lt / inter / pleft ptop
+pset / ptop / not / ptop p_edge_lt
+pset / pleft / not / pleft p_edge_lt
+
+pset / p_edge_rb / inter / pright pbottom
+pset / pbottom / not / pbottom p_edge_rb
+pset / pright / not / pright p_edge_rb
+
+pset / p_edge_rt / inter / pright ptop
+pset / ptop / not / ptop p_edge_rt
+pset / pright / not / pright p_edge_rt
+
+#######
+pset / p_edge_lfr / inter / pleft pfront
+pset / pleft / not / pleft p_edge_lfr
+pset / pfront / not / pfront p_edge_lfr
+
+pset / p_edge_lba / inter / pleft pback
+pset / pleft / not / pleft p_edge_lba
+pset / pback / not / pback p_edge_lba
+
+pset / p_edge_rfr / inter / pright pfront
+pset / pright / not / pright p_edge_rfr
+pset / pfront / not / pfront p_edge_rfr
+
+pset / p_edge_rba / inter / pright pback
+pset / pright / not / pright p_edge_rba
+pset / pback / not / pback p_edge_rba
+
+#######
+
+
+pset / p_edge_frb / inter / pfront pbottom
+pset / pfront / not / pfront p_edge_frb
+pset / pbottom / not / pbottom p_edge_frb
+
+pset / p_edge_bab / inter / pback pbottom
+pset / pback / not / pback p_edge_bab
+pset / pbottom / not / pbottom p_edge_bab
+
+pset / p_edge_frtop / inter / pfront ptop
+pset / pfront / not / pfront p_edge_frtop
+pset / ptop / not / ptop p_edge_frtop
+
+pset / p_edge_btop / inter / pback ptop
+pset / pback / not / pback p_edge_btop
+pset / ptop / not / ptop p_edge_btop
+
+#######
+
+cmo / addatt / mo_dfm / right / vint / scalar / nnodes
+cmo/setatt / mo_dfm / right / 1,0,0 / 0
+cmo/setatt / mo_dfm / right /pset,get,pright / 1
+
+cmo / addatt / mo_dfm / back / vint / scalar / nnodes
+cmo/setatt / mo_dfm / back / 1,0,0 / 0
+cmo/setatt / mo_dfm / back /pset,get,pback / 1
+
+
+cmo / addatt / mo_dfm / left / vint / scalar / nnodes
+cmo/setatt / mo_dfm / left / 1,0,0 / 0
+cmo/setatt / mo_dfm / left /pset,get,pleft / 1
+
+cmo / addatt / mo_dfm / top / vint / scalar / nnodes
+cmo/setatt / mo_dfm / top / 1,0,0 / 0
+cmo/setatt / mo_dfm / top /pset,get,ptop / 1
+
+cmo / addatt / mo_dfm / bottom / vint / scalar / nnodes
+cmo/setatt / mo_dfm / bottom / 1,0,0 / 0
+cmo/setatt / mo_dfm / bottom /pset,get,pbottom / 1
+
+cmo / addatt / mo_dfm / front / vint / scalar / nnodes
+cmo/setatt / mo_dfm / front / 1,0,0 / 0
+cmo/setatt / mo_dfm / front /pset,get,pfront / 1
+
+dump / dfm_tet_w_psets.inp / mo_dfm
+dump / exo / dfm_tet_mesh_w_fsets.exo / mo_dfm / psets / / &
+ facesets &
+"""
+ lagrit_script+=floop
+ lagrit_script+="""
+finish
+"""
+ else:## no psets
+ lagrit_script+="""
+dump / exo / dfm_tet_mesh_w_fsets.exo / mo_dfm / / / &
+ facesets &
+"""
+ lagrit_script+=floop
+ lagrit_script+="""
+finish
+"""
+
+ withopen('dfm_mesh_fracture_driver.lgi','w')asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+
+ print("Creating dfm_mesh_fracture_driver.lgi file: Complete\n")
+
+defdfm_box(box_domain):
+""" This function creates the dfm_box_dimensions.mlgi lagrit script.
+
+ Parameters
+ ----------
+ box_domain : dict
+ dictionary of domain min/max for x,y,z
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+
+ """
+
+ lagrit_script=f"""#
+# Define a bounding box that surrounds, and is a big bigger, than the DFN
+#
+define / X0 / {box_domain['x0']}
+define / X1 / {box_domain['x1']}
+define / Y0 / {box_domain['y0']}
+define / Y1 / {box_domain['y1']}
+define / Z0 / {box_domain['z0']}
+define / Z1 / {box_domain['z1']}
+
+finish
+"""
+ withopen('dfm_box_dimensions.mlgi','w')asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+
+ print("Creating dfm_box_dimensions.mlgi file: Complete\n")
+
+defdfm_build():
+""" Create the dfm_build_background_mesh.mlgi lagrit script.
+
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ Needs to be modified to have different NPX, NPY, NPZ
+ """
+
+ lagrit_script="""#
+# Build a uniform background point distribution.
+#
+cmo / create / MO_BACKGROUND / / / tet
+createpts / xyz / NPX NPY NPZ / X0 Y0 Z0 / X1 Y1 Z1 / 1 1 1
+cmo / setatt / MO_BACKGROUND / imt / 1 0 0 / 1
+connect / noadd
+cmo / setatt / MO_BACKGROUND / itetclr / 1 0 0 / 1
+#
+finish
+"""
+ withopen('dfm_build_background_mesh.mlgi','w')asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+ print("Creating dfm_box_dimensions.mlgi file: Complete\n")
+
+defdfm_fracture_facets(num_frac):
+""" This function creates the dfm_extract_fracture_facets.mlgi lagrit script.
+
+ Parameters
+ ----------
+ num_frac : int
+ Number of fractures in the DFN
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+ """
+ floop1=""
+ floop2=""
+ forifracinrange(1,num_frac+1):
+ floop1+=f"""
+define / FRAC_ID / {ifrac}
+define / FRAC_FILE_OUT / facets_f{ifrac}.inp
+define / FRAC_TABLE_OUT / facets_f{ifrac}.table
+#
+infile dfm_extract_facets.mlgi
+ """
+ ififrac==1:
+ floop2+=f"""
+read / avs / facets_f{ifrac}.inp / mo_merge
+cmo / setatt / mo_merge / itetclr / 1 0 0 / {ifrac}
+ """
+ else:
+ floop2+=f"""
+read / avs / facets_f{ifrac}.inp / mo
+cmo / setatt / mo / itetclr / 1 0 0 / {ifrac}
+addmesh / merge / mo_merge / mo_merge / mo
+cmo / delete / mo
+ """
+ lagrit_script="""#
+define / INPUT / full_mesh.inp
+define / MO_ONE_FRAC / mo_tmp_one_fracture
+#
+read / avs / dfm_tet_mesh.inp / mo_dfm
+#
+cmo / create / mo_merge
+cmo / status / brief
+read / avs / INPUT / mo_dfn
+cmo / status / brief
+"""+floop1+floop2+"""
+dump / avs / facets_merged.inp / mo_merge
+cmo / addatt / mo_merge / id_frac / vint / scalar / nelements
+cmo / copyatt / mo_merge / mo_merge / id_frac / itetclr
+dump / avs / facets_merged.table / mo_merge / 0 0 0 2
+cmo / delete / mo_merge
+
+finish
+"""
+ withopen('dfm_extract_fracture_facets.mlgi','w')asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+ print("Creating dfm_extract_fracture_facets.mlgi file: Complete\n")
+
+defdfm_facets():
+""" This function creates the dfm_extract_facets.mlgi lagrit script.
+
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+
+ """
+
+ lagrit_script=f"""#
+cmo / copy / MO_ONE_FRAC / mo_dfn
+cmo / select / MO_ONE_FRAC
+rmmat / FRAC_ID / element / exclusive
+rmpoint / compress
+resetpts / itp
+cmo / status / brief
+#
+compute / signed_distance_field / mo_dfm / MO_ONE_FRAC / dfield_sign
+
+cmo / delete / MO_ONE_FRAC
+#
+cmo / copy / mo_df_work / mo_dfm
+
+cmo / DELATT / mo_dfm / dfield_sign
+
+cmo / select / mo_df_work
+pset / p1 / attribute / dfield_sign / 1 0 0 / gt 0.0
+pset / p2 / attribute / dfield_sign / 1 0 0 / lt 0.0
+eltset / e1 / inclusive / pset get p1
+eltset / e2 / inclusive / pset get p2
+cmo / setatt / mo_df_work / itetclr / eltset get e1 / 1
+cmo / setatt / mo_df_work / itetclr / eltset get e2 / 2
+resetpts / itp
+extract / surfmesh / 1 0 0 / MO_ONE_FRAC_EXTRACT / mo_df_work
+#
+cmo / select / MO_ONE_FRAC_EXTRACT
+eltset / edel / idelem0 / eq / 0
+rmpoint / element / eltset get edel
+rmpoint / compress
+pset / pdel / attribute / dfield_sign / 1 0 0 / gt / 1.e-9
+rmpoint / pset get pdel / inclusive
+rmpoint / compress
+#
+# idelem0, idelem1 are element numbers
+# idface0, idface1 are the face numbers
+#
+cmo / DELATT / MO_ONE_FRAC_EXTRACT / itetclr0
+cmo / DELATT / MO_ONE_FRAC_EXTRACT / itetclr1
+cmo / DELATT / MO_ONE_FRAC_EXTRACT / facecol
+#
+# Don't keep both sides of the fracture face information.
+#
+cmo / DELATT / MO_ONE_FRAC_EXTRACT / idelem0
+cmo / DELATT / MO_ONE_FRAC_EXTRACT / idface0
+#
+dump / avs2 / FRAC_FILE_OUT / MO_ONE_FRAC_EXTRACT
+dump / avs2 / FRAC_TABLE_OUT / MO_ONE_FRAC_EXTRACT / 0 0 0 2
+#
+cmo / delete / MO_ONE_FRAC_EXTRACT
+#
+cmo / status / brief
+#
+finish
+"""
+ withopen('dfm_extract_facets.mlgi','w')asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+
+ print("Creating dfm_extract_facets.mlgi file: Complete\n")
+
+
+defdfm_diagnostics(h):
+"""
+
+ """
+ eps_offset=0.1*h
+ lagrit_script=f"""
+
+# Figure out which cells (tringles) from DFN full_mesh.inp were not reproduced
+# in the DFM tet fracture faces (facets_f1.inp, facets_f2.inp, etc).
+#
+read / avs / full_mesh.inp / mo_full
+#
+read / avs / facets_merged.inp / mo_merge
+#
+# If the above file exists the next lines can be removed.
+#
+# Interpolate does not work well on coincident 2D triangulations. C'est la vie.
+# To work around this turn the facets into prism volumes by giving them a small
+# negative and positive offset and then combine to make prisms. Then you have volume
+# cells to interpolate from.
+#
+#++++++++++++++++++++++++++++++++++++
+# EPS_OFFSET should be set to ~0.1h
+#
+define / EPS_OFFSET_1 / {-1*eps_offset}
+define / EPS_OFFSET_2 / {eps_offset}
+#++++++++++++++++++++++++++++++++++++
+offsetsurf / mo_offset_1 / mo_merge / EPS_OFFSET_1
+cmo / setatt / mo_offset_1 / imt / 1 0 0 / 1
+offsetsurf / mo_offset_2 / mo_merge / EPS_OFFSET_2
+cmo / setatt / mo_offset_2 / imt / 1 0 0 / 2
+addmesh / merge / mo_offset_1_2 / mo_offset_1 / mo_offset_2
+pset / p_bottom / attribute / imt / 1 0 0 / eq / 1
+pset / p_top / attribute / imt / 1 0 0 / eq / 2
+
+extrude / mo_extrude / mo_offset_1_2 / interp / 0 / &
+ pset,get,p_bottom / pset,get,p_top
+
+cmo / delete / mo_merge
+cmo / delete / mo_offset_1
+cmo / delete / mo_offset_2
+cmo / delete / mo_offset_1_2
+cmo / select / mo_extrude
+quality
+
+cmo / addatt / mo_full / mat_interp / vint / scalar / nelements
+cmo / setatt / mo_full / mat_interp / 1 0 0 / 2
+cmo / setatt / mo_extrude / itetclr / 1 0 0 / 1
+interpolate / map / mo_full mat_interp / 1 0 0 / &
+ mo_extrude itetclr
+dump / avs / tmp_interpolate.inp / mo_full
+cmo / delete / mo_extrude
+cmo / select / mo_full
+eltset / edelete / mat_interp / eq / 1
+
+cmo / addatt / mo_full / volume / e_area
+math / sum / mo_full / area_sum / 1,0,0 / mo_full / e_area
+
+rmpoint / element / eltset get edelete
+rmpoint / compress
+# Note: If there are no missed cells, this will return:
+# RMPOINT: new point count is 0
+# RMPOINT: new element count is 0
+
+cmo / status / brief
+
+cmo / addatt / mo_full / volume / e_area
+math / sum / mo_full / area_sum / 1,0,0 / mo_full / e_area
+# Note: If there are no missed cells, this MO will be empty and this
+# command will return:
+# 0 element attribute: e_area
+# FATAL ERROR: SUM unable to begin.
+# error in command : math/sum/mo_full/area_sum/1,0,0/mo_full/e_area
+#
+# The attributes that are output in this file could be cleaned up so
+# extra unnecessary information is not included.
+cmo / DELATT / mo_full / e_area
+cmo / DELATT / mo_full / mat_interp
+#
+# NOTE: If there are no missed cells, mo_full will be an empty (#nodes=0) MO
+# No file will be written and LaGriT message will be:
+# WARNING: dumpavs
+# WARNING: nnodes=0 nelements = 0
+# WARNING: No output
+dump / avs / missed_cells_full_mesh.inp / mo_full
+
+cmo / delete / mo_full
+
+finish
+
+
+
+"""
+ withopen('dfm_diagnostics.mlgi','w')asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+
+ print("Creating dfm_diagonstics.mlgi file: Complete\n")
+
+
+defcreate_dfm():
+""" This function executes the lagrit scripts.
+
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+
+ """
+ # Run LaGriT
+ mh.run_lagrit_script(
+ "dfm_mesh_fracture_driver.lgi",
+ quiet=False)
+
+
+
+defcleanup_mesh_dfm_directory():
+""" Clean up working files from meshing the DFM
+
+ Parameters
+ ---------------
+ None
+
+ Returns
+ ----------------
+ None
+
+ Notes
+ ---------------
+ None
+
+ """
+ print("--> Cleaning up working directory")
+ # clean up LaGrit Scripts
+ lagrit_script_dir="dfm_lagrit_files"
+ try:
+ os.mkdir(lagrit_script_dir)
+ except:
+ shutil.rmtree(lagrit_script_dir)
+ os.mkdir(lagrit_script_dir)
+ lagrit_scripts=glob.glob("*lgi")
+ forfilenameinlagrit_scripts:
+ shutil.copyfile(filename,lagrit_script_dir+os.sep+filename)
+ os.remove(filename)
+
+ extra_files=['dfm_mesh_fracture_driver.lgi.log','dfm_mesh_fracture_driver.lgi.out',
+ 'tmp_interpolate.inp']
+ forfilenameinextra_files:
+ shutil.copyfile(filename,lagrit_script_dir+os.sep+filename)
+ os.remove(filename)
+
+ table_dir="tables"
+ try:
+ os.mkdir(table_dir)
+ except:
+ shutil.rmtree(table_dir)
+ os.mkdir(table_dir)
+
+ table_files=glob.glob("*table")
+ forfilenameintable_files:
+ shutil.copyfile(filename,table_dir+os.sep+filename)
+ os.remove(filename)
+
+ facets_dir="facets"
+ try:
+ os.mkdir(facets_dir)
+ except:
+ shutil.rmtree(facets_dir)
+ os.mkdir(facets_dir)
+
+ facet_files=glob.glob("facets*inp")
+ forfilenameinfacet_files:
+ shutil.copyfile(filename,facets_dir+os.sep+filename)
+ os.remove(filename)
+
+
+ print("--> Cleaning up working directory: Complete")
+
+
+defcheck_dfm_mesh(allowed_percentage):
+""" Checks how many elements of the DFN meshing are missinf from the DFM. If the percentage missing is larger than the allowed percentage, then the program exists.
+
+ Parameters
+ ----------------
+ allowed_percentage : float
+ Percentage of the mesh allowed to be missing and still continue
+
+ Returns
+ ----------
+ None
+
+ Notes
+ ----------
+ None
+
+ """
+
+ print("--> Checking for missing elements")
+ ifos.path.isfile('missed_cells_full_mesh.inp'):
+ print("--> Missing elements have been found.")
+ print(f"--> Missing elements are in the file 'missed_cells_full_mesh.inp' if you want to see them.")
+ # get number of missed elements in the
+ withopen('missed_cells_full_mesh.inp','r')asfp:
+ line=fp.readline().split()
+ missing_num_elems=int(line[1])
+ # get the total number of elements
+
+ withopen('full_mesh.inp','r')asfp:
+ line=fp.readline().split()
+ total_num_elems=int(line[1])
+ # Compute percentage and compare
+ missing_percent=100*(missing_num_elems/total_num_elems)
+ print(f"--> Out of {total_num_elems} elements in the DFN there are {missing_num_elems} missing from the DFM.")
+ print(f"--> That's {missing_percent:0.2f} percent of the mesh.")
+
+ ifmissing_percent>allowed_percentage:
+ error=f"*** Error. Missing percent of mesh is larger than tolerance {allowed_percentage} ***\n*** Exitting ***\n "
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ print("--> Doesn't seem to bad. Keep Calm and Carry on.")
+
+ # if the file 'missed_cells_full_mesh.inp' does not exists, this means no elements were missed.
+ else:
+ print("--> No missinng elements found. ")
+
+
+[docs]
+defmesh_dfm(self,dirname="dfm_mesh",allowed_percentage=1,psets=False,cleanup=True):
+"""" Creates a conforming mesh of a DFN using a uniform background tetrahedron mesh. The DFN must be meshed using a uniform triangular mesh. (DFN.mesh_network(uniform_mesh = True))
+
+ Parameters
+ ------------------
+ dirname : string
+ name of working directory. Default : dfm_mesh
+ allowed_percentage : float
+ Percentage of the mesh allowed to be missing and still continue
+ cleanup : bool
+ Clean up working directory. If true dep files are moved into subdirectories
+
+ Returns
+ ---------------
+ None
+
+ Notes
+ --------------
+ The final mesh is output in exodus format. This requires that LaGriT is built against exodus.
+
+ """
+
+ print('='*80)
+ print("Creating conforming DFM mesh using LaGriT : Starting")
+ print('='*80)
+
+ setup_mesh_dfm_directory(self.jobname,dirname)
+
+ center=[self.params['domainCenter']['value'][0],self.params['domainCenter']['value'][1],self.params['domainCenter']['value'][2]]
+ translate_mesh(center,[0,0,0])
+ box_domain,num_points_x,num_points_y,num_points_z=create_domain(self.domain,self.h)
+ dfm_driver(num_points_x,num_points_y,num_points_z,self.num_frac,self.h,box_domain,psets)
+ dfm_box(box_domain)
+ dfm_build()
+ dfm_fracture_facets(self.num_frac)
+ dfm_facets()
+ dfm_diagnostics(self.h)
+ create_dfm()
+
+ check_dfm_mesh(allowed_percentage)
+
+ translate_mesh([0,0,0],center)
+
+ ifcleanup:
+ cleanup_mesh_dfm_directory()
+
+ print('='*80)
+ print("Creating conforming DFM mesh using LaGriT : Complete")
+ print('='*80)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mapdfn_ecpm/mapdfn_ecpm.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mapdfn_ecpm/mapdfn_ecpm.html
new file mode 100644
index 000000000..324b89f09
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mapdfn_ecpm/mapdfn_ecpm.html
@@ -0,0 +1,224 @@
+
+
+
+
+
+ pydfnworks.dfnGen.meshing.mapdfn_ecpm.mapdfn_ecpm — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.meshing.mapdfn_ecpm.mapdfn_ecpm
+'''
+ mapdfn2pflotran.py
+
+ Call methods in mapdfn.py to take output of dfnWorks-Version2.0, create
+ equivalent continuous porous medium representation, and write parameters
+ (permeability, porosity, tortuosity) to files for use with PFLOTRAN.
+
+ Usage: Edit values for origin, nx, ny, nz, d, k_background, bulk_por,
+ tortuosity factor, and h5origin.
+ Paths and filenames are hardwired and may also need to be checked.
+ As written, they assume script is being called from a subdirectory.
+ Then: python mapdfn2pflotran.py
+
+ Dependencies: mapdfn.py
+ numpy
+ h5py
+
+ Author:
+
+ Date: 07/13/18
+ SAND Number: SAND2018-7605 O
+
+'''
+importtime
+
+frompydfnworks.dfnGen.meshing.mapdfn_ecpm.mapdfn_upscaleimportmapdfn_porosity,mapdfn_perm_iso,mapdfn_perm_aniso
+frompydfnworks.dfnGen.meshing.mapdfn_ecpm.mapdfn_ioimportwrite_h5_files
+frompydfnworks.dfnGen.meshing.mapdfn_ecpm.mapdfn_helper_functionsimportsetup_output_dir,setup_domain
+
+
+
+[docs]
+defmapdfn_ecpm(self,
+ matrix_perm,
+ matrix_porosity,
+ cell_size,
+ matrix_on=False,
+ tortuosity_factor=0.001,
+ lump_diag_terms=False,
+ correction_factor=True,
+ output_dir="mapdfn_ecpm"):
+""" This script takes the top-level directory of the dfn and maps it to an ecpm, saving the ecpm files in that directory
+
+ Parameters
+ -----------------
+ self : dfnWorks object
+
+ cell_size : float
+ The cell size (meters) to use for the meshing
+
+ correction_factor : boolean
+ Apply stairstep correction from EDFM to not applied to permeability
+
+
+ Returns
+ -----------------
+ None
+
+ Authors
+ -----------------
+ Emily Stein (ergiamb@sandia.gov)
+ Applied Systems Analysis and Research, 8844
+ Sandia National Laboratories
+
+ Edited by Teresa Portone (tporton@sandia.gov) 11/2020 to take arguments.
+
+ Rosie Leone
+
+ Jeffrey Hyman 07/2023 - Integration with pydfnWorks
+
+ Notes
+ -----------------
+
+
+ """
+ print("\n")
+ print('='*80)
+ print("* Starting MAPDFN - ECPM")
+ print('='*80)
+
+ # setup the domain
+ filenames=setup_output_dir(output_dir,self.jobname)
+ origin,nx,ny,nz,num_cells=setup_domain(self.domain,cell_size)
+
+ # id cells that intersect the DFN
+ cell_fracture_id=self.mapdfn_tag_cells(origin,num_cells,nx,ny,nz,
+ cell_size)
+
+ porosity,k_iso,k_aniso=self.mapdfn_upscale(num_cells,cell_fracture_id,
+ cell_size,matrix_porosity,
+ matrix_perm,
+ lump_diag_terms,
+ correction_factor)
+
+ # write evereything to files
+ write_h5_files(filenames,nx,ny,nz,cell_size,cell_fracture_id,k_iso,
+ k_aniso,porosity,matrix_perm,tortuosity_factor,matrix_on)
+
+ print('='*80)
+ print("* MAPDFN Complete")
+ print('='*80)
+ print("\n")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mesh_dfn/mesh_dfn.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mesh_dfn/mesh_dfn.html
new file mode 100644
index 000000000..8ae7c4f1a
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mesh_dfn/mesh_dfn.html
@@ -0,0 +1,290 @@
+
+
+
+
+
+ pydfnworks.dfnGen.meshing.mesh_dfn.mesh_dfn — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[docs]
+defmesh_network(self,
+ uniform_mesh=False,
+ min_dist=0.5,
+ max_dist=10,
+ max_resolution_factor=10,
+ well=False,
+ cleanup=True,
+ strict=True,
+ quiet=True):
+"""
+ Mesh fracture network using LaGriT
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+ uniform_mesh : bool
+ toggle for uniform or variable mesh. Default : False
+ min_dist : float
+ Defines the minimum distance from the intersections with resolution h/2. This value is the factor of h, distance = min_dist * h
+ max_dist : float
+ Defines the minimum distance from the intersections with resolution max_resolution * h. This value is the factor of h, distance = max_dist * h
+ max_resolution_factor : float
+ Maximum factor of the mesh resolultion (max_resolution *h). Depending on the slope of the linear function and size of the fracture, this may not be realized in the mesh.
+ cleanup : bool
+ toggle to clean up directory (remove meshing files after a run). Default : True
+ strict : bool
+ Toggle if a few mesh errors are acceptable. default is true
+ quiet : bool
+ Toggle to turn on/off verbose information to screen about meshing. Default is true, does not print to screen
+
+ Returns
+ -------
+ None
+
+ Notes
+ ------
+ 1. All fractures in self.prune_file must intersect at least 1 other fracture
+
+ """
+
+ print('='*80)
+ print("Meshing DFN using LaGriT : Starting")
+ print('='*80)
+ tic=timeit.default_timer()
+
+ mh.setup_meshing_directory()
+
+ ######## Pruning scripts
+ ifself.prune_file:
+ print(
+ f"Loading list of fractures to remain in network from {self.prune_file}"
+ )
+ self.fracture_list=sort(genfromtxt(self.prune_file).astype(int))
+ print("--> Retaining Fractures: ")
+ print(self.fracture_list)
+ print("\n")
+ ifself.path:
+ self.create_mesh_links(self.path)
+ else:
+ hf.print_error(
+ "User requested pruning in meshing but did not provide path for main files."
+ )
+
+ ifnotself.visual_mode:
+ self.edit_intersection_files()
+ ######## Pruning scripts
+ else:
+ self.fracture_list=range(1,self.num_frac+1)
+
+ ifwell:
+ add_well_points_to_line_of_intersection()
+
+ slope,intercept=mh.compute_mesh_slope_and_intercept(
+ self.h,min_dist,max_dist,max_resolution_factor,uniform_mesh)
+ digits=len(str(self.num_frac))
+ ## Create user resolution function
+ print("--> Creating scripts for LaGriT meshing")
+ lg.create_poisson_user_function_script()
+ ## make driver files for each function
+ forindex,frac_idinenumerate(self.fracture_list):
+ self.create_lagrit_parameters_file(frac_id,index+1,digits,slope,
+ intercept,max_resolution_factor)
+ ## Make viz script or regular script
+ ifself.visual_mode:
+ lg.create_lagrit_reduced_mesh_script(frac_id,digits)
+ else:
+ lg.create_lagrit_poisson_script(frac_id,digits)
+
+ print("--> Creating scripts for LaGriT meshing: complete")
+
+ # ##### FOR SERIAL DEBUG ######
+ # for frac_id in self.fracture_list:
+ # _, msg = run_mesh.mesh_fracture(frac_id, self.visual_mode, self.num_frac)
+ # if msg < 0:
+ # error = f"Fracture {frac_id} failed to mesh properly.\nMsg {msg}.\nExiting Program\n"
+ # sys.stderr.write(error)
+ # sys.exit(msg)
+ # # ##### FOR SERIAL DEBUG ######
+
+ # ### Parallel runs
+ # if there are more processors than fractures,
+ ifself.ncpu>self.num_frac:
+ hf.print_warning(
+ "More processors than fractures requested.\nResetting ncpu to num_frac"
+ )
+ self.ncpu=self.num_frac
+
+ ifself.mesh_fractures_header(quiet):
+ hf.print_error("One or more fractures failed to mesh properly.")
+
+ # ### Parallel runs
+ self.merge_network()
+
+ ## checking and clean up
+ if(notself.visual_modeandnotself.prune_fileandnotself.r_fram):
+ ifnotmh.check_dudded_points(self.dudded_points):
+ mh.cleanup_meshing_files()
+ ifstrict:
+ hf.print_error("Incorrect Number of dudded points removed.")
+
+ ifnotself.visual_mode:
+ lgs.define_zones()
+
+ self.gather_mesh_information()
+
+ ifself.prune_file:
+ self.clean_up_files_after_prune()
+
+ ifcleanup:
+ mh.cleanup_meshing_files()
+
+ elapsed=timeit.default_timer()-tic
+ time_sec=elapsed
+ time_min=elapsed/60
+ time_hrs=elapsed/3600
+
+ print("--> Total Time to Mesh Network:")
+ print(
+ f"--> {time_sec:.2e} seconds\t{time_min:.2e} minutes\t{time_hrs:.2e} hours"
+ )
+ print()
+ print('='*80)
+ print("Meshing DFN using LaGriT : Complete")
+ print('='*80)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mesh_dfn/mesh_dfn_helper.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mesh_dfn/mesh_dfn_helper.html
new file mode 100644
index 000000000..acd732b12
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/mesh_dfn/mesh_dfn_helper.html
@@ -0,0 +1,582 @@
+
+
+
+
+
+ pydfnworks.dfnGen.meshing.mesh_dfn.mesh_dfn_helper — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.meshing.mesh_dfn.mesh_dfn_helper
+"""
+.. module:: mesh_dfn_helper.py
+ :synopsis: helper functions for meshing DFN using LaGriT
+.. moduleauthor:: Jeffrey Hyman <jhyman@lanl.gov>
+
+"""
+importos
+importsys
+importglob
+importshutil
+importnumpyasnp
+importsubprocess
+importpyvtkaspv
+frompydfnworks.generalimporthelper_functionsashf
+
+
+defcheck_dudded_points(dudded,hard=False):
+"""Parses LaGrit log_merge_all.out and checks if number of dudded points is the expected number
+
+ Parameters
+ ---------
+ dudded : int
+ Expected number of dudded points from params.txt
+ hard : bool
+ If hard is false, up to 1% of nodes in the mesh can be missed. If hard is True, no points can be missed.
+ Returns
+ ---------
+ True/False : bool
+ True if the number of dudded points is correct and False if the number of dudded points is incorrect
+
+ Notes
+ -----
+ If number of dudded points is incorrect by over 1%, program will exit.
+ """
+
+ print("--> Checking that number of dudded points is correct\n")
+ withopen("lagrit_logs/log_merge_all.out",encoding='latin-1')asfp:
+ forlineinfp.readlines():
+ if'Dudding'inline:
+ print(f'--> From LaGriT: {line}')
+ try:
+ pts=int(line.split()[1])
+ except:
+ pts=int(line.split()[-1])
+ if'RMPOINT:'inline:
+ print(f'--> From LaGriT: {line}')
+ total_points=int(line.split()[-1])
+ break
+
+ diff=abs(dudded-pts)
+ print(f"--> Expected Number of dudded points: {dudded}")
+ print(f"--> Actual Number of dudded points: {pts}")
+ print(f"--> Difference between expected and actual dudded points: {diff}")
+ ifdiff==0:
+ print('--> The correct number of points were removed. Onward!\n')
+ returnTrue
+ elifdiff>0:
+ hf.print_warning(
+ 'Number of points removed does not match the expected value')
+ ## compare with total number poins
+ diff_ratio=100*(float(diff)/float(total_points))
+ ifdiff_ratio<0.01andhard==False:
+ print(f"--> However value is small: {diff}")
+ print("--> Proceeding\n")
+ returnTrue
+ else:
+ hf.print_warning(
+ f"Incorrect Number of points removed\nOver 0.01% of nodes removed. Value is {diff_ratio:.2f}"
+ )
+ returnFalse
+
+
+defgather_mesh_information(self):
+""" Prints information about the final mesh to file
+
+ Parameters
+ ----------
+ local_jobname : string
+ Name of current DFN job (not path)
+ visual_mode : bool
+ Determines is reduced_mesh or full_mesh is dumped
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+"""
+
+ ifself.visual_mode:
+ withopen('reduced_mesh.inp','r')asfinp:
+ header=finp.readline()
+ header=header.split()
+ self.num_nodes=int(header[0])
+ print(
+ f"--> The reduced mesh in reduced_mesh.inp has {self.num_nodes} nodes and {int(header[1])} triangular elements"
+ )
+ else:
+ withopen('full_mesh.inp','r')asfinp:
+ header=finp.readline()
+ header=header.split()
+ self.num_nodes=int(header[0])
+ print(
+ f"--> The primary mesh in full_mesh.inp has {self.num_nodes} nodes and {int(header[1])} triangular elements"
+ )
+ ## get material -ids
+ self.material_ids=np.genfromtxt('materialid.dat',
+ skip_header=3).astype(int)
+ self.aperture_cell=np.zeros(self.num_nodes)
+ self.perm_cell=np.zeros(self.num_nodes)
+
+
+
+[docs]
+defcreate_mesh_links(self,path):
+''' Makes symlinks for files in path required for meshing
+
+ Parameters
+ ----------
+ self : DFN object
+ path : string
+ Path to where meshing files are located
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+
+ '''
+ importos.path
+ fromshutilimportrmtree
+ print(f"--> Creating links for meshing from {path}")
+ files=[
+ 'params.txt',
+ 'poly_info.dat',
+ 'polys',
+ 'intersections',
+ 'dfnGen_output/connectivity.dat',
+ 'dfnGen_output/left.dat',
+ 'dfnGen_output/right.dat',
+ 'dfnGen_output/front.dat',
+ 'dfnGen_output/back.dat',
+ 'dfnGen_output/top.dat',
+ 'dfnGen_output/fracture_info.dat',
+ 'dfnGen_output/intersection_list.dat',
+ 'dfnGen_output/bottom.dat',
+ ]
+ forfilenameinfiles:
+ ifos.path.isfile(filename)oros.path.isdir(filename):
+ print(f"Removing {filename}")
+ try:
+ rmtree(filename)
+ except:
+ hf.print_warning(f"Unable to remove {filename}")
+ try:
+ os.symlink(path+filename,filename)
+ except:
+ hf.print_warning(f"Unable to make link for {filename}")
+ pass
+ print("--> Complete")
+
+
+
+
+[docs]
+definp2gmv(self,inp_file=None):
+""" Convert inp file to gmv file, for general mesh viewer. Name of output file for base.inp is base.gmv
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+ inp_file : str
+ Name of inp file if not an attribure of self
+
+ Returns
+ ----------
+ None
+
+ Notes
+ ---------
+ """
+
+ ifinp_file:
+ self.inp_file=inp_file
+ else:
+ inp_file=self.inp_file
+
+ ifnotinp_file:
+ hf.print_error('inp file must be specified in inp2gmv')
+
+ gmv_file=inp_file[:-4]+'.gmv'
+
+ withopen('inp2gmv.lgi','w')asfid:
+ fid.write(f'read / avs / {inp_file} / mo\n')
+ fid.write(f'dump / gmv / {gmv_file} / mo\n')
+ fid.write('finish \n\n')
+
+ failure=run_lagrit_script('inp2gmv.lgi')
+
+ iffailure:
+ hf.print_error('Failed to run LaGrit to get gmv from inp file.')
+ print("--> Finished writing gmv format from avs format")
+[docs]
+defrun_lagrit_script(lagrit_file,output_file=None,quiet=False):
+"""
+ Runs LaGriT
+
+ Parameters
+ -----------
+ ----------
+ lagrit_file : string
+ Name of LaGriT script to run
+ output_file : string
+ Name of file to dump LaGriT output
+ quiet : bool
+ If false, information will be printed to screen.
+
+ Returns
+ ----------
+ failure: int
+ If the run was successful, then 0 is returned.
+
+ """
+ ifoutput_file==None:
+ cmd=f"{os.environ['LAGRIT_EXE']} < {lagrit_file} -log {lagrit_file}.log -out {lagrit_file}.out"
+ else:
+ cmd=f"{os.environ['LAGRIT_EXE']} < {lagrit_file} -log {output_file}.log -out {output_file}.out > {output_file}.dump"
+ ifnotquiet:
+ print(f"--> Running: {cmd}")
+ failure=subprocess.call(cmd,shell=True)
+ iffailure:
+ hf.print_error(f"LaGriT script {lagrit_file} failed to run properly")
+ else:
+ ifnotquiet:
+ print(f"--> LaGriT script {lagrit_file} ran successfully")
+ returnfailure
+
+
+
+defsetup_meshing_directory():
+
+ dirs=["lagrit_scripts","lagrit_logs"]
+ fordindirs:
+ try:
+ ifos.path.isdir(d):
+ shutil.rmtree(d)
+ os.mkdir(d)
+ except:
+ hf.print(f"Unable to make directory {d}")
+
+
+defcleanup_meshing_files():
+""" Removes mesh files and directories
+
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ Only runs if cleanup is true
+ """
+ print("\n--> Cleaning up directory after meshing")
+ batch_files_to_remove=[
+ 'part*','log_merge*','merge*','mesh_poly_CPU*','mesh*inp',
+ 'mesh*lg'
+ ]
+ forfilesinbatch_files_to_remove:
+ forflinglob.glob(files):
+ os.remove(fl)
+
+ dirs_to_remove=['lagrit_scripts','lagrit_logs']
+ fordindirs_to_remove:
+ try:
+ ifos.path.isdir(d):
+ shutil.rmtree(d)
+ except:
+ hf.print_error(f"Unable to remove directory {d}")
+
+ files_to_remove=['user_resolution.mlgi']
+ forfilenameinfiles_to_remove:
+ try:
+ ifos.path.isfile(filename):
+ os.remove(filename)
+ except:
+ hf.print_error(f"Unable to remove file {filename}")
+ print("--> Cleaning up directory after meshing complete")
+
+
+defcompute_mesh_slope_and_intercept(h,min_dist,max_dist,
+ max_resolution_factor,uniform_mesh):
+""" computes the slope and intercept of the meshing resolution. The mesh resolution is a piecewise constant and linear function of the distance (d) from the intersection.
+
+
+ if 0 < d < x0*h, then r(d) = h/2
+ if x0*h <= d <= x1*h then r(d) = m * d + b
+ if d < x1 then r(d) = max_resolution_factor*h
+
+ Note that x0 and x1 are factors of h, not spatial units of Length.
+
+ Parameters
+ -------------------
+ h : float
+ FRAM h scale. Mesh resolution along intersections is h/2
+ min_dist : float
+ Defines the minimum distance from the intersections with resolution h/2. This value is the factor of h, distance = min_dist * h
+ max_dist : float
+ Defines the minimum distance from the intersections with resolution max_resolution * h. This value is the factor of h, distance = max_dist * h
+ max_resolution_factor : float
+ Maximum factor of the mesh resolultion (max_resolution *h). Depending on the slope of the linear function and size of the fracture, this may not be realized in the mesh.
+ uniform_mesh : bool
+ Boolean for uniform mesh resolution
+
+ Returns
+ -------------------
+ slope : float
+ slope of the linear function of the mesh resolution
+ intercept : float
+ Intercept of the linear function of the mesh resolution
+
+
+ Notes
+ -------------------
+
+
+ """
+ print("--> Computing mesh resolution function")
+ ifuniform_mesh:
+ print("--> Uniform Mesh Resolution Selected")
+ print("*** Mesh resolution ***")
+ print(f"\tr(d) = {0.5*h}\n")
+ slope=0
+ intercept=0.5*h
+ else:
+ print("--> Variable Mesh Resolution Selected")
+ print(
+ f"*** Minimum distance [m] from intersection with constant resolution h/2 : {min_dist*h}"
+ )
+ print(
+ f"*** Maximum distance [m] from intersection variable resolution : {max_dist*h}"
+ )
+ print(
+ f"*** Upper bound on resolution [m] : {max_resolution_factor*h:0.2f}\n"
+ )
+ ## do some algebra to figure out the slope and intercept
+ ifmin_dist>=max_dist:
+ hf.print_error(
+ f"min_dist greater than or equal to max_dist.\nmin_dist : {min_dist}\nmax_dist : {max_dist}"
+ )
+ slope=h*(max_resolution_factor-0.5)/(max_dist-min_dist)
+ ifslope>1:
+ hf.print_warning(
+ f"Meshing slope too large. {slope} > 1. Resetting to 0.9")
+ slope=0.9
+
+ intercept=h*(0.5-slope*min_dist)
+
+ print("*** Meshing function : ")
+ x0=(0.5*h-intercept)/(slope*h)
+ x1=(max_resolution_factor*h-intercept)/(slope*h)
+ print(f"\tr(d) = {0.5*h:0.2f}\t\t\tfor 0 < d < {x0:0.2f}")
+ ifintercept>0:
+ print(
+ f"\tr(d) = {slope:0.2f} * d + {intercept:0.2f}\t\tfor {x0:0.2f} <= d <= {x1:0.2f} "
+ )
+ else:
+ print(
+ f"\tr(d) = {slope:0.2f} * d {intercept:0.2f}\t\tfor {x0:0.2f} < d < {x1:0.2f} "
+ )
+ print(
+ f"\tr(d) = {max_resolution_factor*h:0.2f}\t\t\tfor {x1:0.2f} <= d"
+ )
+ print("--> Computing mesh resolution function : complete \n")
+ returnslope,intercept
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/udfm/false_connections.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/udfm/false_connections.html
new file mode 100644
index 000000000..194781f30
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/udfm/false_connections.html
@@ -0,0 +1,226 @@
+
+
+
+
+
+ pydfnworks.dfnGen.meshing.udfm.false_connections — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGen.meshing.udfm.false_connections
+"""
+.. module:: false_connections.py
+ :synopsis: Checks for false connections between fractures in upscaled mesh
+.. moduleauthor:: Jeffrey Hyman <jhyman@lanl.gov>
+
+"""
+
+importpickle
+importos
+
+
+
+[docs]
+defcheck_false_connections(self,path="../"):
+"""
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+ fmc_filname : string
+ name of the pickled dictionary of mesh and fracture intersections
+
+ Returns
+ -------
+ num_false_connections : int
+ number of false connections
+ num_cell_false : int
+ number of Voronoi cells with false connections
+ false_connections : list
+ list of tuples of false connections created by upscaling
+
+ Notes
+ -----
+ map2continuum and upscale must be run first to create the fracture/mesh intersection
+ dictionary. Thus must be run in the main job directory which contains connectivity.dat
+
+
+ """
+ print("--> Checking for false connections in the upscaled mesh.")
+ # Create symbolic links to create fracture graph
+ files=["connectivity.dat","left.dat","right.dat","fracture_info.dat"]
+ forfinfiles:
+ try:
+ os.symlink(path+f,f)
+ except:
+ print(f"--> Warning!!! Unable to make symbolic link to {path+f}")
+ pass
+
+ # create fracture graph, with arbitrary source/target
+ G=self.create_graph("fracture","left","right")
+ # remove source and target
+ G.remove_node("s")
+ G.remove_node("t")
+
+ # Make a copy of G and remove all edges
+ H=G.copy()
+ foru,vinH.edges():
+ H.remove_edge(u,v)
+
+ # load the fracture_mesh_connection dictionary
+ print("--> Loading mesh intersection information")
+ fmc=pickle.load(open("connections.p","rb"))
+ print("--> Complete")
+ # Get cell ids for the cells that fractures intersect
+ cells=[keyforkeyinfmc.keys()]
+
+ # walk through the cells and add edges to graph H
+ # if two fractures are in the same cell
+ cell_false=[False]*len(cells)
+ fori,cellinenumerate(cells):
+ num_conn=len(fmc[cell])
+ # If more than one fracture intersects the mesh cell
+ # add edges
+ ifnum_conn>1:
+ # add edges between all fractures in a cell
+ forjinrange(num_conn):
+ id1=fmc[cell][j][0]
+ forkinrange(j+1,num_conn):
+ id2=fmc[cell][k][0]
+ H.add_edge(id1,id2)
+ cell_false[i]=True
+
+ ## check for false connections
+ print("--> Checking for false connections")
+ false_connections=[]
+ foru,v,inH.edges():
+ ifnotG.has_edge(u,v):
+ print(f"--> False connection between fractures {u} and {v}")
+ false_connections.append((u,v))
+
+ iflen(false_connections)>0:
+ num_false_connections=len(false_connections)
+ print(
+ f"--> There are {num_false_connections} false connections between fractures"
+ )
+ num_false_cells=sum(cell_false)
+ print(f"--> These occur in {num_false_cells} Voronoi cells")
+ else:
+ print(f"--> No false connections found")
+ num_false_cells=0
+ num_false_connections=0
+
+ return(num_false_connections,num_false_cells,false_connections)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/udfm/map2continuum.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/udfm/map2continuum.html
new file mode 100644
index 000000000..433321272
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGen/meshing/udfm/map2continuum.html
@@ -0,0 +1,1178 @@
+
+
+
+
+
+ pydfnworks.dfnGen.meshing.udfm.map2continuum — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[docs]
+deftag_well_in_mesh(self,wells):
+""" Identifies nodes in a DFN for nodes the intersect a well with radius r [m]\n
+ 1. Well coordinates in well["filename"] are converted to a polyline that are written into "well_{well['name']}_line.inp"\n
+ 2. Well is expanded to a volume with radius well["r"] and written into the avs file well_{well["name"]}_volume.inp\n
+ 3. Nodes in the DFN that intersect with the well are written into the zone file well_{well["name"]}.zone\n
+ 4. If using PFLOTRAN, then an ex file is created from the well zone file\n
+
+ Parameters
+ -----------
+ self : object
+ DFN Class
+ well: Dictionary
+ Dictionary of information about the well that contains the following attributes
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates with the following format
+ x0 y0 z0\n
+ x1 y1 z1\n
+ ...\n
+ xn yn zn\n
+
+ well["r"] : float
+ radius of the well
+ Returns
+ --------
+ None
+ Notes
+ --------
+ Wells can be a list of well dictionaries
+
+ """
+
+ iftype(wells)isdict:
+ well=wells
+ print(
+ f"\n\n--> Identifying nodes in the DFN intersecting with a vertical well named {well['name']}."
+ )
+
+ # 1) convert well into polyline AVS if it doesn't exist
+ ifnotos.path.isfile(f"well_{well['name']}_line.inp"):
+ convert_well_to_polyline_avs(well)
+
+ # 2) expand the polyline of the well into a volume with radius r
+ self.expand_well(well)
+
+ # 3) find the nodes in the well that corresponds / intersect the well
+ get_well_zone(well,self.inp_file)
+
+ # 4) convert the zone file to ex files for PFLTORAN
+ ifself.flow_solver=="PFLOTRAN":
+ self.zone2ex(zone_file=f"well_{well['name']}.zone",face='well')
+
+ ifself.flow_solver=="FEHM":
+ print(f"--> Well nodes are in well_{well['name']}.zone")
+
+ print(f"--> Well creation for {well['name']} complete\n\n")
+
+ iftype(wells)islist:
+ forwellinwells:
+ print(
+ f"\n\n--> Identifying nodes in the DFN intersecting with a vertical well named {well['name']}."
+ )
+
+ # 1) convert well into polyline AVS if it doesn't exist
+ ifnotos.path.isfile(f"well_{well['name']}_line.inp"):
+ convert_well_to_polyline_avs(well)
+
+ # 2) expand the polyline of the well into a volume with radius r
+ self.expand_well(well)
+
+ # 3) find the nodes in the well that corresponds / intersect the well
+ get_well_zone(well,self.inp_file)
+
+ # 4) convert the zone file to ex files for PFLTORAN
+ ifself.flow_solver=="PFLOTRAN":
+ self.zone2ex(zone_file=f"well_{well['name']}.zone",
+ face='well')
+
+ ifself.flow_solver=="FEHM":
+ print(f"--> Well nodes are in well_{well['name']}.zone")
+
+ print(f"--> Well creation for {well['name']} complete\n\n")
+
+
+
+defconvert_well_to_polyline_avs(well,radius):
+""" Identifies converts well coordinates into a polyline avs file. Distance between
+ point on the polyline are h/2 apart. Polyline is written into "well_{well['name']}_line.inp"
+
+ Parameters
+ -----------
+ well: dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ Format is :
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+
+ h : float
+ h parameter for meshing.
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ If flow solver is set to PFLOTRAN, the zone file dumped by LaGriT will be converted to
+ an ex file.
+
+ """
+ print("--> Interpolating well coordinates into a polyline")
+
+
+ # read in well coordinates
+ print(well['filename'])
+ pts=np.genfromtxt(f"{well['filename']}")
+ n,_=np.shape(pts)
+
+ # Linear interpolation of well into a polyline
+ new_pts=[]
+ new_pts.append(pts[0])
+ new_idx=0
+
+ foriinrange(1,n):
+ distance=np.linalg.norm(pts[i,:]-pts[i-1,:])
+ ifdistance<radius:
+ new_pts.append(pts[i,:])
+ new_idx+=1
+ else:
+ # discretized to less than h
+ m=int(np.ceil(distance/radius))
+ dx=(pts[i,0]-pts[i-1,0])/m
+ dy=(pts[i,1]-pts[i-1,1])/m
+ dz=(pts[i,2]-pts[i-1,2])/m
+ forjinrange(m):
+ interp=np.zeros(3)
+ interp[0]=new_pts[new_idx][0]+dx
+ interp[1]=new_pts[new_idx][1]+dy
+ interp[2]=new_pts[new_idx][2]+dz
+ new_pts.append(interp)
+ delinterp
+ new_idx+=1
+
+ print("--> Interpolating well coordinates into a polyline: Complete")
+ # Write interpolated polyline into an AVS file
+ avs_filename=f"well_{well['name']}_line.inp"
+ print(f"--> Writing polyline into avs file : {avs_filename}")
+
+ num_pts=new_idx+1
+ pt_digits=len(str(num_pts))
+
+ num_elem=new_idx
+ elem_digits=len(str(num_elem))
+
+ favs=open(avs_filename,"w")
+ favs.write(f"{num_pts}\t{num_elem}\t0\t0\t0\n")
+ foriinrange(num_pts):
+ favs.write(
+ f"{i+1:0{pt_digits}d}{new_pts[i][0]:12e}{new_pts[i][1]:12e}{new_pts[i][2]:12e}\n"
+ )
+ foriinrange(num_elem):
+ favs.write(f"{i+1} 1 line {i+1}{i+2}\n")
+ favs.close()
+ print(f"--> Writing polyline into avs file : {avs_filename} : Complete")
+
+
+defexpand_well(self,well):
+""" Expands the polyline defining the well into a volume with radius r [m].
+ A sphere of points around each point is created and then connected.
+ Volume is written into the avs file well_{well["name"]}_volume.inp
+
+ Parameters
+ -----------
+ well:
+ dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ Format is :
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ Mesh of the well is written into the avs file {well["name"][:-4]}.inp
+
+ """
+
+ print("--> Expanding well into a volume.")
+
+ r=well["r"]
+
+ convert_well_to_polyline_avs(well,r)
+
+ angle_r=r/np.sqrt(2)
+
+ lagrit_script=f"""
+ ## read in polyline of well
+ read / well_{well['name']}_line.inp / mo_line
+ cmo / printatt / mo_line / -xyz- / minmax
+
+ ## expand every point in the polyline into a discrete sphere
+ ## with radius r
+ cmo / create / mo_well / / / tet
+ copypts / mo_well / mo_line
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / {well["r"]} 0 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / -{well["r"]} 0 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 {well["r"]} 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 -{well["r"]} 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 0 {well["r"]}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 0 -{well["r"]}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / {angle_r}{angle_r} 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / {angle_r} -{angle_r} 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / -{angle_r}{angle_r} 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / -{angle_r} -{angle_r} 0
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 {angle_r}{angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 {angle_r} -{angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 -{angle_r}{angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / 0 -{angle_r} -{angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / {angle_r} 0 {angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / {angle_r} 0 -{angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / -{angle_r} 0 {angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ cmo / create / mo_tmp / / / line
+ copypts / mo_tmp / mo_line
+ cmo / select / mo_tmp
+ trans / 1 0 0 / 0. 0. 0. / -{angle_r} 0 -{angle_r}
+ copypts / mo_well / mo_tmp
+ cmo / delete / mo_tmp
+
+ ## Could add more permutations, but this looks good enough right now
+ ## JDH 9 Sept. 2020
+
+ ################## DEBUG ###########################
+ # dump / well_pts.inp / mo_well
+ ################## DEBUG ###########################
+
+ # filter out point that are too close
+ cmo / select / mo_well
+ filter / 1 0 0 / {0.1*r}
+ rmpoint / compress
+ cmo / setatt / mo_well / imt / 1 0 0 / 1
+
+ # connect the point cloud and make a volume mesh
+ connect / delaunay
+ resetpts / itp
+
+ ################## DEBUG ###########################
+ # dump / well_{well["name"]}.inp / mo_well
+ ################## DEBUG ###########################
+
+ # add edge_max attribute and remove elements with big edges
+ quality / edge_max / y
+ eltset /big/ edgemax / gt / {np.sqrt(2)*r}
+ cmo / setatt / mo_well / itetclr / eltset, get, big / 2
+ rmmat / 2
+ rmpoint / compress
+
+ dump / well_{well["name"]}_volume.inp / mo_well
+
+ ################## DEBUG ###########################
+ # # extract the surface of the well
+ # # This is done to remove internal points and reduce
+ # # the total number of elements in the mesh
+ # # This speeds up the intersection checking later on
+ # # I couldn't get this to work in a robust way.
+ # # There were some weird LaGriT errors if I deleted
+ # # mesh object.
+ # # Works if we stop before this, but I'm leaving it to
+ # # revisit if need be.
+ # # JDH 10/9/2020
+
+ # extract / surfmesh / 1,0,0 /mo_shell / mo_well
+ # cmo / select / mo_shell
+ # cmo / delete / mo_well
+
+ # ################## DEBUG ###########################
+ # dump / well_{well["name"]}_shell.inp / mo_shell
+ # ################## DEBUG ###########################
+
+ # # Copy the surface of the well into a tet mesh
+ # cmo / create / mo_well2 / / / tet
+ # copypts / mo_well2 / mo_shell
+ # cmo / select / mo_well2
+ # # cmo / delete / mo_shell
+
+ # # filter out point that are too close
+ # filter / 1 0 0 / {0.1*r}
+ # rmpoint / compress
+ # cmo / setatt / mo_well2 / imt / 1 0 0 / 1
+
+ # # connect the point cloud and make a volume mesh
+ # connect / delaunay
+ # resetpts / itp
+
+ # # add edge_max attribute and remove elements with big edges
+ # quality / edge_max / y
+ # eltset /big/ edgemax / gt / {np.sqrt(2)*r}
+ # cmo / setatt / mo_well2 / itetclr / eltset, get, big / 2
+ # rmmat / 2
+ # rmpoint / compress
+
+ # # write out final well mesh
+ # dump / well_{well["name"]}_volume.inp / mo_well2
+
+ finish
+
+ """
+ # Write LaGriT commands to file
+ withopen(f"expand_well_{well['name']}.lgi","w")asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+
+ # Execute LaGriT
+ mh.run_lagrit_script(f"expand_well_{well['name']}.lgi",
+ output_file=f"expand_well_{well['name']}",
+ quiet=False)
+
+ print("--> Expanding well complete.")
+
+
+defget_well_zone(well,inp_file):
+"""Identifies nodes in a DFN for nodes the intersect a well with radius r [m]
+ First, all elements that intersect the well are identified.
+ Second, all nodes of those elements are tagged.
+ Third, that collection of nodes are dumped as a zone file (well_{well["name"]}.zone)
+
+ Parameters
+ -----------
+ self : object
+ DFN Class
+ well:
+ dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ File format:
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ None
+
+ """
+ # # if the well has not been converted to AVS, do that first
+ # if not os.path.isfile(f"well_{well['name']}_line.inp"):
+ # convert_well_to_polyline_avs(well,h)
+ # # if the well has not been expanded
+ # if not os.path.isfile(f"well_{well['name']}_volume.inp"):
+ # expand_well(well)
+
+ lagrit_script=f"""
+
+# read in well volume
+read / well_{well["name"]}_volume.inp / mo_well
+
+# read in DFN
+read / {inp_file} / mo_dfn
+
+# find intersecting cells
+cmo / select / mo_dfn
+intersect_elements / mo_dfn / mo_well / well_{well["name"]}_inter
+
+eltset / ewell / well_{well["name"]}_inter / gt / 0
+
+# dump dfn mesh with intersections tagged
+#dump / avs / {inp_file[:-3]}_tagged.inp / mo_dfn
+
+# gather nodes of intersecting cells
+pset / well_{well["name"]} / eltset / ewell
+
+# dump nodes from intersecting cells
+pset / well_{well["name"]} / zone / well_{well["name"]}.zone
+
+finish
+
+"""
+ # Write LaGriT commands to file
+ withopen(f"get_well_{well['name']}_zone.lgi","w")asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+
+ # Execute LaGriT
+ mh.run_lagrit_script(f"get_well_{well['name']}_zone.lgi",
+ output_file=f"create_well_{well['name']}",
+ quiet=False)
+
+ withopen(f"well_{well['name']}.zone","r")asfp:
+ number_of_nodes=int(fp.readlines()[3])
+ ifnumber_of_nodes>0:
+ print(f"--> There are {number_of_nodes} nodes in the well zone")
+ else:
+ print("--> WARNING!!! The well did not intersect the DFN!!!")
+
+
+
+[docs]
+deffind_well_intersection_points(self,wells):
+""" Identifies points on a DFN where the well intersects the network.
+ These points are used in meshing the network to have higher resolution in the mesh in these points.
+ Calls a sub-routine run_find_well_intersection_points.
+
+ Parameters
+ -----------
+ self : object
+ DFN Class
+ well:
+ dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ Format is :
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ Wells can be a list of well dictionaries.
+ Calls the subroutine run_find_well_intersection_points to remove redundant code.
+
+ """
+ # check for reduced mesh, if it doesn't exists, make it
+ print("--> Checking for reduced_mesh.inp")
+ ifnotos.path.isfile("reduced_mesh.inp"):
+ print("--> reduced_mesh.inp not found. Creating it now.")
+ self.mesh_network()
+ else:
+ print("--> reduced_mesh.inp found. Moving on.")
+
+ # if using a single well
+ iftype(wells)isdict:
+ self.run_find_well_intersection_points(wells)
+ # using a list of wells, loop over them.
+ eliftype(wells)islist:
+ forwellinwells:
+ self.run_find_well_intersection_points(well)
+
+ # Run cross check
+ cross_check_pts(self.h)
+
+
+
+defrun_find_well_intersection_points(self,well):
+""" Runs the workflow for finding the point of intersection of the DFN with the well.
+
+ Parameters
+ -----------
+ well:
+ dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ Format is :
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+ h : float
+ Minimum h length scale in the network
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ This function was designed to minimize redundancy in the workflow. It's called
+ within find_well_intersection_points()
+ """
+
+ print(f"\n\n--> Working on well {well['name']}")
+
+ # 1) convert well into polyline AVS
+ ifnotos.path.isfile(f"well_{well['name']}_line.inp"):
+ convert_well_to_polyline_avs(well,self.h)
+
+ # run LaGriT scripts to dump information
+ find_segments(well)
+
+ self.well_point_of_intersection(well)
+
+
+deffind_segments(well):
+""" LaGriT script to identify the points of intersection between the DFN and the well.
+
+ Parameters
+ -----------
+ well:
+ dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ Format is :
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ points of intersection are written into the avs file well_{well['name']}_intersect.inp
+
+ OUTPUT: well_segments_intersect.inp is a subset of the well line segments that intersect fractures.
+ The segments are tagged so itetclr and imt are set to the value of the fracture they intersect.
+ """
+
+ lagrit_script=f"""
+#
+# OUTPUT: intersected_fracture.list tells you the list of fractures intersected by the well.
+# OUTPUT: well_segments_intersect.inp is a subset of the well line segments that intersect fractures.
+# The segments are tagged so itetclr and imt are set to the value of the fracture they intersect.
+#
+define / INPUT_DFN / reduced_mesh.inp
+define / INPUT_WELL / well_{well['name']}_line.inp
+define / OUTPUT_WELL_SEGMENTS / well_{well['name']}_intersect.inp
+# define / OUTPUT_FRACTURE_LIST / {well['name']}_fracture.list
+#
+read / avs / INPUT_DFN / mo_tri
+read / avs / INPUT_WELL / mo_line
+#
+# Find the triangles of the DFN mesh that intersect the well lines.
+# Get rid of all the non-intersecting triangles.
+#
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+#
+# dump / avs / reduced_reduced_mesh.inp / mo_tri
+cmo / addatt / mo_tri / id_fracture / vint / scalar / nelements
+cmo / copyatt / mo_tri / mo_tri / id_fracture / itetclr
+# dump / avs / OUTPUT_FRACTURE_LIST / mo_tri / 0 0 0 1
+#
+# Find the segments of the well (line object) that intersect the fracture planes (triangles)
+#
+intersect_elements / mo_line / mo_tri / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_line / if_intersect
+# BEGIN DEBUG
+# dump / avs / OUTPUT_WELL_SEGMENTS / mo_line
+# END DEBUG
+#
+# Reduce the size of the triangles so interpolation works.
+#
+cmo / select / mo_tri
+# Refine 2**7 128
+refine2d
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+
+refine2d
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+
+refine2d
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+
+refine2d
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+
+refine2d
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+
+refine2d
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+
+refine2d
+intersect_elements / mo_tri / mo_line / if_intersect
+eltset / e_not_intersect / if_intersect / eq / 0
+rmpoint / element / eltset get e_not_intersect
+rmpoint / compress
+cmo / DELATT / mo_tri / if_intersect
+
+# BEGIN DEBUG
+# dump / avs / tmp_refine.inp / mo_tri
+# END DEBUG
+
+interpolate / voronoi / mo_line itetclr / 1 0 0 / mo_tri imt
+interpolate / voronoi / mo_line imt / 1 0 0 / mo_tri imt
+
+cmo / modatt / mo_line / itp / ioflag / l
+cmo / modatt / mo_line / icr / ioflag / l
+cmo / modatt / mo_line / isn / ioflag / l
+
+dump / avs / OUTPUT_WELL_SEGMENTS / mo_line
+
+finish
+
+
+"""
+ # Write LaGriT commands to file
+ withopen(f"find_well_{well['name']}_segment.lgi","w")asfp:
+ fp.write(lagrit_script)
+ fp.flush()
+
+ # Execute LaGriT
+ mh.run_lagrit_script(f"find_well_{well['name']}_segment.lgi",
+ output_file=f"find_well_{well['name']}_segment",
+ quiet=False)
+
+
+defwell_point_of_intersection(self,well):
+""" Takes the well points found using find_segments and projects the points onto the fracture plane. These points are written into well_points.dat file. During meshing, these points are read in and a higher resolution mesh is created near by them. well_points.dat has the format
+
+ fracture_id x y z
+ ...
+
+ for every intersection point.
+
+ Parameters
+ -----------
+ well:
+ dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ Format is :
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+
+ """
+
+ print(f"--> Finding well points on DFN for {well['name']}")
+ # create file to keep well points if it doesn't exist. Otherwise set to append.
+ ifnotos.path.isfile("well_points.dat"):
+ fwell=open("well_points.dat","w")
+ fwell.write("fracture_id x y z\n")
+ else:
+ fwell=open("well_points.dat","a")
+
+ well_line_file=f"well_{well['name']}_intersect.inp"
+
+ pts,elems,fracture_list=get_segments(well_line_file)
+
+ iflen(fracture_list)==0:
+ print(
+ f"\n--> Warning. The well {well['name']} did not intersect the DFN!!!\n"
+ )
+
+ points=self.gather_points()
+ foreleminelems:# Parameterize the line center of the well
+ l0=np.zeros(3)
+ l0[0]=pts[elem["pt1"]-1]["x"]
+ l0[1]=pts[elem["pt1"]-1]["y"]
+ l0[2]=pts[elem["pt1"]-1]["z"]
+ l1=np.zeros(3)
+ l1[0]=pts[elem["pt2"]-1]["x"]
+ l1[1]=pts[elem["pt2"]-1]["y"]
+ l1[2]=pts[elem["pt2"]-1]["z"]
+ l=l1-l0
+
+ fracture_id=elem["frac"]
+ # get the plane on which the fracture lies
+ n=self.normal_vectors[fracture_id-1,:]
+ p0=points[fracture_id-1,:]
+ # p0 = get_center(fracture_id)
+ R=rotation_matrix(n,[0,0,1])
+
+ # find the point of intersection between the well line and the plane
+ d=np.dot((p0-l0),n)/(np.dot(l,n))
+ p=l0+l*d
+ v=rotate_point(p,R)
+ fwell.write(f"{fracture_id}{v[0]}{v[1]}{v[2]}\n")
+ fwell.close()
+
+
+defcross_check_pts(h):
+""" Sometimes multiple points of intersection are identified on the same fracture. This can occur if the discretized well has points close to the fracture plane. This function walks through well_points.dat and removes duplicate points that are within h of one another and on the same fracture plane.
+
+
+ Parameters
+ -----------
+ h : float
+ Minimum length scale in the network.
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ None
+ """
+
+ print("\n--> Cross Checking well points")
+ pts=np.genfromtxt("well_points.dat",skip_header=1)
+ num_pts,_=np.shape(pts)
+
+ # Walk through well points and see if they are too close together,
+ # This can happen due to machine precision in LaGriT.
+ # We only keep 1 point per fracture per well.
+ remove_idx=[]
+ foriinrange(num_pts):
+ fracture_number=pts[i,0]
+ forjinrange(i+1,num_pts):
+ # Check if points are on the same fracture to reduce number of distance checks
+ iffracture_number==pts[j,0]:
+ dist=abs(pts[i,1]-pts[j,1])+abs(
+ pts[i,2]-pts[j,2])+abs(pts[i,3]-pts[j,3])
+ # if the points are closure that h/2, mark one to be removed.
+ ifdist<h/2:
+ remove_idx.append(j)
+
+ # Find the id of those points to keep
+ keep_pt_indes=list(set(range(num_pts))-set(remove_idx))
+ # keep only those points
+ pts=pts[keep_pt_indes]
+ num_pts=len(pts)
+ # write to file
+ # os.remove("well_points.dat")
+ withopen("well_points.dat","w")asfwell:
+ fwell.write("fracture_id x y z\n")
+ foriinrange(num_pts):
+ fwell.write(f"{int(pts[i,0])}{pts[i,1]}{pts[i,2]}{pts[i,3]}\n")
+ print("--> Cross Checking Complete")
+
+
+defget_segments(well_line_file):
+""" Parses well_line_file (avs) to get point information, element information, and list of fractures that intersect the well.
+
+ Parameters
+ -----------
+ well_line_file : string
+ filename of well_line_file written by find_segments()
+
+ Returns
+ --------
+ pts : list
+ list of dictionaries of intersection points. dictionary contains fracture id and x,y,z coordinates
+ elems: list of dictionaries
+ Information about elements of the discretized well that intersect the DFN
+ fracture_list : list
+ list of fractures that the well intersects
+
+ Notes
+ --------
+ None
+ """
+
+ withopen(well_line_file,"r")asfp:
+ header=fp.readline().split()
+ num_pts=int(header[0])
+ num_elem=int(header[1])
+ pts=[]
+ foriinrange(num_pts):
+ pt=fp.readline().split()
+ tmp={"id":None,"x":None,"y":None,"z":None}
+ tmp["id"]=int(pt[0])
+ tmp["x"]=float(pt[1])
+ tmp["y"]=float(pt[2])
+ tmp["z"]=float(pt[3])
+ pts.append(tmp)
+ elems=[]
+ foriinrange(num_elem):
+ elem=fp.readline().split()
+ tmp={"pt1":None,"pt2":None,"frac":None}
+ tmp["pt1"]=int(elem[3])
+ tmp["pt2"]=int(elem[4])
+ tmp["frac"]=int(elem[1])
+ elems.append(tmp)
+ # get fracture list
+ fracture_list=[]
+ foriinrange(num_elem):
+ ifnotelems[i]["frac"]infracture_list:
+ fracture_list.append(elems[i]["frac"])
+
+ returnpts,elems,fracture_list
+
+
+# def get_normal(self, fracture_id):
+# """ Returns Normal vector of a fracture
+
+# Parameters
+# -----------
+# fracture_id : int
+# fracture number
+
+# Returns
+# --------
+# normal : numpy array
+# normal vector of a fracture
+
+# Notes
+# --------
+# None
+# """
+
+# normals = self.normal_vectors #np.genfromtxt("normal_vectors.dat")
+# return normals[fracture_id - 1, :]
+
+
+# def get_center(fracture_id):
+# """ Returns center of a fracture
+
+# Parameters
+# -----------
+# fracture_id : int
+# fracture number
+
+# Returns
+# --------
+# points : numpy array
+# x,y,z coordinates of a fracture
+
+# Notes
+# --------
+# None
+# """
+# with open('translations.dat') as old, open('points.dat', 'w') as new:
+# old.readline()
+# for line in old:
+# if not 'R' in line:
+# new.write(line)
+# points = np.genfromtxt('points.dat', skip_header=0, delimiter=' ')
+# return points[fracture_id - 1, :]
+
+
+defrotation_matrix(normalA,normalB):
+""" Create a Rotation matrix to transform normal vector A to normal vector B
+
+ Parameters
+ -----------
+ normalA : numpy array
+ normal vector
+ normalB : numpy array
+ normal vector
+
+
+ Returns
+ --------
+ R : numpy array
+ Rotation matrix
+
+ Notes
+ --------
+ None
+ """
+ # Check if normals are the same.
+ comparison=normalA==normalB
+ equal_arrays=comparison.all()
+
+ # If they are equal, Return the Identity Matrix
+ ifequal_arrays:
+ R=np.zeros(9)
+ R[0]=1
+ R[1]=0
+ R[2]=0
+ R[3]=0
+ R[4]=1
+ R[5]=0
+ R[6]=0
+ R[7]=0
+ R[8]=1
+ # If they are not equal, construct and return a Rotation Matrix
+ else:
+
+ xProd=np.cross(normalA,normalB)
+ sin=np.sqrt(xProd[0]*xProd[0]+xProd[1]*xProd[1]+
+ xProd[2]*xProd[2])
+ cos=np.dot(normalA,normalB)
+ v=np.zeros(9)
+ v=[
+ 0,-xProd[2],xProd[1],xProd[2],0,-xProd[0],-xProd[1],
+ xProd[0],0
+ ]
+ scalar=(1.0-cos)/(sin*sin)
+ vSquared=np.zeros(9)
+ vSquared[0]=(v[0]*v[0]+v[1]*v[3]+v[2]*v[6])*scalar
+ vSquared[1]=(v[0]*v[1]+v[1]*v[4]+v[2]*v[7])*scalar
+ vSquared[2]=(v[0]*v[2]+v[1]*v[5]+v[2]*v[8])*scalar
+ vSquared[3]=(v[3]*v[0]+v[4]*v[3]+v[5]*v[6])*scalar
+ vSquared[4]=(v[3]*v[1]+v[4]*v[4]+v[5]*v[7])*scalar
+ vSquared[5]=(v[3]*v[2]+v[4]*v[5]+v[5]*v[8])*scalar
+ vSquared[6]=(v[6]*v[0]+v[7]*v[3]+v[8]*v[6])*scalar
+ vSquared[7]=(v[6]*v[1]+v[7]*v[4]+v[8]*v[7])*scalar
+ vSquared[8]=(v[6]*v[2]+v[7]*v[5]+v[8]*v[8])*scalar
+ R=np.zeros(9)
+ R[0]=1+v[0]+vSquared[0]
+ R[1]=0+v[1]+vSquared[1]
+ R[2]=0+v[2]+vSquared[2]
+ R[3]=0+v[3]+vSquared[3]
+ R[4]=1+v[4]+vSquared[4]
+ R[5]=0+v[5]+vSquared[5]
+ R[6]=0+v[6]+vSquared[6]
+ R[7]=0+v[7]+vSquared[7]
+ R[8]=1+v[8]+vSquared[8]
+
+ returnR
+
+
+defrotate_point(p,R):
+""" Apply Rotation matrix R to the point p
+
+ Parameters
+ -----------
+ p : numpy array
+ point in 3D space
+ R : numpy array
+ Rotation matrix
+ Returns
+ --------
+ v : numpy array
+ The point p with the rotation matrix applied
+
+ Notes
+ --------
+ None
+ """
+ v=np.zeros(3)
+ v[0]=p[0]*R[0]+p[1]*R[1]+p[2]*R[2]
+ v[1]=p[0]*R[3]+p[1]*R[4]+p[2]*R[5]
+ v[2]=p[0]*R[6]+p[1]*R[7]+p[2]*R[8]
+ returnv
+
+
+
+[docs]
+defcleanup_wells(self,wells):
+""" Moves working files created while making wells into well_data directory
+
+ Parameters
+ -----------
+ self : object
+ DFN Class
+ well:
+ dictionary of information about the well. Contains the following:
+
+ well["name"] : string
+ name of the well
+
+ well["filename"] : string
+ filename of the well coordinates. "well_coords.dat" for example.
+ Format is :
+ x0 y0 z0
+ x1 y1 z1
+ ...
+ xn yn zn
+
+ well["r"] : float
+ radius of the well
+
+ Returns
+ --------
+ None
+
+ Notes
+ --------
+ Wells can be a list of well dictionaries
+ """
+ print("--> Cleaning up well files: Starting")
+
+ ifnotos.path.isdir("well_data"):
+ os.mkdir("well_data")
+
+ files=["well_{0}_line.inp","expand_well_{0}.lgi", \
+ "well_{0}_volume.inp","expand_well_{0}.out",\
+ "get_well_{0}_zone.lgi","create_well_{0}.out",\
+ "well_{0}_intersect.inp","create_well_{0}.dump",\
+ "create_well_{0}.log"]
+
+ iftype(wells)isdict:
+ well=wells
+ forfileinfiles:
+ try:
+ shutil.move(file.format(well['name']),
+ "well_data/"+file.format(well['name']))
+ except:
+ print("Error moving "+file.format(well['name']))
+ pass
+
+
+ iftype(wells)islist:
+ forwellinwells:
+ forfileinfiles:
+ try:
+ shutil.move(file.format(well['name']),
+ "well_data/"+file.format(well['name']))
+ except:
+ print("Error moving "+file.format(well['name']))
+ pass
+ print("--> Cleaning up well files: Complete")
+
+
+
+[docs]
+defcombine_well_boundary_zones(self,wells):
+""" Processes zone files for particle tracking. All zone files are combined into allboundaries.zone
+
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ None
+ """
+ # If there is only 1 well, make a symbolic link
+ iftype(wells)isdict:
+ os.symlink(f"well_{well['name']}.zone","well_nodes.zone")
+
+ iftype(wells)islist:
+ number_of_wells=len(wells)
+ fall=open("well_nodes.zone","w")
+ forindex,wellinenumerate(wells):
+ ifindex==0:
+ print(f"Working on well {well['name']}")
+ fzone=open(f"well_{well['name']}.zone","r")
+ lines=fzone.readlines()
+ lines=lines[:-2]
+ fall.writelines(lines)
+ ifindex>0andindex<number_of_wells-1:
+ print(f"Working on well {well['name']}")
+ fzone=open(f"well_{well['name']}.zone","r")
+ lines=fzone.readlines()
+ lines=lines[1:-2]
+ lines[0]=f"{index+1:06d}\t\t{well['name']}\n"
+ fzone.close()
+ fall.writelines(lines)
+ ifindex==number_of_wells-1:
+ print(f"Working on well {well['name']}")
+ fzone=open(f"well_{well['name']}.zone","r")
+ lines=fzone.readlines()
+ lines=lines[1:]
+ lines[0]=f"{index+1:06d}\t\t{well['name']}\n"
+ fzone.close()
+ fall.writelines(lines)
+ fall.close()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/dfn2graph.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/dfn2graph.html
new file mode 100644
index 000000000..a3d9419c0
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/dfn2graph.html
@@ -0,0 +1,555 @@
+
+
+
+
+
+ pydfnworks.dfnGraph.dfn2graph — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[docs]
+defcreate_graph(self,graph_type,inflow,outflow):
+""" Header function to create a graph based on a DFN. Particular algorithms are in files.
+
+ Parameters
+ ----------
+ self : object
+ DFN Class object
+ graph_type : string
+ Option for what graph representation of the DFN is requested. Currently supported are fracture, intersection, and bipartitie
+ inflow : string
+ Name of inflow boundary (connect to source)
+ outflow : string
+ Name of outflow boundary (connect to target)
+
+ Returns
+ -------
+ G : NetworkX Graph
+ Graph based on DFN
+
+ Notes
+ -----
+
+"""
+ ## write hydraulic properties to file.
+ self.dump_hydraulic_values()
+
+ ifgraph_type=="fracture":
+ G=create_fracture_graph(inflow,outflow)
+ elifgraph_type=="intersection":
+ G=create_intersection_graph(inflow,outflow)
+ elifgraph_type=="bipartite":
+ G=create_bipartite_graph(inflow,outflow)
+ else:
+ print(
+ f"Error. Unknown graph type.\nType provided: {graph_type}.\nAccetable names: fracture, intersection, bipartite.\nReturning empty graph."
+ )
+ returnnx.Graph()
+ returnG
+
+
+
+defboundary_index(bc_name):
+"""Determines boundary index in intersections_list.dat from name
+
+ Parameters
+ ----------
+ bc_name : string
+ Boundary condition name
+
+ Returns
+ -------
+ bc_index : int
+ integer indexing of cube faces
+
+ Notes
+ -----
+ top = 1
+ bottom = 2
+ left = 3
+ front = 4
+ right = 5
+ back = 6
+ """
+ bc_dict={
+ "top":-1,
+ "bottom":-2,
+ "left":-3,
+ "front":-4,
+ "right":-5,
+ "back":-6
+ }
+ try:
+ returnbc_dict[bc_name]
+ except:
+ error=f"Error. Unknown boundary condition: {bc_name}\nExiting\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+
+
+[docs]
+defadd_fracture_source(self,G,source):
+"""
+
+ Parameters
+ ----------
+ G : NetworkX Graph
+ NetworkX Graph based on a DFN
+ source_list : list
+ list of integers corresponding to fracture numbers
+ remove_old_source: bool
+ remove old source from the graph
+
+ Returns
+ -------
+ G : NetworkX Graph
+
+ Notes
+ -----
+ bipartite graph not supported
+
+ """
+
+ ifnottype(source)==list:
+ source=[source]
+
+ print("--> Adding new source connections")
+ print("--> Warning old source will be removed!!!")
+
+ ifG.graph['representation']=="fracture":
+ # removing old source term and all connections
+ G.remove_node('s')
+ # add new source node
+ G.add_node('s')
+
+ G.nodes['s']['perm']=1.0
+ G.nodes['s']['iperm']=1.0
+
+ foruinsource:
+ G.add_edge(u,'s')
+
+ elifG.graph['representation']=="intersection":
+ # removing old source term and all connections
+ nodes_to_remove=['s']
+ foru,dinG.nodes(data=True):
+ ifu!='s'andu!='t':
+ f1,f2=d["frac"]
+ #print("node {0}: f1 {1}, f2 {2}".format(u,f1,f2))
+ iff2=='s':
+ nodes_to_remove.append(u)
+
+ print("--> Removing nodes: ",nodes_to_remove)
+ G.remove_nodes_from(nodes_to_remove)
+
+ # add new source node
+ G.add_node('s')
+ foru,dinG.nodes(data=True):
+ ifu!='s'andu!='t':
+ f1=d["frac"][0]
+ f2=d["frac"][1]
+ iff1insource:
+ print(
+ "--> Adding edge between {0} and new source / fracture {1}"
+ .format(u,f1))
+ G.add_edge(u,'s',frac=f1,length=0.,perm=1.,iperm=1.)
+ eliff2insource:
+ print(
+ "--> Adding edge between {0} and new source / fracture {1}"
+ .format(u,f2))
+ G.add_edge(u,'s',frac=f2,length=0.,perm=1.,iperm=1.)
+
+ elifG.graph['representation']=="bipartite":
+ print("--> Not supported for bipartite graph")
+ print("--> Returning unchanged graph")
+ returnG
+
+
+
+
+[docs]
+defadd_fracture_target(self,G,target):
+"""
+
+ Parameters
+ ----------
+ G : NetworkX Graph
+ NetworkX Graph based on a DFN
+ target : list
+ list of integers corresponding to fracture numbers
+ Returns
+ -------
+ G : NetworkX Graph
+
+ Notes
+ -----
+ bipartite graph not supported
+
+ """
+
+ ifnottype(target)==list:
+ source=[target]
+
+ print("--> Adding new target connections")
+ print("--> Warning old target will be removed!!!")
+
+ ifG.graph['representation']=="fracture":
+ # removing old target term and all connections
+ G.remove_node('t')
+ # add new target node
+ G.add_node('t')
+
+ G.nodes['t']['perm']=1.0
+ G.nodes['t']['iperm']=1.0
+
+ foruintarget:
+ G.add_edge(u,'t')
+
+ elifG.graph['representation']=="intersection":
+ # removing old target term and all connections
+ nodes_to_remove=['t']
+ foru,dinG.nodes(data=True):
+ ifu!='s'andu!='t':
+ f1,f2=d["frac"]
+ #print("node {0}: f1 {1}, f2 {2}".format(u,f1,f2))
+ iff2=='t':
+ nodes_to_remove.append(u)
+
+ print("--> Removing nodes: ",nodes_to_remove)
+ G.remove_nodes_from(nodes_to_remove)
+
+ # add new target node
+ G.add_node('t')
+ foru,dinG.nodes(data=True):
+ ifu!='s'andu!='t':
+ f1=d["frac"][0]
+ f2=d["frac"][1]
+ iff1intarget:
+ print(
+ "--> Adding edge between {0} and new target / fracture {1}"
+ .format(u,f1))
+ G.add_edge(u,'t',frac=f1,length=0.,perm=1.,iperm=1.)
+ eliff2intarget:
+ print(
+ "--> Adding edge between {0} and new target / fracture {1}"
+ .format(u,f2))
+ G.add_edge(u,'t',frac=f2,length=0.,perm=1.,iperm=1.)
+
+ elifG.graph['representation']=="bipartite":
+ print("--> Not supported for bipartite graph")
+ print("--> Returning unchanged graph")
+ returnG
+
+
+
+defpull_source_and_target(nodes,source='s',target='t'):
+"""Removes source and target from list of nodes, useful for dumping subnetworks to file for remeshing
+
+ Parameters
+ ----------
+ nodes :list
+ List of nodes in the graph
+ source : node
+ Starting node
+ target : node
+ Ending node
+ Returns
+ -------
+ nodes : list
+ List of nodes with source and target nodes removed
+
+ Notes
+ -----
+
+"""
+ fornodein[source,target]:
+ try:
+ nodes.remove(node)
+ except:
+ pass
+ returnnodes
+
+
+
+[docs]
+defdump_fractures(self,G,filename):
+"""Write fracture numbers assocaited with the graph G out into an ASCII file inputs
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+ G : NetworkX graph
+ NetworkX Graph based on the DFN
+ filename : string
+ Output filename
+
+ Returns
+ -------
+
+ Notes
+ -----
+ """
+
+ ifG.graph['representation']=="fracture":
+ nodes=list(G.nodes())
+ elifG.graph['representation']=="intersection":
+ nodes=[]
+ foru,v,dinG.edges(data=True):
+ nodes.append(G[u][v]['frac'])
+ nodes=list(set(nodes))
+ elifG.graph['representation']=="bipartite":
+ nodes=[]
+ foru,v,dinG.edges(data=True):
+ nodes.append(G[u][v]['frac'])
+ nodes=list(set(nodes))
+
+ nodes=pull_source_and_target(nodes)
+ fractures=[int(i)foriinnodes]
+ fractures=sorted(fractures)
+ print("--> Dumping %s"%filename)
+ np.savetxt(filename,fractures,fmt="%d")
+
+
+
+
+[docs]
+defplot_graph(self,G,source='s',target='t',output_name="dfn_graph"):
+""" Create a png of a graph with source nodes colored blue, target red, and all over nodes black
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ NetworkX Graph based on the DFN
+ source : node
+ Starting node
+ target : node
+ Ending node
+ output_name : string
+ Name of output file (no .png)
+
+ Returns
+ -------
+
+ Notes
+ -----
+ Image is written to output_name.png
+
+ """
+ print("\n--> Plotting Graph")
+ print("--> Output file: %s.png"%output_name)
+ # get positions for all nodes
+ pos=nx.spring_layout(G)
+ nodes=list(G.nodes)
+ # draw nodes
+ nx.draw_networkx_nodes(G,
+ pos,
+ nodelist=nodes,
+ node_color='k',
+ node_size=10,
+ alpha=1.0)
+ nx.draw_networkx_nodes(G,
+ pos,
+ nodelist=[source],
+ node_color='b',
+ node_size=50,
+ alpha=1.0)
+ nx.draw_networkx_nodes(G,
+ pos,
+ nodelist=[target],
+ node_color='r',
+ node_size=50,
+ alpha=1.0)
+
+ nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.5)
+ plt.axis('off')
+ plt.tight_layout()
+ plt.savefig(output_name+".png")
+ plt.clf()
+ print("--> Plotting Graph Complete\n")
+
+
+
+
+[docs]
+defdump_json_graph(self,G,name):
+"""Write graph out in json format
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+ G :networkX graph
+ NetworkX Graph based on the DFN
+ name : string
+ Name of output file (no .json)
+
+ Returns
+ -------
+
+ Notes
+ -----
+
+"""
+ print("--> Dumping Graph into file: "+name+".json")
+ jsondata=json_graph.node_link_data(G)
+ withopen(name+'.json','w')asfp:
+ json.dump(jsondata,fp)
+ print("--> Complete")
+
+
+
+
+[docs]
+defload_json_graph(self,name):
+""" Read in graph from json format
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+ name : string
+ Name of input file (no .json)
+
+ Returns
+ -------
+ G :networkX graph
+ NetworkX Graph based on the DFN
+"""
+
+ print(f"Loading Graph in file: {name}.json")
+ fp=open(name+'.json')
+ G=json_graph.node_link_graph(json.load(fp))
+ print("Complete")
+ returnG
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/graph_flow.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/graph_flow.html
new file mode 100644
index 000000000..4db88722d
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/graph_flow.html
@@ -0,0 +1,517 @@
+
+
+
+
+
+ pydfnworks.dfnGraph.graph_flow — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for pydfnworks.dfnGraph.graph_transport
+"""
+.. module:: graph_transport.py
+ :synopsis: simulate transport on a pipe network representaiton of a DFN
+.. moduleauthor:: Shriram Srinivasan <shrirams@lanl.gov>, Jeffrey Hyman <jhyman@lanl.gov>
+
+"""
+
+importtimeit
+importsys
+importnetworkxasnx
+importnumpyasnp
+importmultiprocessingasmp
+
+# pydfnworks graph modules modules
+importpydfnworks.dfnGraph.particle_ioasio
+frompydfnworks.dfnGraph.graph_tdrwimportset_up_limited_matrix_diffusion
+frompydfnworks.dfnGraph.particle_classimportParticle
+
+
+deftrack_particle(data,verbose=False):
+""" Tracks a single particle through the graph
+
+ all input parameters are in the dictionary named data
+
+ Parameters
+ ----------
+ data : dict
+ Dictionary of parameters the includes particle_number, initial_position,
+ tdrw_flag, matrix_porosity, matrix_diffusivity, cp_flag, control_planes,
+ direction, G, and nbrs_dict.
+
+ Returns
+ -------
+ particle : object
+ Particle will full trajectory
+
+ """
+ ifverbose:
+ p=mp.current_process()
+ _,cpu_id=p.name.split("-")
+ cpu_id=int(cpu_id)
+ print(
+ f"--> Particle {data['particle_number']} is starting on worker {cpu_id}"
+ )
+
+ particle=Particle(data["particle_number"],data["initial_position"],
+ data["tdrw_flag"],data["matrix_porosity"],
+ data["matrix_diffusivity"],data["fracture_spacing"],
+ data["trans_prob"],data["transfer_time"],
+ data["cp_flag"],data["control_planes"],
+ data["direction"])
+
+ # # get current process information
+ globalnbrs_dict
+ globalG_global
+ particle.track(G_global,nbrs_dict)
+ ifverbose:
+ print(
+ f"--> Particle {data['particle_number']} is complete on worker {cpu_id}"
+ )
+ returnparticle
+
+
+defget_initial_posititions(G,initial_positions,nparticles):
+""" Distributes initial particle positions
+
+ Parameters
+ ----------
+
+ G : NetworkX graph
+ obtained from graph_flow
+
+ initial_positions : str
+ distribution of initial conditions. options are uniform and flux (flux-weighted)
+
+ nparticles : int
+ requested number of particles
+
+ Returns
+ -------
+ ip : numpy array
+ array nparticles long. Each element is the initial position for each particle
+
+ """
+
+ inlet_nodes=[vforvinnx.nodes(G)ifG.nodes[v]['inletflag']]
+ cnt=len(inlet_nodes)
+ print(f"--> There are {cnt} inlet nodes")
+ ifcnt==0:
+ error="Error. There are no nodes in the inlet.\nExiting"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ # Uniform Distribution for particles
+ ifinitial_positions=="uniform":
+ print("--> Using uniform initial positions.")
+ ip=np.zeros(nparticles).astype(int)
+ n=int(np.ceil(nparticles/cnt))
+ print(f"--> {n} particles will be placed at every inflow node.\n")
+ ## this could be cleaned up using clever indexing
+ inflow_idx=0
+ inflow_cnt=0
+ foriinrange(nparticles):
+ ip[i]=inlet_nodes[inflow_idx]
+ inflow_cnt+=1
+ ifinflow_cnt>=n:
+ inflow_idx+=1
+ inflow_cnt=0
+
+ ## flux weighted initial positions for particles
+ elifinitial_positions=="flux":
+ print("--> Using flux-weighted initial positions.\n")
+ flux=np.zeros(cnt)
+ fori,uinenumerate(inlet_nodes):
+ forvinG.successors(u):
+ flux[i]+=G.edges[u,v]['flux']
+ flux/=flux.sum()
+ flux_cnts=[np.ceil(nparticles*i)foriinflux]
+ nparticles=int(sum(flux_cnts))
+ ip=np.zeros(nparticles).astype(int)
+ ## Populate ip with Flux Cnts
+ ## this could be cleaned up using clever indexing
+ inflow_idx=0
+ inflow_cnt=0
+ foriinrange(nparticles):
+ ip[i]=inlet_nodes[inflow_idx]
+ inflow_cnt+=1
+ ifinflow_cnt>=flux_cnts[inflow_idx]:
+ inflow_idx+=1
+ inflow_cnt=0
+
+ # Throw error if unknown initial position is provided
+ else:
+ error=f"Error. Unknown initial_positions input {initial_positions}. Options are uniform or flux \n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ returnip,nparticles
+
+
+defcreate_neighbor_list(G):
+""" Create a list of downstream neighbor vertices for every vertex on NetworkX graph obtained after running graph_flow
+
+ Parameters
+ ----------
+ G: NetworkX graph
+ Directed Graph obtained from output of graph_flow
+
+ Returns
+ -------
+ dict : nested dictionary.
+
+ Notes
+ -----
+ dict[n]['child'] is a list of vertices downstream to vertex n
+ dict[n]['prob'] is a list of probabilities for choosing a downstream node for vertex n
+ """
+
+ nbrs_dict={}
+
+ foruinnx.nodes(G):
+
+ ifG.nodes[u]['outletflag']:
+ continue
+
+ node_list=[]
+ prob_list=[]
+ nbrs_dict[u]={}
+
+ forvinG.successors(u):
+ node_list.append(v)
+ prob_list.append(G.edges[u,v]['vol_flow_rate'])
+
+ ifnode_list:
+ nbrs_dict[u]['child']=node_list
+ nbrs_dict[u]['prob']=np.asarray(prob_list)/sum(prob_list)
+ else:
+ nbrs_dict[u]['child']=None
+ nbrs_dict[u]['prob']=None
+
+ returnnbrs_dict
+
+
+defcheck_tdrw_params(matrix_porosity,matrix_diffusivity,fracture_spacing):
+""" Check that the provided tdrw values are physiscal
+
+
+ Parameters
+ ----------
+ G: NetworkX graph
+ Directed Graph obtained from output of graph_flow
+
+ Returns
+ -------
+ dict : nested dictionary.
+
+ Notes
+ -----
+ dict[n]['child'] is a list of vertices downstream to vertex n
+ dict[n]['prob'] is a list of probabilities for choosing a downstream node for vertex n
+
+ """
+
+ ifmatrix_porosityisNone:
+ error=f"Error. Requested TDRW but no value for matrix_porosity was provided\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ elifmatrix_porosity<0ormatrix_porosity>1:
+ error=f"Error. Requested TDRW but value for matrix_porosity provided is outside of [0,1]. Value provided {matrix_porosity}\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ ifmatrix_diffusivityisNone:
+ error=f"Error. Requested TDRW but no value for matrix_diffusivity was provided\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ iffracture_spacingisnotNone:
+ iffracture_spacing<=0:
+ error=f"Error. Non-positive value for fracture_spacing was provided.\nValue {fracture_spacing}\nExiting program"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+
+defcheck_control_planes(control_planes,direction):
+ control_plane_flag=False
+ ifnottype(control_planes)islist:
+ error=f"Error. provided controls planes are not a list\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ else:
+ # add None to indicate the end of the control plane list
+ control_plane_flag=True
+
+ ifdirectionisNone:
+ error=f"Error. Primary direction not provided. Required for control planes\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ elifdirectionnotin['x','y','z']:
+ error=f"Error. Primary direction is not known. Acceptable values are x,y, and z\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ print(f"--> Control Planes: {control_planes}")
+ print(f"--> Direction: {direction}")
+ returncontrol_plane_flag
+
+
+
+[docs]
+defrun_graph_transport(self,
+ G,
+ nparticles,
+ partime_file,
+ frac_id_file=None,
+ format='hdf5',
+ initial_positions="uniform",
+ dump_traj=False,
+ tdrw_flag=False,
+ matrix_porosity=None,
+ matrix_diffusivity=None,
+ fracture_spacing=None,
+ control_planes=None,
+ direction=None,
+ cp_filename='control_planes'):
+""" Run particle tracking on the given NetworkX graph
+
+ Parameters
+ ----------
+ self : object
+ DFN Class
+
+ G : NetworkX graph
+ obtained from graph_flow
+
+ nparticles: int
+ number of particles
+
+ initial_positions : str
+ distribution of initial conditions. options are uniform and flux (flux-weighted)
+
+ partime_file : string
+ name of file to which the total travel times and lengths will be written for each particle
+
+ frac_id_file : string
+ name of file to which detailed information of each particle's travel will be written
+
+ dump_flag: bool
+ on/off to write full trajectory information to file
+
+ tdrw_flag : Bool
+ if False, matrix_porosity and matrix_diffusivity are ignored
+
+ matrix_porosity: float
+ Matrix Porosity used in TDRW
+
+ matrix_diffusivity: float
+ Matrix Diffusivity used in TDRW (SI units m^2/s)
+
+ fracture_spaceing : float
+ finite block size for limited matrix diffusion
+
+ control_planes : list of floats
+ list of control plane locations to dump travel times. Only in primary direction of flow.
+
+ primary direction : string (x,y,z)
+ string indicating primary direction of flow
+
+ Returns
+ -------
+ particles : list
+ list of particles objects
+
+ Notes
+ -----
+ Information on individual functions is found therein
+ """
+ ## the flow graph needs to be a global variable so all processors can access it
+ ## without making a copy of it.
+ globalG_global
+ G_global=nx.Graph()
+ G_global=G.copy()
+
+ ifnotformatin['ascii','hdf5']:
+ error=(
+ f"--> Error. Unknown file format provided in run_graph_transport.\n\n--> Provided value is {format}.\n--> Options: 'ascii' or 'hdf5'.\n\nExitting\n\n"
+ )
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ print("\n--> Running Graph Particle Tracking")
+ # Check parameters for TDRW
+ iftdrw_flag:
+ check_tdrw_params(matrix_porosity,matrix_diffusivity,
+ fracture_spacing)
+ print(
+ f"--> Running particle transport with TDRW.\n--> Matrix porosity {matrix_porosity}.\n--> Matrix Diffusivity {matrix_diffusivity} m^2/s"
+ )
+
+ ifcontrol_planesisNone:
+ control_plane_flag=False
+ else:
+ control_plane_flag=check_control_planes(
+ control_planes=control_planes,direction=direction)
+ print(f"--> Control Plane Flag {control_plane_flag}")
+
+ print("--> Creating downstream neighbor list")
+ globalnbrs_dict
+ nbrs_dict=create_neighbor_list(G)
+
+ print("--> Getting initial Conditions")
+ ip,nparticles=get_initial_posititions(G,initial_positions,nparticles)
+
+ print(f"--> Starting particle tracking for {nparticles} particles")
+
+ ifdump_traj:
+ print(f"--> Writing trajectory information to file")
+
+ iffracture_spacingisnotNone:
+ print(f"--> Using limited matrix block size for TDRW")
+ print(f"--> Fracture spacing {fracture_spacing:0.2e} [m]")
+ trans_prob=set_up_limited_matrix_diffusion(G,fracture_spacing,
+ matrix_porosity,
+ matrix_diffusivity)
+ # This doesn't change for the system.
+ # Transfer time diffusing between fracture blocks
+ transfer_time=fracture_spacing**2/(2*matrix_diffusivity)
+ else:
+ trans_prob=None
+ transfer_time=None
+ ## main loop
+ ifself.ncpu==1:
+ tic=timeit.default_timer()
+ particles=[]
+ foriinrange(nparticles):
+ ifi%1000==0:
+ print(f"--> Starting particle {i} out of {nparticles}")
+ particle=Particle(i,ip[i],tdrw_flag,matrix_porosity,
+ matrix_diffusivity,fracture_spacing,
+ trans_prob,transfer_time,control_plane_flag,
+ control_planes,direction)
+ particle.track(G,nbrs_dict)
+ particles.append(particle)
+
+ elapsed=timeit.default_timer()-tic
+ print(
+ f"--> Main Tracking Loop Complete. Time Required {elapsed:0.2e} seconds"
+ )
+ stuck_particles=io.dump_particle_info(particles,partime_file,
+ frac_id_file,format)
+ ifcontrol_plane_flag:
+ io.dump_control_planes(particles,control_planes,cp_filename,
+ format)
+
+ ifdump_traj:
+ io.dump_trajectories(particles,1)
+
+ ifself.ncpu>1:
+ print(f"--> Using {self.ncpu} processors")
+ ## Prepare input data
+ inputs=[]
+
+ tic=timeit.default_timer()
+ pool=mp.Pool(min(self.ncpu,nparticles))
+
+ particles=[]
+
+ defgather_output(output):
+ particles.append(output)
+
+ foriinrange(nparticles):
+ data={}
+ data["particle_number"]=i
+ data["initial_position"]=ip[i]
+ data["tdrw_flag"]=tdrw_flag
+ data["matrix_porosity"]=matrix_porosity
+ data["matrix_diffusivity"]=matrix_diffusivity
+ data["fracture_spacing"]=fracture_spacing
+ data["transfer_time"]=transfer_time
+ data["trans_prob"]=trans_prob
+ data["cp_flag"]=control_plane_flag
+ data["control_planes"]=control_planes
+ data["direction"]=direction
+ pool.apply_async(track_particle,
+ args=(data,),
+ callback=gather_output)
+
+ pool.close()
+ pool.join()
+ pool.terminate()
+
+ elapsed=timeit.default_timer()-tic
+ print(
+ f"--> Main Tracking Loop Complete. Time Required {elapsed:0.2e} seconds"
+ )
+
+ stuck_particles=io.dump_particle_info(particles,partime_file,
+ frac_id_file,format)
+ ifcontrol_plane_flag:
+ io.dump_control_planes(particles,control_planes,cp_filename,
+ format)
+
+ ifdump_traj:
+ io.dump_trajectories(particles,min(self.ncpu,nparticles))
+
+ ifstuck_particles==0:
+ print("--> All particles exited the network")
+ print("--> Graph Particle Tracking Completed Successfully.")
+ else:
+ print(
+ f"--> Out of {nparticles} particles, {stuck_particles} particles did not exit"
+ )
+
+ # Clean up and delete the global versions
+ delG_global
+ delnbrs_dict
+
+ returnparticles
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/pruning.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/pruning.html
new file mode 100644
index 000000000..57a90598a
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnGraph/pruning.html
@@ -0,0 +1,318 @@
+
+
+
+
+
+ pydfnworks.dfnGraph.pruning — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+importnumpyasnp
+importnetworkxasnx
+
+fromnetworkx.algorithms.flow.shortestaugmentingpathimport*
+fromnetworkx.algorithms.flow.edmondskarpimport*
+fromnetworkx.algorithms.flow.preflowpushimport*
+
+fromitertoolsimportislice
+
+
+defcurrent_flow_threshold(self,
+ G,
+ source="s",
+ target="t",
+ weight=None,
+ thrs=0.0):
+""" Runs current flow (Potential drop between source and target) on the Graph G, and returns a subgraph such that the current on the edges is greater than the threshold value (thrs).
+
+ Parameters
+ ----------
+ G : NetworkX Graph
+ NetworkX Graph based on a DFN
+ source : node
+ Starting node
+ target : node
+ Ending node
+ weight : string
+ Resistance term used in the solution of Laplace's Equation
+ thrs: float
+ Threshold value for pruning the graph
+
+ Returns
+ -------
+ H : NetworkX graph
+ Subgraph such that the current on the edges is greater than the threshold value
+
+ Notes
+ -----
+ Graph attributes (node and edge) are not retained on the subgraph H.
+ """
+
+ print(
+ f'--> Running Current Flow with weight : {weight} and threshold {thrs}'
+ )
+ cf=nx.edge_current_flow_betweenness_centrality_subset(G,
+ sources=[source],
+ targets=[target],
+ weight=weight)
+ print("Current Flow Complete")
+ currentflow_edges=[(u,v)for(u,v),dincf.items()ifd>thrs]
+ H=G.edge_subgraph(currentflow_edges).copy()
+ H.graph["representation"]=G.graph["representation"]
+ # H = nx.Graph(currentflow_edges, representation=G.graph["representation"])
+ print(
+ f"--> Of the {G.number_of_nodes()} in the original graph, {H.number_of_nodes()} are in the thresholded network"
+ )
+ print("--> Running Current Flow Complete")
+ returnH
+
+
+defk_shortest_paths(G,k,source,target,weight):
+"""Returns the k shortest paths in a graph
+
+ Parameters
+ ----------
+ G : NetworkX Graph
+ NetworkX Graph based on a DFN
+ k : int
+ Number of requested paths
+ source : node
+ Starting node
+ target : node
+ Ending node
+ weight : string
+ Edge weight used for finding the shortest path
+
+ Returns
+ -------
+ paths : sets of nodes
+ a list of lists of nodes in the k shortest paths
+
+ Notes
+ -----
+ Edge weights must be numerical and non-negative
+"""
+ returnlist(
+ islice(nx.shortest_simple_paths(G,source,target,weight=weight),k))
+
+
+
+[docs]
+defk_shortest_paths_backbone(self,G,k,source='s',target='t',weight=None):
+"""Returns the subgraph made up of the k shortest paths in a graph
+
+ Parameters
+ ----------
+ G : NetworkX Graph
+ NetworkX Graph based on a DFN
+ k : int
+ Number of requested paths
+ source : node
+ Starting node
+ target : node
+ Ending node
+ weight : string
+ Edge weight used for finding the shortest path
+
+ Returns
+ -------
+ H : NetworkX Graph
+ Subgraph of G made up of the k shortest paths
+
+ Notes
+ -----
+ See Hyman et al. 2017 "Predictions of first passage times in sparse discrete fracture networks using graph-based reductions" Physical Review E for more details
+"""
+
+ print(f"\n--> Determining {k} shortest paths in the network")
+ H=G.copy()
+ k_shortest=set([])
+ forpathink_shortest_paths(G,k,source,target,weight):
+ k_shortest|=set(path)
+ k_shortest.remove('s')
+ k_shortest.remove('t')
+ path_nodes=sorted(list(k_shortest))
+ path_nodes.append('s')
+ path_nodes.append('t')
+ nodes=list(G.nodes())
+ secondary=list(set(nodes)-set(path_nodes))
+ forninsecondary:
+ H.remove_node(n)
+ returnH
+ print("--> Complete\n")
+
+
+
+
+[docs]
+defgreedy_edge_disjoint(self,G,source='s',target='t',weight='None',k=''):
+"""
+ Greedy Algorithm to find edge disjoint subgraph from s to t.
+ See Hyman et al. 2018 SIAM MMS
+
+ Parameters
+ ----------
+ self : object
+ DFN Class Object
+ G : NetworkX graph
+ NetworkX Graph based on the DFN
+ source : node
+ Starting node
+ target : node
+ Ending node
+ weight : string
+ Edge weight used for finding the shortest path
+ k : int
+ Number of edge disjoint paths requested
+
+ Returns
+ -------
+ H : NetworkX Graph
+ Subgraph of G made up of the k shortest of all edge-disjoint paths from source to target
+
+ Notes
+ -----
+ 1. Edge weights must be numerical and non-negative.
+ 2. See Hyman et al. 2018 "Identifying Backbones in Three-Dimensional Discrete Fracture Networks: A Bipartite Graph-Based Approach" SIAM Multiscale Modeling and Simulation for more details
+
+ """
+ print("--> Identifying edge disjoint paths")
+ ifG.graph['representation']!="intersection":
+ print(
+ "--> ERROR!!! Wrong type of DFN graph representation\nRepresentation must be intersection\nReturning Empty Graph\n"
+ )
+ returnnx.Graph()
+ Gprime=G.copy()
+ Hprime=nx.Graph()
+ Hprime.graph['representation']=G.graph['representation']
+ cnt=0
+
+ # if a number of paths in not provided k will equal the min cut between s and t
+ min_cut=len(nx.minimum_edge_cut(G,'s','t'))
+ ifk==''ork>min_cut:
+ k=min_cut
+
+ whilenx.has_path(Gprime,source,target):
+ path=nx.shortest_path(Gprime,source,target,weight=weight)
+ H=Gprime.subgraph(path)
+ Hprime.add_edges_from(H.edges(data=True))
+ Gprime.remove_edges_from(list(H.edges()))
+
+ cnt+=1
+ ifcnt>k:
+ break
+ print("--> Complete")
+ returnHprime
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnTrans/transport.html b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnTrans/transport.html
new file mode 100644
index 000000000..8deddcf24
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_modules/pydfnworks/dfnTrans/transport.html
@@ -0,0 +1,487 @@
+
+
+
+
+
+ pydfnworks.dfnTrans.transport — dfnWorks v2.8 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[docs]
+defcreate_dfn_trans_links(self,path='../'):
+""" Create symlinks to files required to run dfnTrans that are in another directory.
+
+ Parameters
+ ---------
+ self : object
+ DFN Class
+ path : string
+ Absolute path to primary directory.
+
+ Returns
+ --------
+ None
+
+ Notes
+ -------
+ Typically, the path is DFN.path, which is set by the command line argument -path
+
+ """
+ files=[
+ 'params.txt','allboundaries.zone','full_mesh.stor','poly_info.dat',
+ 'full_mesh.inp','aperture.dat'
+ ]
+ ifself.flow_solver=='PFLOTRAN':
+ files.append('cellinfo.dat')
+ files.append('darcyvel.dat')
+ files.append('full_mesh_vol_area.uge')
+ ifself.flow_solver=='FEHM':
+ files.append('tri_frac.fin')
+
+ forfinfiles:
+ try:
+ os.symlink(path+f,f)
+ except:
+ print("--> Error Creating link for %s\n"%f)
+
+
+
+
+[docs]
+defcheck_dfn_trans_run_files(self):
+""" Ensures that all files required for dfnTrans run are in the current directory
+
+ Parameters
+ ---------
+ self : object
+ DFN Class
+
+ Returns
+ --------
+ None
+
+ Notes
+ -------
+ None
+ """
+ cwd=os.getcwd()
+ print(
+ "\nChecking that all files required for dfnTrans are in the current directory"
+ )
+ print("--> Current Working Directory: %s"%cwd)
+ print("--> dfnTrans is running from: %s"%self.local_dfnTrans_file)
+
+ print("--> Checking DFNTrans Parameters")
+ params={
+ "param:":None,
+ "poly:":None,
+ "inp:":None,
+ "stor:":None,
+ "boundary:":None,
+ "out_grid:":None,
+ "out_3dflow:":None,
+ "out_init:":None,
+ "out_tort:":None,
+ "out_curv:":None,
+ "out_avs:":None,
+ "out_traj:":None,
+ "out_fract:":None,
+ "out_filetemp:":None,
+ "out_dir:":None,
+ "out_path:":None,
+ "out_time:":None,
+ "ControlPlane:":None,
+ "control_out:":None,
+ "delta_Control:":None,
+ "flowdir:":None,
+ "init_nf:":None,
+ "init_partn:":None,
+ "init_eqd:":None,
+ "init_npart:":None,
+ "init_fluxw:":None,
+ "init_totalnumber:":None,
+ "init_oneregion:":None,
+ "in_partn:":None,
+ "init_well:":None,
+ "init_nodepart:":None,
+ "in_xmin:":None,
+ "in_xmax:":None,
+ "in_ymin:":None,
+ "in_ymax:":None,
+ "in_zmin:":None,
+ "in_zmax:":None,
+ "init_random:":None,
+ "in_randpart:":None,
+ "init_matrix:":None,
+ "inm_coord:":None,
+ "inm_nodeID:":None,
+ "inm_porosity:":None,
+ "inm_diffcoeff:":None,
+ "streamline_routing:":None,
+ "tdrw:":None,
+ "tdrw_porosity:":None,
+ "tdrw_diffcoeff:":None,
+ "timesteps:":None,
+ "time_units:":None,
+ "flux_weight:":None,
+ "seed:":None,
+ "in-flow-boundary:":None,
+ "out-flow-boundary:":None,
+ "aperture:":None,
+ "aperture_type:":None,
+ "aperture_file:":None,
+ "porosity":None,
+ "density:":None,
+ "satur:":None,
+ "thickness:":None
+ }
+
+ files=["param:","poly:","inp:","stor:","boundary:"]
+
+ ifself.flow_solver=="PFLOTRAN":
+ params["PFLOTRAN_vel:"]=None
+ files.append("PFLOTRAN_vel:")
+
+ params["PFLOTRAN_cell:"]=None
+ files.append("PFLOTRAN_cell:")
+
+ params["PFLOTRAN_uge:"]=None
+ files.append("PFLOTRAN_uge:")
+
+ ifself.flow_solver=="FEHM":
+ params["FEHM_fin:"]=None
+ files.append("FEHM_fin:")
+
+ # Parse DFNTrans input and fill dictionary
+ keys=params.keys()
+ withopen(self.local_dfnTrans_file)asfp:
+
+ forlineinfp.readlines():
+ if"/*"inline:
+ comment=line
+ line=line[:line.index(
+ "/*")]## only process text before '/*' comment
+ if"//"inline:
+ line=line[:line.index("//")]
+
+ iflen(line)>0:
+ forkeyinkeys:
+ ifkeyinline:
+ ifparams[key]==None:
+ params[key]=line.split()[1]
+
+ #for p in params.keys():
+ # print(p,params[p])
+
+ # Check if file required for the run are in the directory and are not empty
+ forkeyinfiles:
+ ifparams[key]isNone:
+ error=f"ERROR!!!!!\nRequired file {key} was not provided.\nPlease check DFNTrans control file\nExiting Program\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+ elifnotos.path.isfile(params[key]):
+ error="ERROR!!!!!\nRequired file %s is not in the current directory.\nPlease check required files\nExiting Program\n"%params[
+ key]
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ print(
+ "--> All files required for dfnTrans have been found in current directory\n\n"
+ )
+
+ forrequiredin[
+ "out_grid:","out_3dflow:","out_init:","out_tort:","out_curv:",
+ "out_avs:","out_traj:","out_fract:","out_filetemp:","out_dir:",
+ "out_path:","out_time:","timesteps:","time_units:",
+ "flux_weight:","seed:","in-flow-boundary:","out-flow-boundary:",
+ "aperture:","porosity","density:","satur:",
+ "streamline_routing:"
+ ]:
+ ifparams[required]==None:
+ error="ERROR!!!\n%s not provided. Exiting\n\n"%(required)
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ # Check Initial conditions, make sure only 1 Initial condition is selected and that
+ # required parameters have been defined
+ print("--> Checking Initial Conditions")
+ initial_conditions=[
+ ("init_nf:","init_partn:"),("init_eqd:","init_npart:"),
+ ("init_fluxw:","init_totalnumber:"),("init_random:","in_randpart:"),
+ ("init_oneregion:","in_partn:","in_xmin:","in_ymin:","in_zmin:",
+ "in_xmax:","in_ymax:","in_zmax:"),
+ ("init_matrix:","inm_coord:","inm_nodeID:","inm_porosity:"),
+ ("init_well:","init_nodepart:")
+ ]
+ ic_selected=[]
+ foricininitial_conditions:
+ #print(ic,params[ic[0]])
+ ifparams[ic[0]]=="yes":
+ ic_selected.append(ic[0])
+ foriinic:
+ ifparams[i]==None:
+ error="Initial condition %s selected but %s not provided\n"%(
+ ic[0],i)
+ sys.stderr.write(error)
+ sys.exit(1)
+ iflen(ic_selected)>1:
+ error="ERROR!!! More than one initial condition defined\nExiting\n"
+ sys.stderr.write(error)
+ print("Selected Initial Conditions:\n:")
+ foricinic_selected:
+ print(ic)
+ print()
+ sys.exit(1)
+ eliflen(ic_selected)==0:
+ error="ERROR!!! No initial condition defined\nExiting\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ifparams["ControlPlane:"]!=None:
+ forrequiredin["control_out:","delta_Control:","flowdir:"]:
+ ifparams[required]==None:
+ error="Parameter %s required for ControlPlane\n"%required
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ifparams["tdrw:"]=="yes":
+ ifparams["time_units:"]!="seconds":
+ error="ERROR!!!You must use seconds for the time_units to run TDRW"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ forrequiredin["tdrw_porosity:","tdrw_diffcoeff:"]:
+ ifparams[required]==None:
+ error="Parameter %s required for tdrw\n"%required
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ ifparams["aperture:"]=="yes":
+ ifparams["aperture_type:"]==None:
+ error="Parameter aperture_type: required for aperture: yes\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ else:
+ ifnotos.path.isfile(params["aperture_file:"])oros.stat(
+ params["aperture_file:"]).st_size==0:
+ error="aperture_file: %s not found or empty\n"%params[
+ "aperture_file:"]
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ else:
+ ifparams["thickness:"]==None:
+ error="Parameter thickness: required for aperture: no:\n"
+ sys.stderr.write(error)
+ sys.exit(1)
+
+ print("--> Checking Initial Conditions Complete")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_sources/applications.rst.txt b/Documentation/sphinx-docs/build/html/_sources/applications.rst.txt
new file mode 100644
index 000000000..62b7d7bef
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/applications.rst.txt
@@ -0,0 +1,53 @@
+.. j_applications-chapter:
+
+Example Applications
+====================
+
+Carbon dioxide sequestration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+dfnWorks provides the framework necessary to perform multiphase simulations (such as flow and reactive transport) at the reservoir scale. A particular application, highlighted here, is sequestering |CO2| from anthropogenic sources and disposing it in geological formations such as deep saline aquifers and abandoned oil fields. Geological |CO2| sequestration is one of the principal methods under consideration to reduce carbon footprint in the atmosphere due to fossil fuels (Bachu, 2002; Pacala and Socolow, 2004). For safe and sustainable long-term storage of |CO2| and to prevent leaks through existing faults and fractured rock (along with the ones created during the injection process), understanding the complex physical and chemical interactions between |CO2| , water (or brine) and fractured rock, is vital. dfnWorks capability to study multiphase flow in a DFN can be used to study potential |CO2| migration through cap-rock, a potential risk associated with proposed subsurface storage of |CO2| in saline aquifers or depleted reservoirs. Moreover, using the reactive transport capabilities of PFLOTRAN coupled with cell-based transmissivity of the DFN allows one to study dynamically changing permeability fields with mineral precipitation and dissolution due to |CO2| –water interaction with rock.
+
+.. figure:: ./figures/time_co2.png
+ :scale: 50 %
+ :alt: alternate text
+ :align: center
+
+ *Temporal evolution of supercritical |CO2| displacing water in a meter cube DFN containing 24 fractures. The DFN is initially fully saturated with water, (top left time 0 hours) and supercritical |CO2| is slowly injected into the system from the bottom of the domain to displace the water for a total time of 10 h. There is an initial flush through the system during the first hour of the simulation, and then the rate of displacement decreases.*
+
+Shale energy extraction
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Hydraulic fracturing (fracking) has provided access to hydrocarbon trapped in low-permeability media, such as tight shales. The process involves injecting water at high pressures to reactivate existing fractures and also create new fractures to increase permeability of the shale allowing hydrocarbons to be extracted. However, the fundamental physics of why fracking works and its long term ramifications are not well understood. Karra et al. (2015) used dfnWorks to generate a typical production site and simulate production. Using this physics based model, they found good agreement with production field data and determined what physical mechanisms control the decline in the production curve.
+
+.. figure:: ./figures/well-pressure.png
+ :scale: 10 %
+ :alt: alternate text
+ :align: center
+
+ *Pressure in a well used for hydraulic fracturing.*
+
+Nuclear waste repository
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The Swedish Nuclear Fuel and Waste Management Company (SKB) has undertaken a detailed investigation of the fractured granite at the Forsmark, Sweden site as a potential host formation for a subsurface repository for spent nuclear fuel (SKB, 2011; Hartley and Joyce, 2013). The Forsmark area is about 120 km north of Stockholm in northern Uppland, and the repository is proposed
+to be constructed in crystalline bedrock at a depth of approximately 500 m. Based on the SKB site investigation, a statistical fracture model with multiple fracture sets was developed; detailed parameters of the Forsmark site model are in SKB (2011). We adopt a subset of the model that consist of three sets of background (non-deterministic) circular fractures whose orientations follow a Fisher distribution, fracture radii are sampled from a truncated power-law distribution, the transmissivity of the fractures is estimated using a power-law model based on the fracture radius, and the fracture aperture is related to the fracture size using the cubic law (Adler et al., 2012). Under such a formulation, the fracture apertures are uniform on each fracture, but vary among fractures. The network is generated in a cubic domain with sides of length one-kilometer. Dirichlet boundary conditions are imposed on the top (1 MPa) and bottom (2 MPa) of the domain to create a pressure gradient aligned with the vertical axis, and noflow boundary conditions are enforced along lateral boundaries.
+
+
+.. figure:: ./figures/forsmark_trajectories.png
+ :scale: 10 %
+ :alt: alternate text
+ :align: center
+
+ *Simulated particle trajectories in fractured granite at Forsmark, Sweden.*
+
+Sources:
+
+- Adler, P.M., Thovert, J.-F., Mourzenko, V.V., 2012. Fractured Porous Media. Oxford University Press, Oxford, United Kingdom.
+- Bachu, S., 2002. Sequestration of |CO2| in geological media in response to climate change: road map for site selection using the transform of the geological space into the |CO2| phase space. Energy Convers. Manag. 43, 87–102.
+- Hartley, L., Joyce, S., 2013. Approaches and algorithms for groundwater flow modeling in support of site investigations and safety assessment of the Fors- mark site, Sweden. J. Hydrol. 500, 200–216.
+- Karra, S., Makedonska, N., Viswanathan, H., Painter, S., Hyman, J., 2015. Effect of advective flow in fractures and matrix diffusion on natural gas production. Water Resour. Res., under review.
+- Pacala, S., Socolow, R., 2004. Stabilization wedges: solving the climate problem for the next 50 years with current technologies. Science 305, 968–972.
+- SKB, Long-Term Safety for the Final Repository for Spent Nuclear Fuel at Forsmark. Main Report of the SR-Site Project. Technical Report SKB TR-11-01, Swedish Nuclear Fuel and Waste Management Co., Stockholm, Sweden, 2011.
+
+.. |CO2| replace:: CO\ :sub:`2`
diff --git a/Documentation/sphinx-docs/build/html/_sources/dfnflow.rst.txt b/Documentation/sphinx-docs/build/html/_sources/dfnflow.rst.txt
new file mode 100644
index 000000000..082bf27ab
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/dfnflow.rst.txt
@@ -0,0 +1,156 @@
+.. _dfnflow-chapter:
+
+dfnFlow
+========
+*dfnFlow* involves using flow solver such as PFLOTRAN or FEHM. PFLOTRAN is recommended if a large number of fractures ( > O(1000)) are involved in a network. Using the function calls that are part of pydfnworks, one can create the mesh files needed to run PFLOTRAN. This will involve creating unstructured mesh file ``*uge`` as well as the boundary ``*ex`` files. Please see the PFLOTRAN user manual at http://www.pflotran.org under unstructured *explicit* format usage for further details. An example input file for PFLOTRAN is provided in the repository. Please use this as a starting point to build your input deck.
+
+Below is a sample input file. Refer to the PFLOTRAN user manual at http://www.pflotran.org for input parameter descriptions.
+
+.. code-block:: c
+
+ # Jan 13, 2014
+ # Nataliia Makedonska, Satish Karra, LANL
+ #================================================
+
+ SIMULATION
+ SIMULATION_TYPE SUBSURFACE
+ PROCESS_MODELS
+ SUBSURFACE_FLOW flow
+ MODE RICHARDS
+ /
+ /
+ END
+ SUBSURFACE
+
+ DFN
+
+ #=========================== discretization ===================================
+ GRID
+ TYPE unstructured_explicit full_mesh_vol_area.uge
+ GRAVITY 0.d0 0.d0 0.d0
+ END
+
+
+ #=========================== fluid properties =================================
+ FLUID_PROPERTY
+ DIFFUSION_COEFFICIENT 1.d-9
+ END
+
+ DATASET Permeability
+ FILENAME dfn_properties.h5
+ END
+
+ #=========================== material properties ==============================
+ MATERIAL_PROPERTY soil1
+ ID 1
+ POROSITY 0.25d0
+ TORTUOSITY 0.5d0
+ CHARACTERISTIC_CURVES default
+ PERMEABILITY
+ DATASET Permeability
+ /
+ END
+
+
+ #=========================== characteristic curves ============================
+ CHARACTERISTIC_CURVES default
+ SATURATION_FUNCTION VAN_GENUCHTEN
+ M 0.5d0
+ ALPHA 1.d-4
+ LIQUID_RESIDUAL_SATURATION 0.1d0
+ MAX_CAPILLARY_PRESSURE 1.d8
+ /
+ PERMEABILITY_FUNCTION MUALEM_VG_LIQ
+ M 0.5d0
+ LIQUID_RESIDUAL_SATURATION 0.1d0
+ /
+ END
+
+ #=========================== output options ===================================
+ OUTPUT
+ TIMES s 0.01 0.05 0.1 0.2 0.5 1
+ # FORMAT TECPLOT BLOCK
+ PRINT_PRIMAL_GRID
+ FORMAT VTK
+ MASS_FLOWRATE
+ MASS_BALANCE
+ VARIABLES
+ LIQUID_PRESSURE
+ PERMEABILITY
+ /
+ END
+
+ #=========================== times ============================================
+ TIME
+ INITIAL_TIMESTEP_SIZE 1.d-8 s
+ FINAL_TIME 1.d0 d==
+ MAXIMUM_TIMESTEP_SIZE 10.d0 d
+ STEADY_STATE
+ END
+
+ # REFERENCE_PRESSURE 1500000.
+
+ #=========================== regions ==========================================
+ REGION All
+ COORDINATES
+ -1.d20 -1.d20 -1.d20
+ 1.d20 1.d20 1.d20
+ /
+ END
+
+ REGION inflow
+ FILE pboundary_left_w.ex
+ END
+
+ REGION outflow
+ FILE pboundary_right_e.ex
+ END
+
+ #=========================== flow conditions ==================================
+ FLOW_CONDITION initial
+ TYPE
+ PRESSURE dirichlet
+ /
+ PRESSURE 1.01325d6
+ END
+
+
+ FLOW_CONDITION outflow
+ TYPE
+ PRESSURE dirichlet
+ /
+ PRESSURE 1.d6
+ END
+
+ FLOW_CONDITION inflow
+ TYPE
+ PRESSURE dirichlet
+ /
+ PRESSURE 2.d6
+ END
+
+ #=========================== condition couplers ===============================
+ # initial condition
+ INITIAL_CONDITION
+ FLOW_CONDITION initial
+ REGION All
+ END
+
+
+ BOUNDARY_CONDITION INFLOW
+ FLOW_CONDITION inflow
+ REGION inflow
+ END
+
+ BOUNDARY_CONDITION OUTFLOW
+ FLOW_CONDITION outflow
+ REGION outflow
+ END
+
+ #=========================== stratigraphy couplers ============================
+ STRATA
+ REGION All
+ MATERIAL soil1
+ END
+
+ END_SUBSURFACE
diff --git a/Documentation/sphinx-docs/build/html/_sources/dfngen.rst.txt b/Documentation/sphinx-docs/build/html/_sources/dfngen.rst.txt
new file mode 100644
index 000000000..6eaadaccb
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/dfngen.rst.txt
@@ -0,0 +1,27 @@
+.. _dfngen-chapter:
+
+dfnGen - C++ Generation Code
+#################################
+
+dfnGen creates the discrete fracture networks using the feature rejection algorithm for meshing (FRAM). Fractures can be created stochastically or as deterministic features.
+
+The detailed description of FRAM and the implemented methodology is in `\J. D. Hyman, C. W. Gable, S. L. Painter, and N. Makedonska. Conforming Delaunay triangulation of stochastically generated three dimensional discrete fracture networks: A feature rejection algorithm for meshing strategy. SIAM J. Sci. Comput., 36(4):A1871–A1894, 2014 `_.
+
+
+.. include:: dfngen_docs/dfngen_domain.inc
+
+.. include:: dfngen_docs/dfngen_fracture_ellipses.inc
+
+.. include:: dfngen_docs/dfngen_fracture_rectangles.inc
+
+.. include:: dfngen_docs/dfngen_user_fractures.inc
+
+..
+ .. include:: dfngen_docs/dfngen_hydro.inc
+
+Source Code Documentation (Doxygen_)
+**************************************************
+
+.. _Doxygen: dfnGen_docs/index.html
+
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/dfntrans.rst.txt b/Documentation/sphinx-docs/build/html/_sources/dfntrans.rst.txt
new file mode 100644
index 000000000..87a66bc89
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/dfntrans.rst.txt
@@ -0,0 +1,42 @@
+.. _dftrans-chapter:
+
+dfnTrans
+============
+
+dfnTrans is a method for resolving solute transport using control volume flow
+solutions obtained from dfnFlow on the unstructured mesh generated using dfnGen.
+We adopt a Lagrangian approach and represent a non-reactive conservative solute
+as a collection of indivisible passive tracer particles. Particle tracking
+methods (a) provide a wealth of information about the local flow field, (b) do
+not suffer from numerical dispersion, which is inherent in the discretizations
+of advection–dispersion equations, and (c) allow for the computation of each
+particle trajectory to be performed in an intrinsically parallel fashion if
+particles are not allowed to interact with one another or the fracture network.
+However, particle tracking on a DFN poses unique challenges that arise from (a)
+the quality of the flow solution, (b) the unstructured mesh representation of
+the DFN, and (c) the physical phenomena of interest. The flow solutions obtained
+from dfnFlow are locally mass conserving, so the particle tracking method does
+not suffer from the problems inherent in using Galerkin finite element codes.
+
+dfnTrans starts from reconstruction of local velocity field: Darcy fluxes
+obtained using dfnFlow are used to reconstruct the local velocity field, which
+is used for particle tracking on the DFN. Then, Lagrangian transport simulation
+is used to determine pathlines through the network and simulate transport. It is
+important to note that dfnTrans itself only solves for advective transport, but
+effects of longitudinal dispersion and matrix diffusion, sorption, and other
+retention processes are easily incorporated by post-processing particle
+trajectories.
+
+The detailed description of dfnTrans algorithm and implemented
+methodology is in `Makedonska, N., Painter, S. L., Bui, Q. M., Gable, C. W., &
+Karra, S. (2015). Particle tracking approach for transport in three-dimensional
+discrete fracture networks. Computational Geosciences, 19(5), 1123-1137.
+`_
+
+
+Documentation
+--------------
+Doxygen_
+
+.. _Doxygen: dfnTrans_docs/index.html
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/examples.rst.txt b/Documentation/sphinx-docs/build/html/_sources/examples.rst.txt
new file mode 100644
index 000000000..15065d38a
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/examples.rst.txt
@@ -0,0 +1,104 @@
+Examples
+=============================
+
+
+This section contains a few examples of DFN generated using dfnWorks. All required input files for these examples are contained in the folder dfnWorks/examples/. The focus of this document is to provide visual confirmation that new users of dfnWorks have the code set up correctly, can carry out the following runs and reproduce the following images. All images are rendered using Paraview, which can be obtained for free at http : //www.paraview.org/. The first two examples are simplest so it is recommended that the user proceed in the order presented here.
+
+All examples are in the examples/ directory. Within each subdirectory are the files required to run the example. The command line input is found in notes.txt. Be sure that you have created ~/test_output_files prior to running the examples.
+
+
+4_user_defined_rects
+--------------------------
+
+Location: examples/4_user_defined_rects/
+
+
+This test case consists of four user defined rectangular fractures within a a cubic domain with sides of length one meter. The network of four fractures, each colored by material ID. The computational mesh is overlaid on the fractures. This image is created by loading the file full_mesh.inp. located in the job folder into Paraview.
+
+.. figure:: figures/4_user_rectangles.png
+ :scale: 10 %
+ :alt: alternate text
+ :align: center
+
+ *The meshed network of four rectangular fractures.*
+
+High pressure (red) Dirichlet boundary conditions are applied on the edge of the single fracture along the boundary x = -0.5, and low pressure (blue) boundary conditions are applied on the edges of the two fractures at the boundary x = 0.5.
+This image is created by loading the file parsed_vtk/dfn_explicit-001.vtk into Paraview.
+
+
+Particles are inserted uniformly along the inlet fracture on the left side of the image.
+Particles exit the domain through the two horizontal fractures on the right side of the image.
+Due to the stochastic nature of the particle tracking algorithm, your pathlines might not be exactly the same as in this image.
+Trajectories are colored by the current velocity magnitude of the particle's velocity.
+Trajectories can be visualized by loading the files part\_*.inp, in the folder 4_user_rectangles/traj/trajectories/
+
+We have used the extract surface and tube filters in paraview for visual clarity.
+
+
+4_user_defined_ell_uniform
+--------------------------
+
+Location: examples/4_user_defined_ell_uniform/
+
+
+This test case consists of four user defined elliptical fractures within a a cubic domain with sides of length one meter. In this case the ellipses are approximated using 8 vertices. We have set the meshing resolution to be uniform by including the argument slope=0 into the mesh_networks function in run_explicit.py.
+
+.. figure:: figures/4_user_ellipses.png
+ :scale: 10 %
+ :alt: alternate text
+ :align: center
+
+ *The uniformly meshed network of four circular fractures.*
+
+
+
+exp: Exponentially Distributed fracture lengths
+-----------------------------------------------------
+
+Location: examples/exp/
+
+This test case consists of a family of fractures whose size is exponentially distributed with a minimum size of 1m and a maximum size of 50m. The domain is cubic with an edge length of 10m. All input parameters for the generator can be found in tests/gen_exponential_dist.dat. We have changed the flow direction to be aligned with the y-axis by modifying the PFLOTRAN input card dfn_explicit.in
+
+.. figure:: figures/exp_pressure.png
+ :scale: 10 %
+ :alt: alternate text
+ :align: center
+
+ *Pressure solution on with rectangular fractures whose lengths following a exponential distribution. Gradient is aligned with the Y-Axis*
+
+
+TPL: Truncated Power-Law
+----------------------------------
+
+Location: examples/TPL/
+
+This test case consists of two families whose sizes have a truncated power law distribution with a minimum size of 1m and a maximum size of 5m an exponent 2.6. The domain size is cubic with an edge length of 15m.
+
+.. figure:: figures/power_mesh.png
+ :scale: 20 %
+ :alt: alternate text
+ :align: center
+
+
+Graph-based pruning
+----------------------
+
+Location: examples/pruning/
+
+
+This example uses a graph representation of a DFN to isolate the 2-core. The pruned DFN has all dead end fractures of the network are removed. This example has two run_explicit.py scripts. The first creates the original DFN and identifies the 2-core using networkx (https://networkx.github.io/). The second meshes the DFN corresponding to the 2-core of the graph and then runs flow and transport. The 2 core network is in a sub-directory 2-core. The original network has 207 fractures and the 2-core has 79 fractures.
+
+.. figure:: figures/dfn_2_core.png
+ :scale: 30 %
+ :alt: alternate text
+ :align: center
+
+ *(left) Graph based on DFN topology. Each vertex is a fracture in the network. The inflow boundary is colored blue and the outflow is colored red. (right) 2-Core of the graph to the left.*
+
+.. figure:: figures/pruned_network.png
+ :scale: 5 %
+ :alt: alternate text
+ :align: center
+
+ *(left) Original DFN (right) DFN corresponding to the 2-core of the DFN to the left.*
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/gallery.rst.txt b/Documentation/sphinx-docs/build/html/_sources/gallery.rst.txt
new file mode 100644
index 000000000..73c5f8bbd
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/gallery.rst.txt
@@ -0,0 +1,42 @@
+dfnWorks Gallery
+=============================
+
+
+.. figure:: figures/TPL_pathlines.png
+ :alt: Figure Not Found
+ :align: center
+
+ *Particle Pathlines within a DFN composed of fractures whose radii follow a truncated powerlaw. Fractures are colored by Pressure.*
+
+.. figure:: figures/power_mesh.png
+ :alt: Figure Not Found
+ :align: center
+
+ *Mesh of a DFN composed of fractures whose radii follow a truncated powerlaw. Fractures are colored by Pressure.*
+
+.. figure:: figures/well-pressure.png
+ :alt: Figure Not Found
+ :align: center
+
+ *Pressure distribution with a hydraulic fracturing simulation*
+
+.. figure:: figures/dead-end_velocity_field.png
+ :alt: Figure Not Found
+ :align: center
+
+ *Pressure contours and velocity vector field within a dead-end fracture.*
+
+.. figure:: figures/dfn_graph.png
+ :alt: Figure Not Found
+ :align: center
+
+ *DFN along with it's graph representation*
+
+.. figure:: figures/in-fracture-variability_pathlines.png
+ :alt: Figure Not Found
+ :align: center
+
+ *DFN with internal aperture variability. Particle Pathlines are shown in purple.*
+
+
+
diff --git a/docs/_sources/index.rst.txt b/Documentation/sphinx-docs/build/html/_sources/index_docs.rst.txt
similarity index 100%
rename from docs/_sources/index.rst.txt
rename to Documentation/sphinx-docs/build/html/_sources/index_docs.rst.txt
diff --git a/Documentation/sphinx-docs/build/html/_sources/intro.rst.txt b/Documentation/sphinx-docs/build/html/_sources/intro.rst.txt
new file mode 100644
index 000000000..72c369583
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/intro.rst.txt
@@ -0,0 +1,260 @@
+Welcome To dfnWorks
+=========================
+
+dfnWorks is a parallelized computational suite to generate three-dimensional
+discrete fracture networks (DFN) and simulate flow and transport. Developed at
+Los Alamos National Laboratory, it has been used to study flow and transport
+in fractured media at scales ranging from millimeters to kilometers. The
+networks are created and meshed using dfnGen, which combines FRAM (the feature
+rejection algorithm for meshing) methodology to stochastically generate
+three-dimensional DFNs with the LaGriT meshing toolbox to create a high-quality
+computational mesh representation. The representation produces a conforming
+Delaunay triangulation suitable for high-performance computing finite volume
+solvers in an intrinsically parallel fashion. Flow through the network is
+simulated with dfnFlow, which utilizes the massively parallel subsurface flow
+and reactive transport finite volume code PFLOTRAN. A Lagrangian approach to
+simulating transport through the DFN is adopted within dfnTrans to determine
+pathlines and solute transport through the DFN. Applications of the dfnWorks
+suite include nuclear waste repository science, hydraulic fracturing and
+|CO2| sequestration.
+
+.. |CO2| replace:: CO\ :sub:`2`
+
+To run a workflow using the dfnWorks suite, the pydfnworks package is
+highly recommended. pydfnworks calls various tools in the dfnWorks suite with
+the aim to provide a seamless workflow for scientific applications of dfnWorks.
+
+Obtaining dfnWorks
+---------------------------
+dfnWorks can be downloaded from https://github.com/lanl/dfnWorks/
+
+A docker container of dfnWorks can be downloaded from https://hub.docker.com/r/ees16/dfnworks
+
+
+Citing dfnWorks
+---------------
+`Hyman, J. D., Karra, S., Makedonska, N., Gable, C. W., Painter, S. L., &
+Viswanathan, H. S. (2015). dfnWorks: A discrete fracture network framework
+for modeling subsurface flow and transport. Computers & Geosciences, 84,
+10-19. `_
+
+*BibTex:*
+
+.. code-block:: none
+
+ @article{hyman2015dfnWorks,
+ title={dfnWorks: A discrete fracture network framework
+ for modeling subsurface flow and transport},
+ author={Hyman, Jeffrey D and Karra, Satish and Makedonska,
+ Nataliia and Gable, Carl W and Painter, Scott L
+ and Viswanathan, Hari S},
+ journal={Computers \& Geosciences},
+ volume={84},
+ pages={10--19},
+ year={2015},
+ publisher={Elsevier}
+ }
+
+Versions
+-------------------
+
+v2.8 - Current
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- New Meshing Using Poisson Disc Sampling (Requires LaGriT v3.3)
+- Conforming Discrete Fracture Matrix Meshing
+- ECPM module using MAP DFN
+- Additional bug fixes
+- New dfnGraph model capabilities
+- TDRW matrix diffusion with finite matrix-block size
+
+v2.7
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Python based assignment of domain parameters, fracture families, user defined fractures
+- Interactive object interface
+- Updated for PFLOTRAN 4.0 compatability
+- Additional bug fixes
+- Increased detail of warning and errors
+
+
+v2.6
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Hydraulic aperture of fracture based on background stress field
+- Bug fixes
+
+
+v2.5
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- New Generation parameters, family orientation by trend/plunge and dip/strike
+- Define fracture families by region
+- Updated output report
+
+
+v2.4
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- New meshing technique (Poisson disc sampling)
+- Define fracture families by region
+- Updated output report
+- Well Package
+
+v2.3
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Bug fixes in LaGrit Meshing
+- Bug fixes in dfnTrans checking
+- Bug fixes in dfnTrans output
+- Expanded examples
+- Added PDF printing abilities
+
+
+v2.2
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- pydfnWorks updated for python3
+- Graph based (pipe-network approximations) for flow and transport
+- Bug fixes in LaGrit Meshing
+- Increased functionalities in pydfnworks including the path option
+- dfn2graph capabilities
+- FEHM flow solver
+- Streamline routing option in dfnTrans
+- Time Domain Random Walk in dfnTrans
+
+v2.1
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Bug fixes in LaGrit Meshing
+- Increased functionalities in pydfnworks including the path option
+
+v2.0
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- New dfnGen C++ code which is much faster than the Mathematica dfnGen. This code has successfully generated networks with 350,000+ fractures.
+- Increased functionality in the pydfnworks package for more streamlined workflow from dfnGen through visualization.
+
+
+About this manual
+------------------
+
+This manual comprises of information on setting up inputs to dfnGen, dfnTrans
+and PFLOTRAN, as well as details on the pydfnworks module: :ref:`pydfnworks
+`. Finally, the manual contains a short tutorial
+with prepared examples that can be found in the ``examples`` directory of the
+dfnWorks repository, and a description of some applications of the dfnWorks
+suite.
+
+Contact
+--------
+
+Please email dfnworks@lanl.gov with questions about dfnWorks. Please let us know if you publish using dfnWorks and we'll add it to the :ref:`Publication Page `
+
+Contributors
+-------------
+LANL
+^^^^^^^
+- Jeffrey D. Hyman
+- Matt Sweeney
+- Nataliia Makedonska
+- Carl Gable
+- Hari Viswanathan
+- Aric Hagberg
+- Shriram Srinivasan
+- Aidan Stansberry
+
+External
+^^^^^^^^^^^^^^
+- Satish Karra (PNNL)
+- Scott Painter (ORNL)
+- Quan Bui (now at LLNL)
+- Jeremy Harrod (now at Spectra Logic)
+- Thomas Sherman (University of Notre Dame)
+- Johannes Krotz (Oregon State University)
+- Yu Chen
+
+
+Copyright Information
+----------------------
+
+Documentation:
+
+LA-UR-17-22216
+
+Software copyright:
+
+LA-CC-17-027
+
+Contact Information : dfnworks@lanl.gov
+
+(or copyright) 2018 Triad National Security, LLC. All rights reserved.
+
+This program was produced under U.S. Government contract 89233218CNA000001
+for Los Alamos National Laboratory (LANL), which is operated by Triad
+National Security, LLC for the U.S. Department of Energy/National Nuclear
+Security Administration.
+
+All rights in the program are reserved by Triad National Security, LLC,
+and the U.S. Department of Energy/National Nuclear Security Administration.
+The Government is granted for itself and others acting on its behalf a
+nonexclusive, paid-up, irrevocable worldwide license in this material
+to reproduce, prepare derivative works, distribute copies to the public,
+perform publicly and display publicly, and to permit others to do so.
+
+
+The U.S. Government has rights to use, reproduce, and distribute this software.
+NEITHER THE GOVERNMENT NOR TRIAD 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 program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2 of the License, or (at your option)
+any later version. Accordingly, this program is distributed in the hope that it
+will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+License for more details.
+
+Additionally, redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following conditions are
+met:
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of Los Alamos National Security, LLC, Los Alamos
+National Laboratory, LANL, the U.S. Government, nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL
+SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+Additionally, this program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at your
+option) any later version. Accordingly, this program is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
+implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+
+.. dfnWorks documentation master file, created by Satish Karra Oct 6, 2016
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/output.rst.txt b/Documentation/sphinx-docs/build/html/_sources/output.rst.txt
new file mode 100644
index 000000000..9de1a0669
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/output.rst.txt
@@ -0,0 +1,386 @@
+.. _output-chapter:
+
+Run Files
+=============
+
+This section describes the contents and purpose of each file used in dfnWorks and their locations.
+
+dfnGen - output
+------------------------
+
+**connectivity.dat:**
+
+.. _connectivity.dat:
+
+Fracture connection list. Each row corresponds to a single fracture. The integers in that row are the fractures that fracture intersects with. These are the non-zero elements of the adjacency matrix.
+
+**DFN_output.txt:**
+
+.. _DFN_output.txt:
+
+Detailed information about fracture network. Output by DFNGen.
+
+**families.dat:**
+
+.. _families.dat:
+
+Information about fracture families. Produced by DFNGen.
+
+**input_generator.dat:**
+
+.. _input_generator.dat:
+
+Input file for DFN generator.
+
+**input_generator_clean.dat:**
+
+.. _input_generator_clean.dat:
+
+Abbreviated input file for DFN generator.
+
+**normal_vectors.dat:**
+
+.. _normal_vectors.dat:
+
+Normal vector of each fracture in the network.
+
+**params.txt:**
+
+.. _params.txt:
+
+Parameter information about the fracture network used for meshing. Includes number of fractures, h, visualmode, expected number of dudded points, and x,y,z dimensions of the domain.
+
+**poly_info.dat:**
+
+.. _poly_info.dat:
+
+Fracture information output by DFNGen. Format: Fracture Number, Family number, rotation angle for rotateln in LaGriT, x0, y0, z0, x1, y1, z1 (end points of line of rotation).
+
+**user_rects.dat:**
+
+.. _user_rects.dat:
+
+User defined rectangle file.
+
+**radii.dat:**
+
+.. _radii.dat:
+
+Concatentate file of fracture radii. Contains fractures that are removed due to isolation.
+
+
+**radii_Final.dat:**
+
+.. _radii_Final.dat:
+
+Concatentated file of final radii in the DFN.
+
+
+**rejections.dat:**
+
+.. _rejections.dat:
+
+Summary of rejection reasons.
+
+**rejectsPerAttempt.dat:**
+
+.. _rejectsPerAttempt.dat:
+
+Number of rejections per attempted fracture.
+
+
+**translations.dat:**
+
+.. _translations.dat:
+
+Fracture centriods.
+
+
+**triple_points.dat:**
+
+.. _triple_points.dat:
+
+x,y,z location of triple intersection points.
+
+
+**warningFileDFNGen.txt:**
+
+.. _warningFileDFNGen.txt:
+
+Warning file output by DFNGen.
+
+**intersection_list.dat:**
+
+.. _intersection_list.dat:
+
+List of intersections between fractures. Format is fracture1 fracture2 x y z length. Negative numbers correspond to intersections with boundaries.
+
+LaGrit - Output
+------------------
+
+**bound_zones.lgi:**
+
+.. _bound_zones.lgi:
+
+LaGriT run file to identify boundary nodes. Dumps zone files.
+
+**boundary_output.txt:**
+
+.. _boundary_output.txt:
+
+Output file from bound_zones.lgi.
+
+**finalmesh.txt:**
+
+.. _finalmesh.txt:
+
+Brief summary of final mesh.
+
+**full_mesh.inp:**
+
+.. _full_mesh.inp:
+
+Full DFN mesh in AVS format.
+
+**full_mesh.lg:**
+
+.. _full_mesh.lg:
+
+Full DFN mesh in LaGriT binary format.
+
+**full_mesh.uge:**
+
+.. _full_mesh.uge:
+
+Full DFN mesh in UGE format. NOTE volumes are not correct in this file. This file is processed by convert_uge to create full_mesh_vol_area.uge, which has the correct volumes.
+
+**full_mesh_viz.inp:**
+
+.. _full_mesh_viz.inp:
+
+**intersections:**
+
+.. _intersections:
+
+Directory containing intersection avs files output by the generator and used by LaGrit.
+
+**lagrit_logs:**
+
+.. _lagrit_logs:
+
+Directory of output files from individual meshing.
+
+**logx3dgen:**
+
+.. _logx3dgen:
+
+LaGriT output.
+
+**outx3dgen:**
+
+.. _outx3dgen:
+
+LaGriT output.
+
+**parameters:**
+
+.. _parameters:
+
+Directory of parameter*.mgli files used for fracture meshing.
+
+
+**polys:**
+
+.. _polys:
+
+Subdirectory contiaining AVS file for polygon boundaries.
+
+**tri_fracture.stor:**
+
+.. _tri_fracture.stor:
+
+FEHM stor file. Information about cell volume and area.
+
+**user_function.lgi:**
+
+.. _user_function.lgi:
+
+Function used by LaGriT for meshing. Defines coarsening gradient.
+
+
+PFLOTRAN - output
+--------------------
+
+Fracture based aperture value for the DFN. Used to rescale volumes in full_mesh_vol_area.uge.
+
+**cellinfo.dat:**
+
+.. _cellinfo.dat:
+
+Mesh information output by PFLOTRAN.
+
+**dfn_explicit-000.vtk:**
+
+.. _dfn_explicit-000.vtk:
+
+VTK file of initial conditions of PFLOTRAN. Mesh is not included in this file.
+
+**dfn_explicit-001.vtk:**
+
+.. _dfn_explicit-001.vtk:
+
+VTK file of steady-state solution of PFLOTRAN. Mesh is not included in this file.
+
+**dfn_explicit-mas.dat:**
+
+.. _dfn_explicit-mas.dat:
+
+pflotran information file.
+
+**dfn_explicit.in:**
+
+.. _dfn_explicit.in:
+
+pflotran input file.
+
+**_dfn_explicit.out:**
+
+.. _dfn_explicit.out:
+
+pflotran output file.
+
+**dfn_properties.h5:**
+
+.. _dfn_properties.h5:
+
+h5 file of fracture network properties, permeability, used by pflotran.
+
+
+Full DFN mesh with limited attributes in AVS format.
+
+**full_mesh_vol_area.uge:**
+
+.. _full_mesh_vol_area.uge:
+
+Full DFN in uge format. Volumes and areas have been corrected.
+
+**materialid.dat:**
+
+.. _materialid.dat:
+
+Material ID (Fracture Number) for every node in the mesh.
+
+**parsed_vtk:**
+
+.. _parsed_vtk:
+
+Directory of pflotran results.
+
+**perm.dat:**
+
+.. _perm.dat:
+
+Fracture permeabilities in FEHM format. Each fracture is listed as a zone, starting index at 7.
+
+**pboundary_back_n.ex:**
+
+.. _pboundary_back_n.ex:
+
+Boundary file for back of the domain used by PFLOTRAN.
+
+**pboundary_bottom.ex:**
+
+.. _pboundary_bottom.ex:
+
+Boundary file for bottom of the domain used by PFLOTRAN.
+
+**pboundary_front_s.ex:**
+
+.. _pboundary_front_s.ex:
+
+Boundary file for front of the domain used by PFLOTRAN.
+
+**pboundary_left_w.ex:**
+
+.. _pboundary_left_w.ex:
+
+Boundary file for left side of the domain used by PFLOTRAN.
+
+**pboundary_right_e.ex:**
+
+.. _pboundary_right_e.ex:
+
+Boundary file for right of the domain used by PFLOTRAN.
+
+**pboundary_top.ex:**
+
+.. _pboundary_top.ex:
+
+Boundary file for top of the domain used by PFLOTRAN.
+
+.. dfnTrans
+.. -------------
+
+.. **allboundaries.zone:**
+
+.. .. _allboundaries.zone:
+
+.. Concatenated file of all zone files.
+
+.. **darcyvel.dat:**
+
+.. .. _darcyvel.dat:
+
+.. Concatenated file of darcy velocities output by PFLOTRAN.
+
+.. **dfnTrans_output_dir:**
+
+.. .. _dfnTrans_output_dir:
+
+.. Outpur directory from DFNTrans. Particle travel times, trajectories, and reconstructed Velocities are in this directory.
+
+.. **PTDFN_control.dat:**
+
+.. .. _PTDFN_control.dat:
+
+.. Input file for DFNTrans.
+
+.. **pboundary_back_n.zone:**
+
+.. .. _pboundary_back_s.zone:
+
+.. Boundary zone file for the back of the domain. Normal vector (0,1,0) +- pi/2
+
+.. **pboundary_bottom.zone:**
+
+.. .. _pboundary_bottom.zone:
+
+.. Boundary zone file for the bottom of the domain. Normal vector (0,0,-1) +- pi/2
+
+.. **pboundary_front_s.zone:**
+
+.. .. _pboundary_front_n.zone:
+
+.. Boundary zone file for the front of the domain. Normal vector (0,-1,0) +- pi/2
+
+
+.. **pboundary_left_w.zone:**
+
+.. .. _pboundary_left_w.zone:
+
+.. Boundary zone file for the left side of the domain. Normal vector (-1,0,0) +- pi/2
+
+
+.. **pboundary_right_e.zone:**
+
+.. .. _pboundary_right_e.zone:
+
+
+.. Boundary zone file for the bottom of the domain. Normal vector (1,0,0) +- pi/2
+
+.. **pboundary_top.zone:**
+
+.. .. _pboundary_top.zone:
+
+.. Boundary zone file for the top of the domain. Normal vector (0,0,1) +- pi/2
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/publications.rst.txt b/Documentation/sphinx-docs/build/html/_sources/publications.rst.txt
new file mode 100644
index 000000000..7d8f99dbd
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/publications.rst.txt
@@ -0,0 +1,127 @@
+.. _publications-chapter:
+
+dfnWorks Publications
+======================
+
+The following are publications that use *dfnWorks*:
+
+#. `\J. D. Hyman, C. W. Gable, S. L. Painter, and N. Makedonska. Conforming Delaunay triangulation of stochastically generated three dimensional discrete fracture networks: A feature rejection algorithm for meshing strategy. SIAM J. Sci. Comput. (2014) `_.
+
+#. `\R.S. Middleton, J.W. Carey, R.P. Currier, J. D. Hyman, Q. Kang, S. Karra, J. Jimenez-Martınez, M.L. Porter, and H.S. Viswanathan. Shale gas and non-aqueous fracturing fluids: Opportunities and challenges for supercritical CO2. Applied Energy, (2015) `_.
+
+#. `\J. D. Hyman, S. L. Painter, H. Viswanathan, N. Makedonska, and S. Karra. Influence of injection mode on transport properties in kilometer-scale three-dimensional discrete fracture networks. Water Resources Research (2015) `_.
+
+#. `\S. Karra, Nataliia Makedonska, Hari S Viswanathan, Scott L Painter, and Jeffrey D. Hyman. Effect of advective flow in fractures and matrix diffusion on natural gas production. Water Resources Research (2015) `_.
+
+#. `\J. D. Hyman, S. Karra, N. Makedonska, C. W Gable, S. L Painter, and H. S Viswanathan. dfnWorks: A discrete fracture network framework for modeling subsurface flow and transport. Computers & Geosciences (2015) `_.
+
+#. `\H. S. Viswanathan, J. D. Hyman, S. Karra, J.W. Carey, M. L. Porter, E. Rougier, R. P. Currier,Q. Kang, L. Zhou, J. Jimenez-Martınez, N. Makedonska, L. Chen, and R. S. Middleton. Using Discovery Science To Increase Efficiency of Hydraulic Fracturing While Reducing Water Usage, chapter 4, pages 71–88. ACS Publications, (2016) `_.
+
+#. `\N. Makedonska, S. L Painter, Q. M Bui, C. W Gable, and S. Karra. Particle tracking approach for transport in three-dimensional discrete fracture networks. Computational Geosciences (2015) `_.
+
+#. `\D. O’Malley, S. Karra, R. P. Currier, N. Makedonska, J. D. Hyman, and H. S. Viswanathan. Where does water go during hydraulic fracturing? Groundwater (2016) `_.
+
+#. `\J. D. Hyman, J Jiménez-Martínez, HS Viswanathan, JW Carey, ML Porter, E Rougier, S Karra, Q Kang, L Frash, L Chen, et al. Understanding hydraulic fracturing: a multi-scale problem. Phil. Trans. R. Soc. A, (2016) `_.
+
+#. `\G. Aldrich, J. D. Hyman, S. Karra, C. W. Gable, N. Makedonska, H. Viswanathan, J.Woodring, and B. Hamann. Analysis and visualization of discrete fracture networks using a flow topology graph. IEEE Transactions on Visualization and Computer Graphics (2017) `_.
+
+#. `\N. Makedonska, J. D. Hyman, S. Karra, S. L. Painter, C.W. Gable, and H. S. Viswanathan. Evaluating the effect of internal aperture variability on transport in kilometer scale discrete fracture networks. Advances in Water Resources (2016) `_.
+
+#. `\J. D. Hyman, G. Aldrich, H. Viswanathan, N. Makedonska, and S. Karra. Fracture size and transmissivity correlations: Implications for transport simulations in sparse three-dimensional discrete fracture networks following a truncated power law distribution of fracture size. Water Resources Research (2016) `_.
+
+#. `\H. Djidjev, D. O’Malley, H. Viswanathan, J. D. Hyman, S. Karra, and G. Srinivasan. Learning on graphs for predictions of fracture propagation, flow and transport. In 2017 IEEE International Parallel and Distributed Processing Symposium Workshops (IPDPSW) (2017) `_.
+
+#. `\J. D. Hyman, A. Hagberg, G. Srinivasan, J. Mohd-Yusof, and H. Viswanathan. Predictions of first passage times in sparse discrete fracture networks using graph-based reductions. Phys. Rev. E, 96:013304, Jul `_.
+
+#. `\T Hadgu, S. Karra, N. Makedonska, J. D. Hyman, K. Klise, H. S. Viswanathan, and Y.Wang. A comparative study of discrete fracture network and equivalent continuum models for simulating flow and transport in the far field of a hypothetical nuclear waste repository in crystalline host rock. J. Hydrology, 2017 `_.
+
+#. `\V. Romano, J. D. Hyman, S. Karra, A. J. Valocchi, M. Battaglia, and S. Bigi. Numerical modeling of fluid flow in a fault zone: a case of study from majella mountain (Italy). Energy Procedia, 125:556 – 560, 2017 `_.
+
+#. `\M. Valera, Z. Guo, P. Kelly, S. Matz, A. Cantu, A.G. Percus, J. D. Hyman, G. Srinivasan, and H.S. Viswanathan. Machine learning for graph-based representations of three-dimensional discrete fracture networks. Computational Geosciences, (2018) `_.
+
+#. `\M. K. Mudunuru, S. Karra, N. Makedonska, and T. Chen. Sequential geophysical and flow inversion to characterize fracture networks in subsurface systems. Statistical Analysis and Data Mining: The ASA Data Science Journal (2017) `_.
+
+#. `\J. D. Hyman, Satish Karra, J. William Carey, Carl W. Gable, Hari Viswanathan, Esteban Rougier, and Zhou Lei. Discontinuities in effective permeability due to fracture percolation. Mechanics of Materials (2018) `_.
+
+#. `\S. Karra, D. O’Malley, J. D. Hyman, H.S. Viswanathan, and G. Srinivasan. Modeling flow and transport in fracture networks using graphs. Phys. Rev. E, (2018) `_.
+
+#. `\J. D. Hyman and J. Jimenéz-Martínez. Dispersion and mixing in three-dimensional discrete fracture networks: Nonlinear interplay between structural and hydraulic heterogeneity. Water Resources Research (2018) `_.
+
+#. `\D. O’Malley, S. Karra, J. D. Hyman, H. Viswanathan, and G. Srinivasan. Efficient Monte Carlo with graph-based subsurface flow and transport models. Water Resour. Res., (2018) `_.
+
+#. `\G. Srinivasan, J. D. Hyman, D. Osthus, B. Moore, D. O’Malley, S. Karra, E Rougier, A. Hagberg, A. Hunter, and H. S. Viswanathan. Quantifying topological uncertainty in fractured systems using graph theory and machine learning. Scientific Reports, (2018) `_.
+
+#. `\H. S. Viswanathan, J. D. Hyman, S. Karra, D. O’Malley, S. Srinivasan, A. Hagberg, and G. Srinivasan. Advancing graph-based algorithms for predicting flow and transport in fractured rock. Water Resour. Res., (2018) `_.
+
+#. `\S. Srinivasan, J. D. Hyman, S. Karra, D. O’Malley, H. Viswanathan, and G. Srinivasan. Robust system size reduction of discrete fracture networks: A multi-fidelity method that preserves transport characteristics. Computational Geosciences, 2018 `_.
+
+#. `\J. D. Hyman, Aric Hagberg, Dave Osthus, Shriram Srinivasan, Hari Viswanathan, and Gowri Srinivasan. Identifying backbones in three-dimensional discrete fracture net- works: A bipartite graph-based approach. Multiscale Modeling & Simulation (2018) `_.
+
+#. `\G. Aldrich, J. Lukasczyk, J. D. Hyman, G. Srinivasan, H. Viswanathan, C. Garth, H. Leitte, J. Ahrens, and B. Hamann. A query-based framework for searching, sorting, and exploring data ensembles. Computing in Science Engineering, (2018) `_.
+
+#. `\T. Sherman, J. D. Hyman, D. Bolster, N. Makedonska, and G. Srinivasan. Characterizing the impact of particle behavior at fracture intersections in three-dimensional discrete fracture networks. Physical Review E (2019) `_.
+
+#. `\J. D. Hyman, M. Dentz, A. Hagberg, and P. Kang. Linking structural and transport properties in three-dimensional fracture networks. J. Geophys. Res. Sol. Ea., (2019) `_.
+
+#. `\S. Srinivasan, S. Karra, J. D. Hyman, H. Viswanathan, and G. Srinivasan. Model reduction for fractured porous media: A machine-learning approach for identifying main flow pathways. Computational Geosciences (2018) `_.
+
+#. `\N. Makedonska, J.D, Hyman, E. Kwicklis, K. Birdsell, Conference Proceedings, Discrete Fracture Network Modeling and Simulation of Subsurface Transport for the Topopah Spring Aquifer at Pahute Mesa, 2nd International Discrete Fracture Network Engineering (2018) `_.
+
+#. `\N. Makedonska, C.W. Gable, R. Pawar, Conference Proceedings, Merging Discrete Fracture Network Meshes With 3D Continuum Meshes of Rock Matrix: A Novel Approach, 2nd International Discrete Fracture Network Engineering (2018) `_.
+
+#. `\A. Frampton, J.D, Hyman, L. Zou, Advective transport in discrete fracture networks with connected and disconnected textures representing internal aperture variability, Water Resources Research (2019) `_.
+
+#. `\J.D. Hyman, J. Jiménez-Martínez, C. W. Gable, P. H. Stauffer, and R. J. Pawar. Characterizing the Impact of Fractured Caprock Heterogeneity on Supercritical CO2 Injection. Transport in Porous Media (2019) `_.
+
+#. `\J.D. Hyman, H. Rajaram, S. Srinivasan, N. Makedonska, S. Karra, H. Viswanathan, H., & G. Srinivasan, (2019). Matrix diffusion in fractured media: New insights into power law scaling of breakthrough curves. Geophysical Research Letters (2019) `_.
+
+#. `\J.D. Hyman, M. Dentz, A. Hagberg, & P. K. Kang, (2019). Emergence of Stable Laws for First Passage Times in Three-Dimensional Random Fracture Networks. Physical Review Letters (2019) `_.
+
+#. `\M. R. Sweeney, C. W. Gable, S. Karra, P. H. Stauffer, R. J. Pawar, J. D. Hyman (2019). Upscaled discrete fracture matrix model (UDFM): an octree-refined continuum representation of fractured porous mediaComputational Geosciences (2019) `_.
+
+#. `\T. Sherman, J. D. Hyman, M. Dentz, and D. Bolster. Characterizing the influence of fracture density on network scale transport. J. Geophys. Res. Sol. Ea., (2019) `_.
+
+#. `\D. Osthus, J. D. Hyman, S. Karra, N. Panda, and G. Srinivasan. A probabilistic clustering approach for identifying primary subnetworks of discrete fracture networks with quantified uncertainty. SIAM/ASA Journal on Uncertainty Quantification, (2020) `_.
+
+#. `\V. Romano, S. Bigi, F. Carnevale, J. D. Hyman, S. Karra, A. Valocchi, M. Tartarello, and M. Battaglia. Hydraulic characterization of a fault zone from fracture distribution. Journal of Structural Geology, (2020) `_.
+
+#. `\S. Srinivasan, E. Cawi, J. D. Hyman, D. Osthus, A. Hagberg, H. Viswanathan, and G. Srinivasan. Physics-informed machine-learning for backbone identification in discrete fracture networks. Comput. Geosci., (2020) `_.
+
+
+#. `\N. Makedonska, S. Karra, H.S. Viswanathan, and G.D. Guthrie,. Role of Interaction between Hydraulic and Natural Fractures on Production. Journal of Natural Gas Science and Engineering (2020). `_.
+
+#. `\H. Pham, R. Parashar, N. Sund, and K. Pohlmann. A Method to Represent a Well in a Three‐dimensional Discrete Fracture Network Model. Groundwater. (2020) `_.
+
+
+#. `\M.R. Sweeney, and J.D. Hyman. Stress effects on flow and transport in three‐dimensional fracture networks. Journal of Geophysical Research: Solid Earth. (2020) `_.
+
+#. `\J.D. Hyman. Flow Channeling in Fracture Networks: Characterizing the Effect of Density on Preferential Flow Path Formation. Water Resources Research (2020): e2020WR027986. `_.
+
+
+#. `\H. Pham, R. Parashar, N. Sund, and K. Pohlmann. Determination of fracture apertures via calibration of three-dimensional discrete-fracture-network models: application to Pahute Mesa, Nevada National Security Site, USA. Hydrogeol J (2020). `_.
+
+#. `\S. Srinivasan, D. O’Malley, J. D. Hyman, s. Karra, H. S. Viswanathan, and G. Srinivasan Transient flow modeling in fractured media using graphs. (2020) Physical Review E. `_.
+
+#. `\Liangchao Zou and Vladimir Cvetkovic. Inference of Transmissivity in Crystalline Rock Using Flow Logs Under Steady‐State Pumping: Impact of Multiscale Heterogeneity. Water Resources Research (2020) `_.
+
+#. `\P. K. Kang, J. D. Hyman, W. S. Han, & M. Dentz, Anomalous Transport in Three‐Dimensional Discrete Fracture Networks: Interplay between Aperture Heterogeneity and Injection Modes. Water Resources Research (2020) `_.
+
+#. `Hyman, J. D., & Dentz, M. Transport upscaling under flow heterogeneity and matrix-diffusion in three-dimensional discrete fracture networks. Advances in Water Resources (2021) `_.
+
+#. `T. Sherman, G. Sole-Mari, J. Hyman, M. R. Sweeney, D. Vassallo, and D. Bolster. Characterizing Reactive Transport Behavior in a Three-Dimensional Discrete Fracture Network. Transport in Porous Media (2021) `_.
+
+#. `S. Shriram, D. O’Malley, M. K. Mudunuru, M. R. Sweeney, J. D. Hyman, S. Karra, L. Frash et al. A machine learning framework for rapid forecasting and history matching in unconventional reservoirs. (2021) Scientific Reports `_.
+
+
+#. `J. D. Hyman, M. R. Sweeney, L. P. Frash, J. W. Carey, and H. S. Viswanathan. Scale‐Bridging in Three‐Dimensional Fracture Networks: Characterizing the Effects of Variable Fracture Apertures on Network‐Scale Flow Channelization. Geophysical Research Letters (2021) `_.
+
+#. `Liangchao Zou and Vladimir Cvetkovic. Evaluation of Flow‐Log Data From Crystalline Rocks With Steady‐State Pumping and Ambient Flow. Geophysical Research Letters (2021) `_.
+
+#. `H. Ushijima-Mwesigwa, J. D. Hyman, A. Hagberg, I. Safro, S. Karra, C. W. Gable, M. R. Sweeney, and G. Srinivasan. Multilevel graph partitioning for three-dimensional discrete fracture network flow simulations. Mathematical Geosciences (2021) `_.
+
+#. `Yingtao Hu, Wenjie Xu, Liangtong Zhan, Liangchao Zou, and Yunmin Chen. "Modeling of solute transport in a fracture-matrix system with a three-dimensional discrete fracture network." Journal of Hydrology (2021) `_.
+
+#. `C. R. Romano, R. T. Williams; Evolution of Fault-Zone Hydromechanical Properties in Response to Different Cementation Processes. Lithosphere (2022) `_.
+
+#. `J. Krotz, M.R. Sweeney, C.W. Gable, J.D. Hyman, & J.M. Restrepo, (2022). Variable resolution Poisson-disk sampling for meshing discrete fracture networks. Journal of Computational and Applied Mathematics (2022) `_.
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/pydfnFlow.rst.txt b/Documentation/sphinx-docs/build/html/_sources/pydfnFlow.rst.txt
new file mode 100644
index 000000000..0dadd3402
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/pydfnFlow.rst.txt
@@ -0,0 +1,26 @@
+.. _dfnWorks-python-chapter-dfnFlow:
+
+pydfnworks: dfnFlow
+========================================
+
+DFN Class functions used in flow simulations (PFLOTRAN and FEHM)
+
+Running Flow : General
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnFlow.flow
+ :members:
+
+Running Flow: PFLOTRAN
+^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnFlow.pflotran
+ :members: lagrit2pflotran, pflotran, parse_pflotran_vtk_python, pflotran_cleanup, write_perms_and_correct_volumes_areas, zone2ex
+
+Running Flow: FEHM
+^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnFlow.fehm
+ :members: correct_stor_file, fehm
+
+Processing Flow
+^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnFlow.mass_balance
+ :members: effective_perm
diff --git a/Documentation/sphinx-docs/build/html/_sources/pydfnGen.rst.txt b/Documentation/sphinx-docs/build/html/_sources/pydfnGen.rst.txt
new file mode 100644
index 000000000..f8662c407
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/pydfnGen.rst.txt
@@ -0,0 +1,200 @@
+.. _dfnWorks-python-chapter-dfnGen:
+
+pydfnworks: dfnGen
+========================================
+
+DFN Class functions used in network generation and meshing
+
+dfnGen
+-------
+
+Adding Fracture Families
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGen.generation.input_checking
+ :members: add_fracture_family
+ :noindex:
+
+Example:
+
+.. code-block:: python
+
+ DFN.add_fracture_family(shape="ell",
+ distribution="tpl",
+ alpha=1.8,
+ min_radius=1.0,
+ max_radius=5.0,
+ kappa=1.0,
+ theta=0.0,
+ phi=0.0,
+ aspect=2,
+ beta_distribution=1,
+ beta=45.0,
+ p32=1.1,
+ hy_variable='aperture',
+ hy_function='correlated',
+ hy_params={
+ "alpha": 10**-5,
+ "beta": 0.5
+ })
+
+
+Adding User Fractures
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions
+ :members: add_user_fract
+ :noindex:
+
+Example:
+
+.. code-block:: python
+
+ DFN.add_user_fract(shape='ell',
+ radii=.4,
+ aspect_ratio=1,
+ translation=[0.2, 0, 0.2],
+ normal_vector=[0, 0, 1],
+ number_of_vertices=8,
+ aperture=1.0e-5)
+
+Adding User Fractures From a File
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions
+ :members: add_user_fract_from_file
+ :noindex:
+
+Example:
+
+.. code-block:: python
+
+ DFN.add_user_fract_from_file(shape="poly",
+ filename = f'{src_path}/polygons.dat',
+ permeability = 1e-12)
+
+
+Print Parameter Information
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Note: Some of these functions are automatically called when processing the input.
+
+.. automodule:: pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions
+ :members: print_user_fracture_information
+
+.. automodule:: pydfnworks.dfnGen.generation.input_checking
+ :members: print_domain_parameters, print_family_information
+
+Processing Generator Input
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: pydfnworks.dfnGen.generation.input_checking
+ :members: check_input
+ :noindex:
+
+Running the Generator
+^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGen.generation.generator
+ :members: dfn_gen, make_working_directory, create_network, grab_polygon_data
+
+Analysis of Generated DFN
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGen.generation.output_report.gen_output
+ :members: output_report
+
+Additional Information on the Modification of Hydraulic Properties of the DFN
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Hydraulic properties can be assigned to fractures based on four different models. One can assign hydraulic aperture :math:`b`, permeability, :math:`k`, or transmissivity :math:`T`. Below we present the functions for hydraulic aperture, but the equations for other values are the same.
+
+The first is a perfectly correlated model where the hydraulic property is a function of the fracture radius
+
+.. math::
+ b = \alpha r^\beta
+
+The keyword for this model is correlated.
+
+The second is a semi-correlated correlated model where the hydraulic property is a function of the fracture radius
+
+.. math::
+ \log_{10}(b) = \log_{10}(\alpha r^\beta) + \sigma \mathcal{N}(0,1)
+
+where a stochastic term is included into the correlated model
+to account for uncertainty and variability between fractures of the same size. The strength of the stochastic term is determined by the variance of a log-normal distribution :math:`\sigma` and the stochastic term is an independent identically distributed random variable sampled from a normal distribution with mean 0 and variance 1, :math:`\mathcal{N}(0,1)`. This model results in a log-normal distribution of fracture transmissivities around a positively cor- related power law mean. We refer to this model as semicorrelated.
+
+The keyword for this model is semi-correlated.
+
+The third model assumes that there is no correlation between the fracture size and transmissivity and all values are independent identically distributed random variables from a log-normal distribution with speci- fied mean :math:`\mu` and variance :math:`\sigma`,
+
+.. math::
+ \log_{10}(b) = \mu + \sigma \mathcal{N}(0,1)
+
+The keyword for this model is log-normal.
+
+The fourth model represents an assumption that in addition to no relationship between size and hydraulic properties, there is no variation between fractures
+
+.. math::
+ b = \mu
+
+The keyword for this model is constant.
+
+Notes:
+
+See Hyman et al. 2016 “Fracture size and transmissivity correlations: Implications for transport simulations in sparse three-dimensional discrete fracture networks following a truncated power law distribution of fracture size” Water Resources Research for more details
+
+Changes in hydraulic properties are assigned when defining a fracture family or user defined fracture. User defined fractures currently only support constant hydraulic properties.
+
+
+Modification of hydraulic properties of the DFN based on background stress field
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: pydfnworks.dfnGen.generation.stress
+ :members: stress_based_apertures
+
+Meshing - LaGriT
+-----------------
+
+Primary DFN meshing driver
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGen.meshing.mesh_dfn.mesh_dfn
+ :members: mesh_network
+
+
+Meshing helper methods
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGen.meshing.mesh_dfn.mesh_dfn_helper
+ :members: inp2gmv, inp2vtk_python, create_mesh_links, run_lagrit_script
+
+.. automodule:: pydfnworks.dfnGen.meshing.add_attribute_to_mesh
+ :members: add_variable_to_mesh
+
+
+
+UDFM
+--------
+
+Creating an upscaled mesh of the DFN (UDFM)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGen.meshing.udfm.map2continuum
+ :members: map_to_continuum
+
+.. automodule:: pydfnworks.dfnGen.meshing.udfm.upscale
+ :members: upscale
+
+.. automodule:: pydfnworks.dfnGen.meshing.udfm.false_connections
+ :members: check_false_connections
+
+Map-DFN upscaling
+--------------------------
+.. automodule:: pydfnworks.dfnGen.meshing.mapdfn_ecpm.mapdfn_ecpm
+ :members: mapdfn_ecpm
+
+DFM
+--------
+
+Creating a conforming DFM mesh DFN
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGen.meshing.dfm.mesh_dfm
+ :members: mesh_dfm
+
+
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/pydfnGraph.rst.txt b/Documentation/sphinx-docs/build/html/_sources/pydfnGraph.rst.txt
new file mode 100644
index 000000000..4965abfbb
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/pydfnGraph.rst.txt
@@ -0,0 +1,26 @@
+.. _dfnWorks-python-chapter-dfnGraph:
+
+pydfnworks: dfnGraph
+========================================
+
+DFN Class functions used in graph analysis and pipe-network simulations
+
+General Graph Functions
+^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGraph.dfn2graph
+ :members: create_graph, dump_json_graph, load_json_graph, plot_graph, dump_fractures, add_fracture_source, add_fracture_target
+
+.. automodule:: pydfnworks.dfnGraph.pruning
+ :members: k_shortest_paths_backbone, greedy_edge_disjoint
+
+
+
+
+
+Graph-Based Flow and Transport
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnGraph.graph_flow
+ :members: run_graph_flow
+
+.. automodule:: pydfnworks.dfnGraph.graph_transport
+ :members: run_graph_transport
diff --git a/Documentation/sphinx-docs/build/html/_sources/pydfnTrans.rst.txt b/Documentation/sphinx-docs/build/html/_sources/pydfnTrans.rst.txt
new file mode 100644
index 000000000..93f99325a
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/pydfnTrans.rst.txt
@@ -0,0 +1,11 @@
+.. _dfnWorks-python-chapter-dfnTrans:
+
+pydfnworks: dfnTrans
+========================================
+
+DFN Class functions used in particle transport simulations (DFNTrans)
+
+Running Transport Simulations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. automodule:: pydfnworks.dfnTrans.transport
+ :members:
diff --git a/Documentation/sphinx-docs/build/html/_sources/pydfnWorks-well.rst.txt b/Documentation/sphinx-docs/build/html/_sources/pydfnWorks-well.rst.txt
new file mode 100644
index 000000000..1d6276a10
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/pydfnWorks-well.rst.txt
@@ -0,0 +1,11 @@
+.. _dfnWorks-python-chapter-well-package:
+
+pydfnworks: Well Package
+========================================
+
+DFN Class functions used for well package
+
+dfnWorks - Well Package
+--------------------------
+.. automodule:: pydfnworks.dfnGen.well_package.wells
+ :members: tag_well_in_mesh, find_well_intersection_points, combine_well_boundary_zones, cleanup_wells
diff --git a/Documentation/sphinx-docs/build/html/_sources/pydfnworks.rst.txt b/Documentation/sphinx-docs/build/html/_sources/pydfnworks.rst.txt
new file mode 100644
index 000000000..0913ea686
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/pydfnworks.rst.txt
@@ -0,0 +1,226 @@
+.. _dfnWorks-python-chapter:
+
+pydfnworks: the dfnWorks python package
+========================================
+
+The pydfnworks package allows the user to run dfnWorks from the command line and call dfnWorks within other python scripts. Because pydfnworks is a package, users can call individual methods from the package.
+
+pydfnworks must be installed by the user prior to running dfnworks (:ref:`pydfnWorks install `)
+
+
+Running dfnWorks from the command line using pydfnWorks
+---------------------------------------------------------
+The recommended way to run dfnWorks is using a python call on the command line, or running the script in your favorite IDE.
+
+.. code-block:: bash
+
+ $ python driver.py
+
+
+The script ``driver.py`` is the python control file that contains the workflow of the particular simulation. Below is a basic example taken from the 4_user_rects_example example:
+
+.. code-block:: python
+
+ from pydfnworks import *
+ import os
+
+ src_path = os.getcwd()
+ jobname = src_path + "/output"
+ dfnFlow_file = src_path+ '/dfn_explicit.in'
+ dfnTrans_file = src_path + '/PTDFN_control.dat'
+
+ DFN = DFNWORKS(jobname,
+ dfnFlow_file=dfnFlow_file,
+ dfnTrans_file=dfnTrans_file,
+ ncpu=8)
+
+ DFN.params['domainSize']['value'] = [1.0, 1.0, 1.0]
+ DFN.params['h']['value'] = 0.050
+
+ DFN.add_user_fract(shape='rect',
+ radii=0.6,
+ translation=[-0.4, 0, 0],
+ normal_vector=[0, 0, 1],
+ permeability=1.0e-12)
+
+ DFN.add_user_fract(shape='rect',
+ radii=1.0,
+ aspect_ratio=.65,
+ translation=[0, 0, 0],
+ normal_vector=[1, 0, 0],
+ permeability=1.0e-12)
+
+ DFN.add_user_fract(shape='rect',
+ radii=.6,
+ translation=[0.4, 0, 0.2],
+ normal_vector=[0, 0, 1],
+ permeability=2.0e-12)
+
+ DFN.add_user_fract(shape='rect',
+ radii=.6,
+ translation=[0.4, 0, -0.2],
+ normal_vector=[0, 0, 1],
+ permeability=1.0e-12)
+
+ DFN.make_working_directory(delete=True)
+ DFN.check_input()
+ DFN.print_domain_parameters()
+
+ DFN.create_network()
+ DFN.mesh_network()
+
+ DFN.dfn_flow()
+ DFN.dfn_trans()
+
+
+The DFNWORKS class
+---------------------------
+
+Within the python script, a DFN (discrete fracture network) object is created to control the model workflow. Data and model functions are stored on this object, allowing the user to both access information about the DFN while debugging, as well as call functions for modelling everything from network generation to transport modelling. Arguments for creating the DFN object are listed below. Additional arguments and functions required to create the DFN are discussed in other sections of this manual.
+
+Default Arguments:
+
+.. code-block:: python
+
+ from pydfnworks import *
+
+ DFN = DFNWORKS(jobname = None, #required
+ ncpu = 4,
+ dfnGen_file = None, #automatically generated
+ dfnFlow_file = None, #required for DFN.dfn_flow()
+ dfnTrans_file = None, #required for DFN.dfn_trans()
+ path = None,
+ prune_file = None,
+ flow_solver = 'PFLOTRAN',
+ inp_file = 'full_mesh.inp',
+ uge_file = 'full_mesh.uge',
+ mat_file = 'materialid.dat',
+ stor_file = None,
+ vtk_file = None,
+ num_nodes = None,
+ mesh_type = 'dfn',
+ cell_based_aperture = False)
+
+
+jobname
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Description: (Mandatory) Path of the simulation directory. Must be a valid path. The path is stored in ``DFN.jobname`` of the DFN object
+
+Type: string
+
+Example:
+
+.. code-block:: python
+
+ import os
+ src_path = os.getcwd()
+ jobname = src_path + "/output"
+
+ncpu
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Description: Number of processors to be used in the simulation. Stored as ``DFN.ncpu``.
+
+Type: integer
+
+Example:
+
+.. code-block:: python
+
+ ncpu = 8
+
+
+dfnFlow_file/dfnGen_file/dfnTrans_file
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. note:: dfnGen_file is depreciated, file name is automatically specified
+
+Description: (Mandatory) Path of the input file containing run files for dfnGen, dfnFlow (PFLOTRAN/FEHM/AMANZI), and dfnTrans. This file is parsed and the paths contained within are stored as ``DFN.dfnGen_file``, ``DFN.dfnFlow_file``, and ``DFN.dfnTrans_file``. The local path for the files (string after the final ``/`` are stored as ``DFN.local_dfnGen_file``, ``DFN.local_dfnFlow_file``, and ``DFN.local_dfnTrans_file``.
+
+Type: string
+
+Example:
+
+.. code-block:: python
+
+ dfnGen_file = 'gen_4_user_rectangles.dat'
+ dfnFlow_file = 'dfn_explicit.in'
+ dfnTrans_file = 'PTDFN_control.dat'
+
+
+path
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Description: Path to parent directory. Useful for multiple runs using the same network with different meshing techniques, hydraulic properties, flow simulations, or pruned networks. Path is stored as ``DFN.path``.
+
+Type: string
+
+Example:
+
+.. code-block:: python
+
+ path = '/dfnWorks/work/4_user_rects_example'
+
+prune_file
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Description: Path to ascii file of fractures to be retained (not removed) in the network after pruning. See the pruning example for a workflow demonstration.
+
+Type: string
+
+Example:
+
+.. code-block:: python
+
+ prune_file = '/dfnWorks/work/pruning_example/2_core.dat'
+
+.. note:: To prune the network, include ``DFN.mesh_network(prune=True)`` in the python run file.
+
+
+flow_solver
+^^^^^^^^^^^^^^^
+Description: Either 'PFLOTRAN' or 'FEHM'
+
+Example:
+
+.. code-block:: python
+
+ flow_solver = 'PFLOTRAN'
+
+
+cell_based_aperture
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Description: Toggle if the fracture apertures are cell based. If the option is included, then the workflow will assign cell-based apertures and permeabilities from the files ``aper_node.dat`` and ``perm_node.dat``. These files consist of two columns, with a single line header value. The first column is the node number. The second column is the aperture/permeability value. See the See the in_fracture_var example for a workflow demonstration.
+
+Type: Boolean
+
+Example:
+
+.. code-block:: python
+
+ cell_based_aperture = True
+
+additional arguments
+^^^^^^^^^^^^^^^^^^^^^
+Descriptions: additional arguments that have not been described here will likely not be changed by the user.
+
+
+pydfnWorks : Modules
+------------------------
+Information about the various pieces of pydfnworks is found in
+
+:ref:`pydfnGen ` - Network generation, meshing, and analysis
+
+:ref:`pydfnFlow ` - Flow simulations using PFLOTRAN and FEHM
+
+:ref:`pydfnTrans ` - Particle Tracking
+
+:ref:`pydfnGraph ` - Graph-based analysis and pipe-network simulations
+
+:ref:`Well-Package ` - Well simulations
+
+.. note:: There are additional required arguments for network generation described in :ref:`dfnGen `
+
+Detailed Doxygen Documentation
+----------------------------------
+Doxygen_
+
+.. _Doxygen: pydfnWorks_docs/index.html
+
diff --git a/Documentation/sphinx-docs/build/html/_sources/setup.rst.txt b/Documentation/sphinx-docs/build/html/_sources/setup.rst.txt
new file mode 100644
index 000000000..0d3a4f7c5
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_sources/setup.rst.txt
@@ -0,0 +1,198 @@
+.. _pydfnworks-setup:
+
+Setting and Running up dfnWorks
+================================
+
+Docker
+------------------------------
+The easiest way to get started with dfnWorks is using our docker container (https://hub.docker.com/r/ees16/dfnworks).
+
+If you do not already have Docker installed on your machine,
+visit `Getting Started with Docker `_.
+
+The dfnWorks Docker image can be pulled from DockerHub using:
+
+.. code-block:: bash
+
+ $ docker pull ees16/dfnworks:latest
+
+
+Running the dfnWorks container
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The base command for running the dfnWorks container is:
+
+.. code-block:: bash
+
+ docker run -ti ees16/dfnworks:latest
+
+However, to exchange files between the host and container, we will need to mount
+a volume.
+
+The option ``-v LOCAL_FOLDER:/dfnWorks/work`` will allow all files present in the
+container folder ``dfnWorks/work`` to be exposed to ``LOCAL_FOLDER``, where
+``LOCAL_FOLDER`` is the absolute path to a folder on your machine.
+
+With this is place, the final command for running the Docker container is:
+
+**On macOS:**
+
+.. code-block:: bash
+
+ docker run -ti -v :/dfnWorks/work ees16/dfnworks:latest
+
+Native build from github repository
+------------------------------------------
+
+This document contains instructions for setting up dfnWorks natively on your
+machine. To setup dfnWorks using Docker instead, see the next section.
+
+Clone the dnfWorks repository
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. code-block:: bash
+
+ $ git clone https://github.com/lanl/dfnWorks.git
+
+
+Fix paths in test directory
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Fix the pathnames in files throughout pydfnworks. This can be done automatically by running the script ``fix_paths.py``:
+
+.. code-block:: bash
+
+ $ cd dfnWorks/pydfnworks/bin/
+ $ python fix_paths.py
+
+Set the LagriT, PETSC, PFLOTRAN, Python, and FEHM paths
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Before executing dfnWorks,** the following paths must be set:
+
+- dfnWorks_PATH: the dfnWorks repository folder
+- PETSC_DIR and PETSC_ARCH: PETSC environmental variables
+- PFLOTRAN_EXE: Path to PFLOTRAN executable
+- PYTHON_EXE: Path to python executable
+- LAGRIT_EXE: Path to LaGriT executable
+
+.. code-block:: bash
+
+ $ vi dfnWorks/pydfnworks/pydfnworks/paths.py
+
+For example:
+
+.. code-block:: python
+
+ os.environ['dfnWorks_PATH'] = '/home/username/dfnWorks/'
+
+Alternatively, you can create a ``.dfnworksrc`` file in your home directory with the following format
+
+.. code-block:: bash
+
+ {
+ "dfnworks_PATH": "/src/dfnworks-main/",
+ "PETSC_DIR": "/src/petsc",
+ "PETSC_ARCH": "arch-darwin-c-debug",
+ "PFLOTRAN_EXE": "/src/pflotran/src/pflotran/pflotran",
+ "PYTHON_EXE": "/anaconda3/bin/python",
+ "LAGRIT_EXE": "/bin/lagrit",
+ "FEHM_EXE": "//src/xfehm_v3.3.1"
+ }
+
+
+Installing pydfnworks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Go up into the pydfnworks sub-directory:
+
+.. code-block:: bash
+
+ $ cd dfnWorks/pydfnworks/
+
+Complie The pydfnWorks Package:
+
+.. code-block:: bash
+
+ $ python setup.py bdist_wheel
+
+
+Install on Your Local Machine:
+
+.. code-block:: bash
+
+ $ python -m pip install dist/pydfnworks-2.6-py3-none-any.whl
+
+**Note that the python version in dist/ needs to be consistent with the current release**
+
+Installation Requirements for Native Build
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Tools that you will need to run the dfnWorks work flow are described in
+this section. VisIt and ParaView, which enable visualization of desired
+quantities on the DFNs, are optional, but at least one of them is highly
+recommended for visualization. CMake is also optional but allows faster IO
+processing using C++.
+
+Operating Systems
+*****************************
+
+dfnWorks currently runs on Macs and Unix machine running Ubuntu.
+
+Python
+*****************************
+
+pydfnworks uses Python 3. We recommend using
+the Anaconda 3 distribution of Python, available at https://www.continuum.io/.
+pydfnworks requires the following python modules: ``numpy``, ``h5py``, ``scipy``, ``matplotlib``, ``multiprocessing``, ``argparse``, ``shutil``, ``os``, ``sys``, ``networkx``, ``subprocess``, ``glob``, ``networkx``, ``fpdf``, and ``re``.
+
+
+LaGriT
+******
+The LaGriT_ meshing toolbox is used to create a high resolution computational
+mesh representation of the DFN in parallel. An algorithm for conforming
+Delaunay triangulation is implemented so that fracture intersections are
+coincident with triangle edges in the mesh and Voronoi control volumes are
+suitable for finite volume flow solvers such as FEHM and PFLOTRAN.
+
+.. _LaGriT: https://lagrit.lanl.gov
+
+PFLOTRAN
+********
+PFLOTRAN_ is a massively parallel subsurface flow and reactive transport
+code. PFLOTRAN solves a system of partial differential equations for
+multiphase, multicomponent and multi-scale reactive flow and transport in
+porous media. The code is designed to run on leadership-class supercomputers
+as well as workstations and laptops.
+
+.. _PFLOTRAN: http://pflotran.org
+
+FEHM
+****
+FEHM_ is a subsurface multiphase flow code developed at Los Alamos National
+Laboratory.
+
+.. _FEHM: https://fehm.lanl.gov
+
+CMake
+*****************************
+CMake_ is an open-source, cross-platform family of tools designed to build,
+test and package software. It is needed to use C++ for processing files at a
+bottleneck IO step of dfnWorks. Using C++ for this file processing optional
+but can greatly increase the speed of dfnWorks for large fracture networks.
+Details on how to use C++ for file processing are in the scripts section of
+this documentation.
+
+.. _CMake: https://cmake.org
+
+Paraview
+*****************************
+
+Paraview_ is a parallel, open-source visualisation software. PFLOTRAN can
+output in ``.xmf`` and ``.vtk`` format. These can be imported in Paraview
+for visualization. While not required for running dfnWorks, Paraview is
+very helpful for visualizing dfnWorks simulations.
+
+Instructions for downloading and installing Paraview_ can be found at
+http://www.paraview.org/download/
+
+.. _Paraview: http://www.paraview.org
+
diff --git a/Documentation/sphinx-docs/build/html/_static/_sphinx_javascript_frameworks_compat.js b/Documentation/sphinx-docs/build/html/_static/_sphinx_javascript_frameworks_compat.js
new file mode 100644
index 000000000..81415803e
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/_sphinx_javascript_frameworks_compat.js
@@ -0,0 +1,123 @@
+/* Compatability shim for jQuery and underscores.js.
+ *
+ * Copyright Sphinx contributors
+ * Released under the two clause BSD licence
+ */
+
+/**
+ * small helper function to urldecode strings
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
+ */
+jQuery.urldecode = function(x) {
+ if (!x) {
+ return x
+ }
+ return decodeURIComponent(x.replace(/\+/g, ' '));
+};
+
+/**
+ * small helper function to urlencode strings
+ */
+jQuery.urlencode = encodeURIComponent;
+
+/**
+ * This function returns the parsed url parameters of the
+ * current request. Multiple values per key are supported,
+ * it will always return arrays of strings for the value parts.
+ */
+jQuery.getQueryParameters = function(s) {
+ if (typeof s === 'undefined')
+ s = document.location.search;
+ var parts = s.substr(s.indexOf('?') + 1).split('&');
+ var result = {};
+ for (var i = 0; i < parts.length; i++) {
+ var tmp = parts[i].split('=', 2);
+ var key = jQuery.urldecode(tmp[0]);
+ var value = jQuery.urldecode(tmp[1]);
+ if (key in result)
+ result[key].push(value);
+ else
+ result[key] = [value];
+ }
+ return result;
+};
+
+/**
+ * highlight a given string on a jquery object by wrapping it in
+ * span elements with the given class name.
+ */
+jQuery.fn.highlightText = function(text, className) {
+ function highlight(node, addItems) {
+ if (node.nodeType === 3) {
+ var val = node.nodeValue;
+ var pos = val.toLowerCase().indexOf(text);
+ if (pos >= 0 &&
+ !jQuery(node.parentNode).hasClass(className) &&
+ !jQuery(node.parentNode).hasClass("nohighlight")) {
+ var span;
+ var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
+ if (isInSVG) {
+ span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
+ } else {
+ span = document.createElement("span");
+ span.className = className;
+ }
+ span.appendChild(document.createTextNode(val.substr(pos, text.length)));
+ node.parentNode.insertBefore(span, node.parentNode.insertBefore(
+ document.createTextNode(val.substr(pos + text.length)),
+ node.nextSibling));
+ node.nodeValue = val.substr(0, pos);
+ if (isInSVG) {
+ var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
+ var bbox = node.parentElement.getBBox();
+ rect.x.baseVal.value = bbox.x;
+ rect.y.baseVal.value = bbox.y;
+ rect.width.baseVal.value = bbox.width;
+ rect.height.baseVal.value = bbox.height;
+ rect.setAttribute('class', className);
+ addItems.push({
+ "parent": node.parentNode,
+ "target": rect});
+ }
+ }
+ }
+ else if (!jQuery(node).is("button, select, textarea")) {
+ jQuery.each(node.childNodes, function() {
+ highlight(this, addItems);
+ });
+ }
+ }
+ var addItems = [];
+ var result = this.each(function() {
+ highlight(this, addItems);
+ });
+ for (var i = 0; i < addItems.length; ++i) {
+ jQuery(addItems[i].parent).before(addItems[i].target);
+ }
+ return result;
+};
+
+/*
+ * backward compatibility for jQuery.browser
+ * This will be supported until firefox bug is fixed.
+ */
+if (!jQuery.browser) {
+ jQuery.uaMatch = function(ua) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
+ /(webkit)[ \/]([\w.]+)/.exec(ua) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
+ /(msie) ([\w.]+)/.exec(ua) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
+ [];
+
+ return {
+ browser: match[ 1 ] || "",
+ version: match[ 2 ] || "0"
+ };
+ };
+ jQuery.browser = {};
+ jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
+}
diff --git a/Documentation/sphinx-docs/build/html/_static/basic.css b/Documentation/sphinx-docs/build/html/_static/basic.css
new file mode 100644
index 000000000..f316efcb4
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/basic.css
@@ -0,0 +1,925 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+ clear: both;
+}
+
+div.section::after {
+ display: block;
+ content: '';
+ clear: left;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+ width: 100%;
+ font-size: 90%;
+}
+
+div.related h3 {
+ display: none;
+}
+
+div.related ul {
+ margin: 0;
+ padding: 0 0 0 10px;
+ list-style: none;
+}
+
+div.related li {
+ display: inline;
+}
+
+div.related li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+ padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ float: left;
+ width: 230px;
+ margin-left: -100%;
+ font-size: 90%;
+ word-wrap: break-word;
+ overflow-wrap : break-word;
+}
+
+div.sphinxsidebar ul {
+ list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+ margin-left: 20px;
+ list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+ margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar #searchbox form.search {
+ overflow: hidden;
+}
+
+div.sphinxsidebar #searchbox input[type="text"] {
+ float: left;
+ width: 80%;
+ padding: 0.25em;
+ box-sizing: border-box;
+}
+
+div.sphinxsidebar #searchbox input[type="submit"] {
+ float: left;
+ width: 20%;
+ border-left: none;
+ padding: 0.25em;
+ box-sizing: border-box;
+}
+
+
+img {
+ border: 0;
+ max-width: 100%;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li p.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 1.3em;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+ width: 100%;
+}
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable ul {
+ margin-top: 0;
+ margin-bottom: 0;
+ list-style-type: none;
+}
+
+table.indextable > tbody > tr > td > ul {
+ padding-left: 0em;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+div.modindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+/* -- domain module index --------------------------------------------------- */
+
+table.modindextable td {
+ padding: 2px;
+ border-collapse: collapse;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+div.body {
+ min-width: 360px;
+ max-width: 800px;
+}
+
+div.body p, div.body dd, div.body li, div.body blockquote {
+ -moz-hyphens: auto;
+ -ms-hyphens: auto;
+ -webkit-hyphens: auto;
+ hyphens: auto;
+}
+
+a.headerlink {
+ visibility: hidden;
+}
+
+a:visited {
+ color: #551A8B;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink,
+caption:hover > a.headerlink,
+p.caption:hover > a.headerlink,
+div.code-block-caption:hover > a.headerlink {
+ visibility: visible;
+}
+
+div.body p.caption {
+ text-align: inherit;
+}
+
+div.body td {
+ text-align: left;
+}
+
+.first {
+ margin-top: 0 !important;
+}
+
+p.rubric {
+ margin-top: 30px;
+ font-weight: bold;
+}
+
+img.align-left, figure.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, figure.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, figure.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+img.align-default, figure.align-default, .figure.align-default {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ text-align: center;
+}
+
+.align-default {
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar,
+aside.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 1px solid #ddb;
+ padding: 7px;
+ background-color: #ffe;
+ width: 40%;
+ float: right;
+ clear: right;
+ overflow-x: auto;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+}
+
+nav.contents,
+aside.topic,
+div.admonition, div.topic, blockquote {
+ clear: left;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+nav.contents,
+aside.topic,
+div.topic {
+ border: 1px solid #ccc;
+ padding: 7px;
+ margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+}
+
+div.admonition dt {
+ font-weight: bold;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+div.body p.centered {
+ text-align: center;
+ margin-top: 25px;
+}
+
+/* -- content of sidebars/topics/admonitions -------------------------------- */
+
+div.sidebar > :last-child,
+aside.sidebar > :last-child,
+nav.contents > :last-child,
+aside.topic > :last-child,
+div.topic > :last-child,
+div.admonition > :last-child {
+ margin-bottom: 0;
+}
+
+div.sidebar::after,
+aside.sidebar::after,
+nav.contents::after,
+aside.topic::after,
+div.topic::after,
+div.admonition::after,
+blockquote::after {
+ display: block;
+ content: '';
+ clear: both;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ border: 0;
+ border-collapse: collapse;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-default {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table caption span.caption-number {
+ font-style: italic;
+}
+
+table caption span.caption-text {
+}
+
+table.docutils td, table.docutils th {
+ padding: 1px 8px 1px 5px;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 1px solid #aaa;
+}
+
+th {
+ text-align: left;
+ padding-right: 5px;
+}
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px;
+}
+
+table.citation td {
+ border-bottom: none;
+}
+
+th > :first-child,
+td > :first-child {
+ margin-top: 0px;
+}
+
+th > :last-child,
+td > :last-child {
+ margin-bottom: 0px;
+}
+
+/* -- figures --------------------------------------------------------------- */
+
+div.figure, figure {
+ margin: 0.5em;
+ padding: 0.5em;
+}
+
+div.figure p.caption, figcaption {
+ padding: 0.3em;
+}
+
+div.figure p.caption span.caption-number,
+figcaption span.caption-number {
+ font-style: italic;
+}
+
+div.figure p.caption span.caption-text,
+figcaption span.caption-text {
+}
+
+/* -- field list styles ----------------------------------------------------- */
+
+table.field-list td, table.field-list th {
+ border: 0 !important;
+}
+
+.field-list ul {
+ margin: 0;
+ padding-left: 1em;
+}
+
+.field-list p {
+ margin: 0;
+}
+
+.field-name {
+ -moz-hyphens: manual;
+ -ms-hyphens: manual;
+ -webkit-hyphens: manual;
+ hyphens: manual;
+}
+
+/* -- hlist styles ---------------------------------------------------------- */
+
+table.hlist {
+ margin: 1em 0;
+}
+
+table.hlist td {
+ vertical-align: top;
+}
+
+/* -- object description styles --------------------------------------------- */
+
+.sig {
+ font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+}
+
+.sig-name, code.descname {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+.sig-name {
+ font-size: 1.1em;
+}
+
+code.descname {
+ font-size: 1.2em;
+}
+
+.sig-prename, code.descclassname {
+ background-color: transparent;
+}
+
+.optional {
+ font-size: 1.3em;
+}
+
+.sig-paren {
+ font-size: larger;
+}
+
+.sig-param.n {
+ font-style: italic;
+}
+
+/* C++ specific styling */
+
+.sig-inline.c-texpr,
+.sig-inline.cpp-texpr {
+ font-family: unset;
+}
+
+.sig.c .k, .sig.c .kt,
+.sig.cpp .k, .sig.cpp .kt {
+ color: #0033B3;
+}
+
+.sig.c .m,
+.sig.cpp .m {
+ color: #1750EB;
+}
+
+.sig.c .s, .sig.c .sc,
+.sig.cpp .s, .sig.cpp .sc {
+ color: #067D17;
+}
+
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+ list-style: decimal;
+}
+
+ol.loweralpha {
+ list-style: lower-alpha;
+}
+
+ol.upperalpha {
+ list-style: upper-alpha;
+}
+
+ol.lowerroman {
+ list-style: lower-roman;
+}
+
+ol.upperroman {
+ list-style: upper-roman;
+}
+
+:not(li) > ol > li:first-child > :first-child,
+:not(li) > ul > li:first-child > :first-child {
+ margin-top: 0px;
+}
+
+:not(li) > ol > li:last-child > :last-child,
+:not(li) > ul > li:last-child > :last-child {
+ margin-bottom: 0px;
+}
+
+ol.simple ol p,
+ol.simple ul p,
+ul.simple ol p,
+ul.simple ul p {
+ margin-top: 0;
+}
+
+ol.simple > li:not(:first-child) > p,
+ul.simple > li:not(:first-child) > p {
+ margin-top: 0;
+}
+
+ol.simple p,
+ul.simple p {
+ margin-bottom: 0;
+}
+
+aside.footnote > span,
+div.citation > span {
+ float: left;
+}
+aside.footnote > span:last-of-type,
+div.citation > span:last-of-type {
+ padding-right: 0.5em;
+}
+aside.footnote > p {
+ margin-left: 2em;
+}
+div.citation > p {
+ margin-left: 4em;
+}
+aside.footnote > p:last-of-type,
+div.citation > p:last-of-type {
+ margin-bottom: 0em;
+}
+aside.footnote > p:last-of-type:after,
+div.citation > p:last-of-type:after {
+ content: "";
+ clear: both;
+}
+
+dl.field-list {
+ display: grid;
+ grid-template-columns: fit-content(30%) auto;
+}
+
+dl.field-list > dt {
+ font-weight: bold;
+ word-break: break-word;
+ padding-left: 0.5em;
+ padding-right: 5px;
+}
+
+dl.field-list > dd {
+ padding-left: 0.5em;
+ margin-top: 0em;
+ margin-left: 0em;
+ margin-bottom: 0em;
+}
+
+dl {
+ margin-bottom: 15px;
+}
+
+dd > :first-child {
+ margin-top: 0px;
+}
+
+dd ul, dd table {
+ margin-bottom: 10px;
+}
+
+dd {
+ margin-top: 3px;
+ margin-bottom: 10px;
+ margin-left: 30px;
+}
+
+.sig dd {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+.sig dl {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+dl > dd:last-child,
+dl > dd:last-child > :last-child {
+ margin-bottom: 0;
+}
+
+dt:target, span.highlighted {
+ background-color: #fbe54e;
+}
+
+rect.highlighted {
+ fill: #fbe54e;
+}
+
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+.versionmodified {
+ font-style: italic;
+}
+
+.system-message {
+ background-color: #fda;
+ padding: 5px;
+ border: 3px solid red;
+}
+
+.footnote:target {
+ background-color: #ffa;
+}
+
+.line-block {
+ display: block;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.line-block .line-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+ font-family: sans-serif;
+}
+
+.accelerator {
+ text-decoration: underline;
+}
+
+.classifier {
+ font-style: oblique;
+}
+
+.classifier:before {
+ font-style: normal;
+ margin: 0 0.5em;
+ content: ":";
+ display: inline-block;
+}
+
+abbr, acronym {
+ border-bottom: dotted 1px;
+ cursor: help;
+}
+
+.translated {
+ background-color: rgba(207, 255, 207, 0.2)
+}
+
+.untranslated {
+ background-color: rgba(255, 207, 207, 0.2)
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+ overflow: auto;
+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
+}
+
+pre, div[class*="highlight-"] {
+ clear: both;
+}
+
+span.pre {
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ -webkit-hyphens: none;
+ hyphens: none;
+ white-space: nowrap;
+}
+
+div[class*="highlight-"] {
+ margin: 1em 0;
+}
+
+td.linenos pre {
+ border: 0;
+ background-color: transparent;
+ color: #aaa;
+}
+
+table.highlighttable {
+ display: block;
+}
+
+table.highlighttable tbody {
+ display: block;
+}
+
+table.highlighttable tr {
+ display: flex;
+}
+
+table.highlighttable td {
+ margin: 0;
+ padding: 0;
+}
+
+table.highlighttable td.linenos {
+ padding-right: 0.5em;
+}
+
+table.highlighttable td.code {
+ flex: 1;
+ overflow: hidden;
+}
+
+.highlight .hll {
+ display: block;
+}
+
+div.highlight pre,
+table.highlighttable pre {
+ margin: 0;
+}
+
+div.code-block-caption + div {
+ margin-top: 0;
+}
+
+div.code-block-caption {
+ margin-top: 1em;
+ padding: 2px 5px;
+ font-size: small;
+}
+
+div.code-block-caption code {
+ background-color: transparent;
+}
+
+table.highlighttable td.linenos,
+span.linenos,
+div.highlight span.gp { /* gp: Generic.Prompt */
+ user-select: none;
+ -webkit-user-select: text; /* Safari fallback only */
+ -webkit-user-select: none; /* Chrome/Safari */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* IE10+ */
+}
+
+div.code-block-caption span.caption-number {
+ padding: 0.1em 0.3em;
+ font-style: italic;
+}
+
+div.code-block-caption span.caption-text {
+}
+
+div.literal-block-wrapper {
+ margin: 1em 0;
+}
+
+code.xref, a code {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
+ background-color: transparent;
+}
+
+.viewcode-link {
+ float: right;
+}
+
+.viewcode-back {
+ float: right;
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ margin: -1px -10px;
+ padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+ vertical-align: middle;
+}
+
+div.body div.math p {
+ text-align: center;
+}
+
+span.eqno {
+ float: right;
+}
+
+span.eqno a.headerlink {
+ position: absolute;
+ z-index: 1;
+}
+
+div.math:hover a.headerlink {
+ visibility: visible;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+@media print {
+ div.document,
+ div.documentwrapper,
+ div.bodywrapper {
+ margin: 0 !important;
+ width: 100%;
+ }
+
+ div.sphinxsidebar,
+ div.related,
+ div.footer,
+ #top-link {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_static/css/badge_only.css b/Documentation/sphinx-docs/build/html/_static/css/badge_only.css
new file mode 100644
index 000000000..c718cee44
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/css/badge_only.css
@@ -0,0 +1 @@
+.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff
new file mode 100644
index 000000000..6cb600001
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2
new file mode 100644
index 000000000..7059e2314
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff
new file mode 100644
index 000000000..f815f63f9
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2
new file mode 100644
index 000000000..f2c76e5bd
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.eot b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.eot
new file mode 100644
index 000000000..e9f60ca95
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.eot differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.svg b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.svg
new file mode 100644
index 000000000..855c845e5
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.svg
@@ -0,0 +1,2671 @@
+
+
+
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.ttf b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.ttf
new file mode 100644
index 000000000..35acda2fa
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.ttf differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.woff b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.woff
new file mode 100644
index 000000000..400014a4b
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.woff differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.woff2
new file mode 100644
index 000000000..4d13fc604
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold-italic.woff b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold-italic.woff
new file mode 100644
index 000000000..88ad05b9f
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold-italic.woff differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold-italic.woff2 b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold-italic.woff2
new file mode 100644
index 000000000..c4e3d804b
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold-italic.woff2 differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold.woff b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold.woff
new file mode 100644
index 000000000..c6dff51f0
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold.woff differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold.woff2 b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold.woff2
new file mode 100644
index 000000000..bb195043c
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-bold.woff2 differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal-italic.woff b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal-italic.woff
new file mode 100644
index 000000000..76114bc03
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal-italic.woff differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal-italic.woff2 b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal-italic.woff2
new file mode 100644
index 000000000..3404f37e2
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal-italic.woff2 differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal.woff b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal.woff
new file mode 100644
index 000000000..ae1307ff5
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal.woff differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal.woff2 b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal.woff2
new file mode 100644
index 000000000..3bf984332
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/css/fonts/lato-normal.woff2 differ
diff --git a/Documentation/sphinx-docs/build/html/_static/css/theme.css b/Documentation/sphinx-docs/build/html/_static/css/theme.css
new file mode 100644
index 000000000..19a446a0e
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/css/theme.css
@@ -0,0 +1,4 @@
+html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block}
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_static/dfnworks_logo.png b/Documentation/sphinx-docs/build/html/_static/dfnworks_logo.png
new file mode 100644
index 000000000..43da41a8f
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/dfnworks_logo.png differ
diff --git a/Documentation/sphinx-docs/build/html/_static/doctools.js b/Documentation/sphinx-docs/build/html/_static/doctools.js
new file mode 100644
index 000000000..4d67807d1
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/doctools.js
@@ -0,0 +1,156 @@
+/*
+ * doctools.js
+ * ~~~~~~~~~~~
+ *
+ * Base JavaScript utilities for all Sphinx HTML documentation.
+ *
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+"use strict";
+
+const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
+ "TEXTAREA",
+ "INPUT",
+ "SELECT",
+ "BUTTON",
+]);
+
+const _ready = (callback) => {
+ if (document.readyState !== "loading") {
+ callback();
+ } else {
+ document.addEventListener("DOMContentLoaded", callback);
+ }
+};
+
+/**
+ * Small JavaScript module for the documentation.
+ */
+const Documentation = {
+ init: () => {
+ Documentation.initDomainIndexTable();
+ Documentation.initOnKeyListeners();
+ },
+
+ /**
+ * i18n support
+ */
+ TRANSLATIONS: {},
+ PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
+ LOCALE: "unknown",
+
+ // gettext and ngettext don't access this so that the functions
+ // can safely bound to a different name (_ = Documentation.gettext)
+ gettext: (string) => {
+ const translated = Documentation.TRANSLATIONS[string];
+ switch (typeof translated) {
+ case "undefined":
+ return string; // no translation
+ case "string":
+ return translated; // translation exists
+ default:
+ return translated[0]; // (singular, plural) translation tuple exists
+ }
+ },
+
+ ngettext: (singular, plural, n) => {
+ const translated = Documentation.TRANSLATIONS[singular];
+ if (typeof translated !== "undefined")
+ return translated[Documentation.PLURAL_EXPR(n)];
+ return n === 1 ? singular : plural;
+ },
+
+ addTranslations: (catalog) => {
+ Object.assign(Documentation.TRANSLATIONS, catalog.messages);
+ Documentation.PLURAL_EXPR = new Function(
+ "n",
+ `return (${catalog.plural_expr})`
+ );
+ Documentation.LOCALE = catalog.locale;
+ },
+
+ /**
+ * helper function to focus on search bar
+ */
+ focusSearchBar: () => {
+ document.querySelectorAll("input[name=q]")[0]?.focus();
+ },
+
+ /**
+ * Initialise the domain index toggle buttons
+ */
+ initDomainIndexTable: () => {
+ const toggler = (el) => {
+ const idNumber = el.id.substr(7);
+ const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
+ if (el.src.substr(-9) === "minus.png") {
+ el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
+ toggledRows.forEach((el) => (el.style.display = "none"));
+ } else {
+ el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
+ toggledRows.forEach((el) => (el.style.display = ""));
+ }
+ };
+
+ const togglerElements = document.querySelectorAll("img.toggler");
+ togglerElements.forEach((el) =>
+ el.addEventListener("click", (event) => toggler(event.currentTarget))
+ );
+ togglerElements.forEach((el) => (el.style.display = ""));
+ if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
+ },
+
+ initOnKeyListeners: () => {
+ // only install a listener if it is really needed
+ if (
+ !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
+ !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
+ )
+ return;
+
+ document.addEventListener("keydown", (event) => {
+ // bail for input elements
+ if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
+ // bail with special keys
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
+
+ if (!event.shiftKey) {
+ switch (event.key) {
+ case "ArrowLeft":
+ if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
+
+ const prevLink = document.querySelector('link[rel="prev"]');
+ if (prevLink && prevLink.href) {
+ window.location.href = prevLink.href;
+ event.preventDefault();
+ }
+ break;
+ case "ArrowRight":
+ if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
+
+ const nextLink = document.querySelector('link[rel="next"]');
+ if (nextLink && nextLink.href) {
+ window.location.href = nextLink.href;
+ event.preventDefault();
+ }
+ break;
+ }
+ }
+
+ // some keyboard layouts may need Shift to get /
+ switch (event.key) {
+ case "/":
+ if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
+ Documentation.focusSearchBar();
+ event.preventDefault();
+ }
+ });
+ },
+};
+
+// quick alias for translations
+const _ = Documentation.gettext;
+
+_ready(Documentation.init);
diff --git a/Documentation/sphinx-docs/build/html/_static/documentation_options.js b/Documentation/sphinx-docs/build/html/_static/documentation_options.js
new file mode 100644
index 000000000..f53cd810e
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/documentation_options.js
@@ -0,0 +1,13 @@
+const DOCUMENTATION_OPTIONS = {
+ VERSION: 'v2.8',
+ LANGUAGE: 'en',
+ COLLAPSE_INDEX: false,
+ BUILDER: 'html',
+ FILE_SUFFIX: '.html',
+ LINK_SUFFIX: '.html',
+ HAS_SOURCE: true,
+ SOURCELINK_SUFFIX: '.txt',
+ NAVIGATION_WITH_KEYS: false,
+ SHOW_SEARCH_SUMMARY: true,
+ ENABLE_SEARCH_SHORTCUTS: true,
+};
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_static/file.png b/Documentation/sphinx-docs/build/html/_static/file.png
new file mode 100644
index 000000000..a858a410e
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/file.png differ
diff --git a/Documentation/sphinx-docs/build/html/_static/jquery.js b/Documentation/sphinx-docs/build/html/_static/jquery.js
new file mode 100644
index 000000000..c4c6022f2
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/jquery.js
@@ -0,0 +1,2 @@
+/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"
","
"],col:[2,"
","
"],tr:[2,"
","
"],td:[3,"
","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document);
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_static/js/html5shiv.min.js b/Documentation/sphinx-docs/build/html/_static/js/html5shiv.min.js
new file mode 100644
index 000000000..cd1c674f5
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/js/html5shiv.min.js
@@ -0,0 +1,4 @@
+/**
+* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
+*/
+!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_static/js/theme.js b/Documentation/sphinx-docs/build/html/_static/js/theme.js
new file mode 100644
index 000000000..1fddb6ee4
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/js/theme.js
@@ -0,0 +1 @@
+!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap(""),n("table.docutils.footnote").wrap(""),n("table.docutils.citation").wrap(""),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0
+ var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
+ var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
+ var s_v = "^(" + C + ")?" + v; // vowel in stem
+
+ this.stemWord = function (w) {
+ var stem;
+ var suffix;
+ var firstch;
+ var origword = w;
+
+ if (w.length < 3)
+ return w;
+
+ var re;
+ var re2;
+ var re3;
+ var re4;
+
+ firstch = w.substr(0,1);
+ if (firstch == "y")
+ w = firstch.toUpperCase() + w.substr(1);
+
+ // Step 1a
+ re = /^(.+?)(ss|i)es$/;
+ re2 = /^(.+?)([^s])s$/;
+
+ if (re.test(w))
+ w = w.replace(re,"$1$2");
+ else if (re2.test(w))
+ w = w.replace(re2,"$1$2");
+
+ // Step 1b
+ re = /^(.+?)eed$/;
+ re2 = /^(.+?)(ed|ing)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ re = new RegExp(mgr0);
+ if (re.test(fp[1])) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+ }
+ else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1];
+ re2 = new RegExp(s_v);
+ if (re2.test(stem)) {
+ w = stem;
+ re2 = /(at|bl|iz)$/;
+ re3 = new RegExp("([^aeiouylsz])\\1$");
+ re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+ if (re2.test(w))
+ w = w + "e";
+ else if (re3.test(w)) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+ else if (re4.test(w))
+ w = w + "e";
+ }
+ }
+
+ // Step 1c
+ re = /^(.+?)y$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(s_v);
+ if (re.test(stem))
+ w = stem + "i";
+ }
+
+ // Step 2
+ re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = new RegExp(mgr0);
+ if (re.test(stem))
+ w = stem + step2list[suffix];
+ }
+
+ // Step 3
+ re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = new RegExp(mgr0);
+ if (re.test(stem))
+ w = stem + step3list[suffix];
+ }
+
+ // Step 4
+ re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
+ re2 = /^(.+?)(s|t)(ion)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(mgr1);
+ if (re.test(stem))
+ w = stem;
+ }
+ else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1] + fp[2];
+ re2 = new RegExp(mgr1);
+ if (re2.test(stem))
+ w = stem;
+ }
+
+ // Step 5
+ re = /^(.+?)e$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(mgr1);
+ re2 = new RegExp(meq1);
+ re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+ if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
+ w = stem;
+ }
+ re = /ll$/;
+ re2 = new RegExp(mgr1);
+ if (re.test(w) && re2.test(w)) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+
+ // and turn initial Y back to y
+ if (firstch == "y")
+ w = firstch.toLowerCase() + w.substr(1);
+ return w;
+ }
+}
+
diff --git a/Documentation/sphinx-docs/build/html/_static/minus.png b/Documentation/sphinx-docs/build/html/_static/minus.png
new file mode 100644
index 000000000..d96755fda
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/minus.png differ
diff --git a/Documentation/sphinx-docs/build/html/_static/plus.png b/Documentation/sphinx-docs/build/html/_static/plus.png
new file mode 100644
index 000000000..7107cec93
Binary files /dev/null and b/Documentation/sphinx-docs/build/html/_static/plus.png differ
diff --git a/Documentation/sphinx-docs/build/html/_static/pygments.css b/Documentation/sphinx-docs/build/html/_static/pygments.css
new file mode 100644
index 000000000..691aeb82d
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/pygments.css
@@ -0,0 +1,74 @@
+pre { line-height: 125%; }
+td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #eeffcc; }
+.highlight .c { color: #408090; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #007020; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
+.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #007020 } /* Comment.Preproc */
+.highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */
+.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #333333 } /* Generic.Output */
+.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #007020 } /* Keyword.Pseudo */
+.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #902000 } /* Keyword.Type */
+.highlight .m { color: #208050 } /* Literal.Number */
+.highlight .s { color: #4070a0 } /* Literal.String */
+.highlight .na { color: #4070a0 } /* Name.Attribute */
+.highlight .nb { color: #007020 } /* Name.Builtin */
+.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
+.highlight .no { color: #60add5 } /* Name.Constant */
+.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #007020 } /* Name.Exception */
+.highlight .nf { color: #06287e } /* Name.Function */
+.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
+.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #bb60d5 } /* Name.Variable */
+.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mb { color: #208050 } /* Literal.Number.Bin */
+.highlight .mf { color: #208050 } /* Literal.Number.Float */
+.highlight .mh { color: #208050 } /* Literal.Number.Hex */
+.highlight .mi { color: #208050 } /* Literal.Number.Integer */
+.highlight .mo { color: #208050 } /* Literal.Number.Oct */
+.highlight .sa { color: #4070a0 } /* Literal.String.Affix */
+.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
+.highlight .sc { color: #4070a0 } /* Literal.String.Char */
+.highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */
+.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
+.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
+.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
+.highlight .sx { color: #c65d09 } /* Literal.String.Other */
+.highlight .sr { color: #235388 } /* Literal.String.Regex */
+.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
+.highlight .ss { color: #517918 } /* Literal.String.Symbol */
+.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
+.highlight .fm { color: #06287e } /* Name.Function.Magic */
+.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
+.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
+.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
+.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */
+.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
\ No newline at end of file
diff --git a/Documentation/sphinx-docs/build/html/_static/searchtools.js b/Documentation/sphinx-docs/build/html/_static/searchtools.js
new file mode 100644
index 000000000..92da3f8b2
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/searchtools.js
@@ -0,0 +1,619 @@
+/*
+ * searchtools.js
+ * ~~~~~~~~~~~~~~~~
+ *
+ * Sphinx JavaScript utilities for the full-text search.
+ *
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+"use strict";
+
+/**
+ * Simple result scoring code.
+ */
+if (typeof Scorer === "undefined") {
+ var Scorer = {
+ // Implement the following function to further tweak the score for each result
+ // The function takes a result array [docname, title, anchor, descr, score, filename]
+ // and returns the new score.
+ /*
+ score: result => {
+ const [docname, title, anchor, descr, score, filename] = result
+ return score
+ },
+ */
+
+ // query matches the full name of an object
+ objNameMatch: 11,
+ // or matches in the last dotted part of the object name
+ objPartialMatch: 6,
+ // Additive scores depending on the priority of the object
+ objPrio: {
+ 0: 15, // used to be importantResults
+ 1: 5, // used to be objectResults
+ 2: -5, // used to be unimportantResults
+ },
+ // Used when the priority is not in the mapping.
+ objPrioDefault: 0,
+
+ // query found in title
+ title: 15,
+ partialTitle: 7,
+ // query found in terms
+ term: 5,
+ partialTerm: 2,
+ };
+}
+
+const _removeChildren = (element) => {
+ while (element && element.lastChild) element.removeChild(element.lastChild);
+};
+
+/**
+ * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
+ */
+const _escapeRegExp = (string) =>
+ string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
+
+const _displayItem = (item, searchTerms, highlightTerms) => {
+ const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
+ const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
+ const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
+ const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
+ const contentRoot = document.documentElement.dataset.content_root;
+
+ const [docName, title, anchor, descr, score, _filename] = item;
+
+ let listItem = document.createElement("li");
+ let requestUrl;
+ let linkUrl;
+ if (docBuilder === "dirhtml") {
+ // dirhtml builder
+ let dirname = docName + "/";
+ if (dirname.match(/\/index\/$/))
+ dirname = dirname.substring(0, dirname.length - 6);
+ else if (dirname === "index/") dirname = "";
+ requestUrl = contentRoot + dirname;
+ linkUrl = requestUrl;
+ } else {
+ // normal html builders
+ requestUrl = contentRoot + docName + docFileSuffix;
+ linkUrl = docName + docLinkSuffix;
+ }
+ let linkEl = listItem.appendChild(document.createElement("a"));
+ linkEl.href = linkUrl + anchor;
+ linkEl.dataset.score = score;
+ linkEl.innerHTML = title;
+ if (descr) {
+ listItem.appendChild(document.createElement("span")).innerHTML =
+ " (" + descr + ")";
+ // highlight search terms in the description
+ if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
+ highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
+ }
+ else if (showSearchSummary)
+ fetch(requestUrl)
+ .then((responseData) => responseData.text())
+ .then((data) => {
+ if (data)
+ listItem.appendChild(
+ Search.makeSearchSummary(data, searchTerms, anchor)
+ );
+ // highlight search terms in the summary
+ if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
+ highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
+ });
+ Search.output.appendChild(listItem);
+};
+const _finishSearch = (resultCount) => {
+ Search.stopPulse();
+ Search.title.innerText = _("Search Results");
+ if (!resultCount)
+ Search.status.innerText = Documentation.gettext(
+ "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
+ );
+ else
+ Search.status.innerText = _(
+ "Search finished, found ${resultCount} page(s) matching the search query."
+ ).replace('${resultCount}', resultCount);
+};
+const _displayNextItem = (
+ results,
+ resultCount,
+ searchTerms,
+ highlightTerms,
+) => {
+ // results left, load the summary and display it
+ // this is intended to be dynamic (don't sub resultsCount)
+ if (results.length) {
+ _displayItem(results.pop(), searchTerms, highlightTerms);
+ setTimeout(
+ () => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
+ 5
+ );
+ }
+ // search finished, update title and status message
+ else _finishSearch(resultCount);
+};
+// Helper function used by query() to order search results.
+// Each input is an array of [docname, title, anchor, descr, score, filename].
+// Order the results by score (in opposite order of appearance, since the
+// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
+const _orderResultsByScoreThenName = (a, b) => {
+ const leftScore = a[4];
+ const rightScore = b[4];
+ if (leftScore === rightScore) {
+ // same score: sort alphabetically
+ const leftTitle = a[1].toLowerCase();
+ const rightTitle = b[1].toLowerCase();
+ if (leftTitle === rightTitle) return 0;
+ return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
+ }
+ return leftScore > rightScore ? 1 : -1;
+};
+
+/**
+ * Default splitQuery function. Can be overridden in ``sphinx.search`` with a
+ * custom function per language.
+ *
+ * The regular expression works by splitting the string on consecutive characters
+ * that are not Unicode letters, numbers, underscores, or emoji characters.
+ * This is the same as ``\W+`` in Python, preserving the surrogate pair area.
+ */
+if (typeof splitQuery === "undefined") {
+ var splitQuery = (query) => query
+ .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
+ .filter(term => term) // remove remaining empty strings
+}
+
+/**
+ * Search Module
+ */
+const Search = {
+ _index: null,
+ _queued_query: null,
+ _pulse_status: -1,
+
+ htmlToText: (htmlString, anchor) => {
+ const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
+ for (const removalQuery of [".headerlinks", "script", "style"]) {
+ htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
+ }
+ if (anchor) {
+ const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`);
+ if (anchorContent) return anchorContent.textContent;
+
+ console.warn(
+ `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`
+ );
+ }
+
+ // if anchor not specified or not found, fall back to main content
+ const docContent = htmlElement.querySelector('[role="main"]');
+ if (docContent) return docContent.textContent;
+
+ console.warn(
+ "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template."
+ );
+ return "";
+ },
+
+ init: () => {
+ const query = new URLSearchParams(window.location.search).get("q");
+ document
+ .querySelectorAll('input[name="q"]')
+ .forEach((el) => (el.value = query));
+ if (query) Search.performSearch(query);
+ },
+
+ loadIndex: (url) =>
+ (document.body.appendChild(document.createElement("script")).src = url),
+
+ setIndex: (index) => {
+ Search._index = index;
+ if (Search._queued_query !== null) {
+ const query = Search._queued_query;
+ Search._queued_query = null;
+ Search.query(query);
+ }
+ },
+
+ hasIndex: () => Search._index !== null,
+
+ deferQuery: (query) => (Search._queued_query = query),
+
+ stopPulse: () => (Search._pulse_status = -1),
+
+ startPulse: () => {
+ if (Search._pulse_status >= 0) return;
+
+ const pulse = () => {
+ Search._pulse_status = (Search._pulse_status + 1) % 4;
+ Search.dots.innerText = ".".repeat(Search._pulse_status);
+ if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
+ };
+ pulse();
+ },
+
+ /**
+ * perform a search for something (or wait until index is loaded)
+ */
+ performSearch: (query) => {
+ // create the required interface elements
+ const searchText = document.createElement("h2");
+ searchText.textContent = _("Searching");
+ const searchSummary = document.createElement("p");
+ searchSummary.classList.add("search-summary");
+ searchSummary.innerText = "";
+ const searchList = document.createElement("ul");
+ searchList.classList.add("search");
+
+ const out = document.getElementById("search-results");
+ Search.title = out.appendChild(searchText);
+ Search.dots = Search.title.appendChild(document.createElement("span"));
+ Search.status = out.appendChild(searchSummary);
+ Search.output = out.appendChild(searchList);
+
+ const searchProgress = document.getElementById("search-progress");
+ // Some themes don't use the search progress node
+ if (searchProgress) {
+ searchProgress.innerText = _("Preparing search...");
+ }
+ Search.startPulse();
+
+ // index already loaded, the browser was quick!
+ if (Search.hasIndex()) Search.query(query);
+ else Search.deferQuery(query);
+ },
+
+ _parseQuery: (query) => {
+ // stem the search terms and add them to the correct list
+ const stemmer = new Stemmer();
+ const searchTerms = new Set();
+ const excludedTerms = new Set();
+ const highlightTerms = new Set();
+ const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
+ splitQuery(query.trim()).forEach((queryTerm) => {
+ const queryTermLower = queryTerm.toLowerCase();
+
+ // maybe skip this "word"
+ // stopwords array is from language_data.js
+ if (
+ stopwords.indexOf(queryTermLower) !== -1 ||
+ queryTerm.match(/^\d+$/)
+ )
+ return;
+
+ // stem the word
+ let word = stemmer.stemWord(queryTermLower);
+ // select the correct list
+ if (word[0] === "-") excludedTerms.add(word.substr(1));
+ else {
+ searchTerms.add(word);
+ highlightTerms.add(queryTermLower);
+ }
+ });
+
+ if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
+ localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
+ }
+
+ // console.debug("SEARCH: searching for:");
+ // console.info("required: ", [...searchTerms]);
+ // console.info("excluded: ", [...excludedTerms]);
+
+ return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
+ },
+
+ /**
+ * execute search (requires search index to be loaded)
+ */
+ _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => {
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const titles = Search._index.titles;
+ const allTitles = Search._index.alltitles;
+ const indexEntries = Search._index.indexentries;
+
+ // Collect multiple result groups to be sorted separately and then ordered.
+ // Each is an array of [docname, title, anchor, descr, score, filename].
+ const normalResults = [];
+ const nonMainIndexResults = [];
+
+ _removeChildren(document.getElementById("search-progress"));
+
+ const queryLower = query.toLowerCase().trim();
+ for (const [title, foundTitles] of Object.entries(allTitles)) {
+ if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
+ for (const [file, id] of foundTitles) {
+ let score = Math.round(100 * queryLower.length / title.length)
+ normalResults.push([
+ docNames[file],
+ titles[file] !== title ? `${titles[file]} > ${title}` : title,
+ id !== null ? "#" + id : "",
+ null,
+ score,
+ filenames[file],
+ ]);
+ }
+ }
+ }
+
+ // search for explicit entries in index directives
+ for (const [entry, foundEntries] of Object.entries(indexEntries)) {
+ if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
+ for (const [file, id, isMain] of foundEntries) {
+ const score = Math.round(100 * queryLower.length / entry.length);
+ const result = [
+ docNames[file],
+ titles[file],
+ id ? "#" + id : "",
+ null,
+ score,
+ filenames[file],
+ ];
+ if (isMain) {
+ normalResults.push(result);
+ } else {
+ nonMainIndexResults.push(result);
+ }
+ }
+ }
+ }
+
+ // lookup as object
+ objectTerms.forEach((term) =>
+ normalResults.push(...Search.performObjectSearch(term, objectTerms))
+ );
+
+ // lookup as search terms in fulltext
+ normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms));
+
+ // let the scorer override scores with a custom scoring function
+ if (Scorer.score) {
+ normalResults.forEach((item) => (item[4] = Scorer.score(item)));
+ nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
+ }
+
+ // Sort each group of results by score and then alphabetically by name.
+ normalResults.sort(_orderResultsByScoreThenName);
+ nonMainIndexResults.sort(_orderResultsByScoreThenName);
+
+ // Combine the result groups in (reverse) order.
+ // Non-main index entries are typically arbitrary cross-references,
+ // so display them after other results.
+ let results = [...nonMainIndexResults, ...normalResults];
+
+ // remove duplicate search results
+ // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
+ let seen = new Set();
+ results = results.reverse().reduce((acc, result) => {
+ let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
+ if (!seen.has(resultStr)) {
+ acc.push(result);
+ seen.add(resultStr);
+ }
+ return acc;
+ }, []);
+
+ return results.reverse();
+ },
+
+ query: (query) => {
+ const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query);
+ const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms);
+
+ // for debugging
+ //Search.lastresults = results.slice(); // a copy
+ // console.info("search results:", Search.lastresults);
+
+ // print the results
+ _displayNextItem(results, results.length, searchTerms, highlightTerms);
+ },
+
+ /**
+ * search for object names
+ */
+ performObjectSearch: (object, objectTerms) => {
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const objects = Search._index.objects;
+ const objNames = Search._index.objnames;
+ const titles = Search._index.titles;
+
+ const results = [];
+
+ const objectSearchCallback = (prefix, match) => {
+ const name = match[4]
+ const fullname = (prefix ? prefix + "." : "") + name;
+ const fullnameLower = fullname.toLowerCase();
+ if (fullnameLower.indexOf(object) < 0) return;
+
+ let score = 0;
+ const parts = fullnameLower.split(".");
+
+ // check for different match types: exact matches of full name or
+ // "last name" (i.e. last dotted part)
+ if (fullnameLower === object || parts.slice(-1)[0] === object)
+ score += Scorer.objNameMatch;
+ else if (parts.slice(-1)[0].indexOf(object) > -1)
+ score += Scorer.objPartialMatch; // matches in last name
+
+ const objName = objNames[match[1]][2];
+ const title = titles[match[0]];
+
+ // If more than one term searched for, we require other words to be
+ // found in the name/title/description
+ const otherTerms = new Set(objectTerms);
+ otherTerms.delete(object);
+ if (otherTerms.size > 0) {
+ const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
+ if (
+ [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
+ )
+ return;
+ }
+
+ let anchor = match[3];
+ if (anchor === "") anchor = fullname;
+ else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
+
+ const descr = objName + _(", in ") + title;
+
+ // add custom score for some objects according to scorer
+ if (Scorer.objPrio.hasOwnProperty(match[2]))
+ score += Scorer.objPrio[match[2]];
+ else score += Scorer.objPrioDefault;
+
+ results.push([
+ docNames[match[0]],
+ fullname,
+ "#" + anchor,
+ descr,
+ score,
+ filenames[match[0]],
+ ]);
+ };
+ Object.keys(objects).forEach((prefix) =>
+ objects[prefix].forEach((array) =>
+ objectSearchCallback(prefix, array)
+ )
+ );
+ return results;
+ },
+
+ /**
+ * search for full-text terms in the index
+ */
+ performTermsSearch: (searchTerms, excludedTerms) => {
+ // prepare search
+ const terms = Search._index.terms;
+ const titleTerms = Search._index.titleterms;
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const titles = Search._index.titles;
+
+ const scoreMap = new Map();
+ const fileMap = new Map();
+
+ // perform the search on the required terms
+ searchTerms.forEach((word) => {
+ const files = [];
+ const arr = [
+ { files: terms[word], score: Scorer.term },
+ { files: titleTerms[word], score: Scorer.title },
+ ];
+ // add support for partial matches
+ if (word.length > 2) {
+ const escapedWord = _escapeRegExp(word);
+ if (!terms.hasOwnProperty(word)) {
+ Object.keys(terms).forEach((term) => {
+ if (term.match(escapedWord))
+ arr.push({ files: terms[term], score: Scorer.partialTerm });
+ });
+ }
+ if (!titleTerms.hasOwnProperty(word)) {
+ Object.keys(titleTerms).forEach((term) => {
+ if (term.match(escapedWord))
+ arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
+ });
+ }
+ }
+
+ // no match but word was a required one
+ if (arr.every((record) => record.files === undefined)) return;
+
+ // found search word in contents
+ arr.forEach((record) => {
+ if (record.files === undefined) return;
+
+ let recordFiles = record.files;
+ if (recordFiles.length === undefined) recordFiles = [recordFiles];
+ files.push(...recordFiles);
+
+ // set score for the word in each file
+ recordFiles.forEach((file) => {
+ if (!scoreMap.has(file)) scoreMap.set(file, {});
+ scoreMap.get(file)[word] = record.score;
+ });
+ });
+
+ // create the mapping
+ files.forEach((file) => {
+ if (!fileMap.has(file)) fileMap.set(file, [word]);
+ else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word);
+ });
+ });
+
+ // now check if the files don't contain excluded terms
+ const results = [];
+ for (const [file, wordList] of fileMap) {
+ // check if all requirements are matched
+
+ // as search terms with length < 3 are discarded
+ const filteredTermCount = [...searchTerms].filter(
+ (term) => term.length > 2
+ ).length;
+ if (
+ wordList.length !== searchTerms.size &&
+ wordList.length !== filteredTermCount
+ )
+ continue;
+
+ // ensure that none of the excluded terms is in the search result
+ if (
+ [...excludedTerms].some(
+ (term) =>
+ terms[term] === file ||
+ titleTerms[term] === file ||
+ (terms[term] || []).includes(file) ||
+ (titleTerms[term] || []).includes(file)
+ )
+ )
+ break;
+
+ // select one (max) score for the file.
+ const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
+ // add result to the result list
+ results.push([
+ docNames[file],
+ titles[file],
+ "",
+ null,
+ score,
+ filenames[file],
+ ]);
+ }
+ return results;
+ },
+
+ /**
+ * helper function to return a node containing the
+ * search summary for a given text. keywords is a list
+ * of stemmed words.
+ */
+ makeSearchSummary: (htmlText, keywords, anchor) => {
+ const text = Search.htmlToText(htmlText, anchor);
+ if (text === "") return null;
+
+ const textLower = text.toLowerCase();
+ const actualStartPosition = [...keywords]
+ .map((k) => textLower.indexOf(k.toLowerCase()))
+ .filter((i) => i > -1)
+ .slice(-1)[0];
+ const startWithContext = Math.max(actualStartPosition - 120, 0);
+
+ const top = startWithContext === 0 ? "" : "...";
+ const tail = startWithContext + 240 < text.length ? "..." : "";
+
+ let summary = document.createElement("p");
+ summary.classList.add("context");
+ summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
+
+ return summary;
+ },
+};
+
+_ready(Search.init);
diff --git a/Documentation/sphinx-docs/build/html/_static/sphinx_highlight.js b/Documentation/sphinx-docs/build/html/_static/sphinx_highlight.js
new file mode 100644
index 000000000..8a96c69a1
--- /dev/null
+++ b/Documentation/sphinx-docs/build/html/_static/sphinx_highlight.js
@@ -0,0 +1,154 @@
+/* Highlighting utilities for Sphinx HTML documentation. */
+"use strict";
+
+const SPHINX_HIGHLIGHT_ENABLED = true
+
+/**
+ * highlight a given string on a node by wrapping it in
+ * span elements with the given class name.
+ */
+const _highlight = (node, addItems, text, className) => {
+ if (node.nodeType === Node.TEXT_NODE) {
+ const val = node.nodeValue;
+ const parent = node.parentNode;
+ const pos = val.toLowerCase().indexOf(text);
+ if (
+ pos >= 0 &&
+ !parent.classList.contains(className) &&
+ !parent.classList.contains("nohighlight")
+ ) {
+ let span;
+
+ const closestNode = parent.closest("body, svg, foreignObject");
+ const isInSVG = closestNode && closestNode.matches("svg");
+ if (isInSVG) {
+ span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
+ } else {
+ span = document.createElement("span");
+ span.classList.add(className);
+ }
+
+ span.appendChild(document.createTextNode(val.substr(pos, text.length)));
+ const rest = document.createTextNode(val.substr(pos + text.length));
+ parent.insertBefore(
+ span,
+ parent.insertBefore(
+ rest,
+ node.nextSibling
+ )
+ );
+ node.nodeValue = val.substr(0, pos);
+ /* There may be more occurrences of search term in this node. So call this
+ * function recursively on the remaining fragment.
+ */
+ _highlight(rest, addItems, text, className);
+
+ if (isInSVG) {
+ const rect = document.createElementNS(
+ "http://www.w3.org/2000/svg",
+ "rect"
+ );
+ const bbox = parent.getBBox();
+ rect.x.baseVal.value = bbox.x;
+ rect.y.baseVal.value = bbox.y;
+ rect.width.baseVal.value = bbox.width;
+ rect.height.baseVal.value = bbox.height;
+ rect.setAttribute("class", className);
+ addItems.push({ parent: parent, target: rect });
+ }
+ }
+ } else if (node.matches && !node.matches("button, select, textarea")) {
+ node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
+ }
+};
+const _highlightText = (thisNode, text, className) => {
+ let addItems = [];
+ _highlight(thisNode, addItems, text, className);
+ addItems.forEach((obj) =>
+ obj.parent.insertAdjacentElement("beforebegin", obj.target)
+ );
+};
+
+/**
+ * Small JavaScript module for the documentation.
+ */
+const SphinxHighlight = {
+
+ /**
+ * highlight the search words provided in localstorage in the text
+ */
+ highlightSearchWords: () => {
+ if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
+
+ // get and clear terms from localstorage
+ const url = new URL(window.location);
+ const highlight =
+ localStorage.getItem("sphinx_highlight_terms")
+ || url.searchParams.get("highlight")
+ || "";
+ localStorage.removeItem("sphinx_highlight_terms")
+ url.searchParams.delete("highlight");
+ window.history.replaceState({}, "", url);
+
+ // get individual terms from highlight string
+ const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
+ if (terms.length === 0) return; // nothing to do
+
+ // There should never be more than one element matching "div.body"
+ const divBody = document.querySelectorAll("div.body");
+ const body = divBody.length ? divBody[0] : document.querySelector("body");
+ window.setTimeout(() => {
+ terms.forEach((term) => _highlightText(body, term, "highlighted"));
+ }, 10);
+
+ const searchBox = document.getElementById("searchbox");
+ if (searchBox === null) return;
+ searchBox.appendChild(
+ document
+ .createRange()
+ .createContextualFragment(
+ '
dfnWorks provides the framework necessary to perform multiphase simulations (such as flow and reactive transport) at the reservoir scale. A particular application, highlighted here, is sequestering CO2 from anthropogenic sources and disposing it in geological formations such as deep saline aquifers and abandoned oil fields. Geological CO2 sequestration is one of the principal methods under consideration to reduce carbon footprint in the atmosphere due to fossil fuels (Bachu, 2002; Pacala and Socolow, 2004). For safe and sustainable long-term storage of CO2 and to prevent leaks through existing faults and fractured rock (along with the ones created during the injection process), understanding the complex physical and chemical interactions between CO2 , water (or brine) and fractured rock, is vital. dfnWorks capability to study multiphase flow in a DFN can be used to study potential CO2 migration through cap-rock, a potential risk associated with proposed subsurface storage of CO2 in saline aquifers or depleted reservoirs. Moreover, using the reactive transport capabilities of PFLOTRAN coupled with cell-based transmissivity of the DFN allows one to study dynamically changing permeability fields with mineral precipitation and dissolution due to CO2 –water interaction with rock.
Hydraulic fracturing (fracking) has provided access to hydrocarbon trapped in low-permeability media, such as tight shales. The process involves injecting water at high pressures to reactivate existing fractures and also create new fractures to increase permeability of the shale allowing hydrocarbons to be extracted. However, the fundamental physics of why fracking works and its long term ramifications are not well understood. Karra et al. (2015) used dfnWorks to generate a typical production site and simulate production. Using this physics based model, they found good agreement with production field data and determined what physical mechanisms control the decline in the production curve.
The Swedish Nuclear Fuel and Waste Management Company (SKB) has undertaken a detailed investigation of the fractured granite at the Forsmark, Sweden site as a potential host formation for a subsurface repository for spent nuclear fuel (SKB, 2011; Hartley and Joyce, 2013). The Forsmark area is about 120 km north of Stockholm in northern Uppland, and the repository is proposed
+to be constructed in crystalline bedrock at a depth of approximately 500 m. Based on the SKB site investigation, a statistical fracture model with multiple fracture sets was developed; detailed parameters of the Forsmark site model are in SKB (2011). We adopt a subset of the model that consist of three sets of background (non-deterministic) circular fractures whose orientations follow a Fisher distribution, fracture radii are sampled from a truncated power-law distribution, the transmissivity of the fractures is estimated using a power-law model based on the fracture radius, and the fracture aperture is related to the fracture size using the cubic law (Adler et al., 2012). Under such a formulation, the fracture apertures are uniform on each fracture, but vary among fractures. The network is generated in a cubic domain with sides of length one-kilometer. Dirichlet boundary conditions are imposed on the top (1 MPa) and bottom (2 MPa) of the domain to create a pressure gradient aligned with the vertical axis, and noflow boundary conditions are enforced along lateral boundaries.
+
+
Sources:
+
+
Adler, P.M., Thovert, J.-F., Mourzenko, V.V., 2012. Fractured Porous Media. Oxford University Press, Oxford, United Kingdom.
+
Bachu, S., 2002. Sequestration of CO2 in geological media in response to climate change: road map for site selection using the transform of the geological space into the CO2 phase space. Energy Convers. Manag. 43, 87–102.
+
Hartley, L., Joyce, S., 2013. Approaches and algorithms for groundwater flow modeling in support of site investigations and safety assessment of the Fors- mark site, Sweden. J. Hydrol. 500, 200–216.
+
Karra, S., Makedonska, N., Viswanathan, H., Painter, S., Hyman, J., 2015. Effect of advective flow in fractures and matrix diffusion on natural gas production. Water Resour. Res., under review.
+
Pacala, S., Socolow, R., 2004. Stabilization wedges: solving the climate problem for the next 50 years with current technologies. Science 305, 968–972.
+
SKB, Long-Term Safety for the Final Repository for Spent Nuclear Fuel at Forsmark. Main Report of the SR-Site Project. Technical Report SKB TR-11-01, Swedish Nuclear Fuel and Waste Management Co., Stockholm, Sweden, 2011.
dfnFlow involves using flow solver such as PFLOTRAN or FEHM. PFLOTRAN is recommended if a large number of fractures ( > O(1000)) are involved in a network. Using the function calls that are part of pydfnworks, one can create the mesh files needed to run PFLOTRAN. This will involve creating unstructured mesh file *uge as well as the boundary *ex files. Please see the PFLOTRAN user manual at http://www.pflotran.org under unstructured explicit format usage for further details. An example input file for PFLOTRAN is provided in the repository. Please use this as a starting point to build your input deck.
+
Below is a sample input file. Refer to the PFLOTRAN user manual at http://www.pflotran.org for input parameter descriptions.
dfnGen creates the discrete fracture networks using the feature rejection algorithm for meshing (FRAM). Fractures can be created stochastically or as deterministic features.
Description: (Mandatory) Defines the domain size, which is centered at the origin (0,0,0). The first entry is span in x (east/west), the second entry is span in y (North/South), and the third entry is span in z (Top/Bottom).
+
Type: List of three floats
+
Default : None
+
Example:
+
DFN.parameters['domainSize']['value']=[10.0,5.0,20.0]
+# Create a domain of 10 m by 5 m by 20.
+# Minimum/Maximum x is -5/+5
+# Minimum/Maximum in y is -2.5/+2.5
+# Minimum/Maximum in z is -10/+10.
+
+
+
+
Note
+
The minimum and maximum in each direction is 1/2 the input value.
Description: Temporary domainSize increase for inserting fracture centers outside of the domain defined by domainSize. After generation is complete, the domain is truncated back to domainSize. First entry is expansion in x (east/west), second entry is expansion in y (North/South), and third entry is expansion in z (Top/Bottom). This is used to help mitigate edge density effects.
+
Type: List of three floats
+
Default: [0,0,0]
+
Example:
+
DFN.params['domainSizeIncrease']['value']=[2,1,5]
+# Increase the domain-size by:
+# adding 1 to the +x, and subtracting 1 to the -x
+# adding 0.5 to +y, and subtracting -0.5 to -y
+# adding 2.5 to +z, and subtracting -2.5 to -z
+
+
+
+
Note
+
The domain size increase in each direction must be less than 1/2 the domain size in that direction.
+
+
+
Tip
+
A good rule of thumb is to set the expansion length to be at least the radius of the largest fracture.
Description: Defines the number of stratographic layers in the domain. If numOfLayers is 0, then there are no layers. For N layers, there must be N sets of minimum and maximum heights defined in layers. Each stochastic fracture set is assigned to a layer when creating a fracture family (See pydfnGen).
+
Type: Non-Negative Integer (N > 0)
+
Default: 0
+
Example:
+
DFN.params['numOfLayers']['value']=2# There will be two layers in the domain
+
Description. Defines the lower and upper limits for each layer. The first layer listed is layer 1, the second is layer 2, etc. Every stochastic families must be assigned to a layer. If the family is assigned to layer 0, then the family in generated through the entire domain.
+
Type: List of numOfLayers lists with two elements. [zMin, zMax]
+
Default: None
+
Example:
+
DFN.params['layers']['value']=[[-50,-30],# Minimum and Maximum height of layer 1 is -50 m and -30 m
+ [10,40]]# Minimum and Maximum height of layer 2 is 10 m and 40 m
+
+
+
+
Note
+
+
First entry (zMin) must be less than second entry (zMax)
Description: Defines the number of cuboid regions in the domain. If numOfRegions is 0, then there are no regions. There must be N sets of defined by regions. Each stochastic fracture set is assigned to a region during fracture family creation (See pydfnGen).
+
Type: Non-Negative Integer (N > 0)
+
Default: 0
+
Example:
+
DFN.params['numOfRegions']['value']=1# There will be one region in the domain
+
Description: Defines the bounding box of each region. The first region listed is region 1, the region is region 2, etc. Stochastic families must be assigned to theses regions. If the family is assigned to region 0, then the family in generated through the entire domain.
+
Type: List of numOfRegions lists with six elements. [minX, maxX, minY, maxY, minZ, maxZ].
+
Default: None
+
Example:
+
DFN.params['regions']['value']=[[-5,5,-10,10,-20,10],
+ [0,10,-5,15,10,20]]
+ # Will create two regions for sampling
+ # The first region with
+ # x-min: -5, x-max: 5
+ # y-min: -10, y-max: 10
+ # z-min: -20, z-max: 10
+
+
+
+
Note
+
+
Min/Max values for each direction do not need to be the same.
+
Minimum value must be less than the maximum value in each direction
Description: Selects domain boundaries for flow. The generation will only keep clusters of fractures with connections to domain boundaries which are set to 1.
+
Type: list of six boolean values corresponding to each face of the domain.
+
+
+
boundaryFaces[0] = +X domain boundary
+
boundaryFaces[1] = -X domain boundary
+
boundaryFaces[2] = +Y domain boundary
+
boundaryFaces[3] = -Y domain boundary
+
boundaryFaces[4] = +Z domain boundary
+
boundaryFaces[5] = -Z domain boundary
+
+
+
Default: [1,1,0,0,0,0]
+
Example:
+
DFN.params['boundaryFaces']['value']=[1,1,0,0,0,0]# Keep fractures within a cluster that connect the X boundaries
+
Description: Selection criteria for when network generation stops.
+
Type: boolean (0/1)
+
Default: 1
+
+
0: stop generation once nPoly fractures are accepted
+
1: stop generation once all fracture family p32 values have been meet
+
+
Example:
+
DFN.params['stopCondition']['value']=1
+
+
+
+
Note
+
p32 values are defined for each family during the creation of the fracture family. If stopCondition = 0 the famProb of a fracture belonging to a family is set during the creation of the fracture family (See pydfnGen).
Description: Probability of occurrence for each family of stochastically generated fractures. Values of famProb elements must add up to 1.0. The probabilities are saved in order of families starting with all stochastic ellipses, and then all stochastic rectangles.
+
Type: List of length number of stochastic fracture families (nFamEll + nFamRect)
+
This list is automatically generated. The values are defined with the creation of each fracture family (See pydfnGen).
+
+
Note
+
User defined ellipses, rectangles, and polygons are inserted into the domain prior to any stochastic fractures. However, there is a possibility they will be rejected if FRAM constraints are not met.
disableFram: If FRAM is turned off (disableFram: True) the resulting network can only be meshed using the coarse visual mode. You cannot mesh DFN or run flow and transport if the network is generated with disableFram: True.
+
+
Description: (Mandatory) Turn FRAM on/off. Having FRAM on is required for full DFN capabilities of meshing, flow, and transport. If FRAM is off then capabilities will be limited.
+
Type: boolean
+
+
False: FRAM is on
+
True: FRAM is off
+
+
Default: False
+
Example:
+
DFN.params['disableFram']['value']=False
+
+
+
+
Note
+
disableFram: True and visualizationMode :1 are recommended if the intention is to use an upscaled octree mesh using the UDFM module in pydfnWorks.
Description: If fracture is rejected, it will be re-translated to a new position rejectsPerFracture number of times. Increasing this value can help hit distribution targets for stochastic families.
+
Type: Positive Integer
+
Default: 10
+
Example:
+
DFN.params['rejectsPerFracture']['value']=10# If a fracture is rejected, it will be translated to a new point in the domain 10 times before being completely rejected
+
Description: Increases the length of the radii list in the sampling queue by this percentage. Fracture radii are sampled and ordered (largest to smallest) prior to beginning network generation. If the target distributions are not being properly resolved due to rejection, then increasing this value can help provide a more uniform representation of the distribution. Once the original list is exhausted, then fracture radii are sampled from the distribution at random and only smaller fractures are likely to be accepted.
+
Type: Positive Double
+
Default: 0.1
+
Example:
+
DFN.params['radiiListIncrease']['value']=0.10# Increase the length of the possible samples by 10%.
+
+
+
+
Note
+
Set to 0 to ignore.
+
+
+
Tip
+
Examine the dfnGen output report to check if the prescribed distributions are being properly resolved. Run DFN.output_report() in the python work file after generation is complete to generate the output report.
Description: Seed for random generator. Setting the seed equal to 0 will seed off the clock and a unique network will be produced. Setting the seed equal to a value > 0 will create the same network every time, which is useful for reproducibility.
+
Type: Non-negative integer
+
Default: 1
+
Example:
+
DFN.params['seed']['value']=42069
+
+
+
+
Tip
+
If you set seed to 0, the seed used in the generation is saved in the file DFN_output.txt created by dfnGen.
Description: Selection to retain multiple clusters that connect boundaries or only the largest cluster. The largest cluster is defined by the number of fractures in the cluster.
+
Type: boolean
+
+
False: Keep all clusters that connect the specified boundary faces in boundaryFaces
+
True: Keep only the largest cluster that connects the specified boundary faces in boundaryFaces
Isolated fractures do not contribute to flow in the DFN as they are not connected to flow boundaries. If you are running a DFN, keepIsolatedFractures should be set to False. You can keep isolated fractures in the domain for UDFM meshing.
+
+
+
Danger
+
Full DFN-meshing will fail if isolated fractures are not removed. Reduced meshing for visualization can still be performed.
Description: All fractures with radius less than removeFracturesLessThan are removed from the network after generation is complete.
+
Type: Non-Negative double
+
Default: 0
+
Example:
+
DFN.params['removeFracturesLessThan']['value']=5# Remove all fracture with radius less than 5 meters.
+
+
+
+
Note
+
The lower cutoff of fracture size is defined using fracture family generation, e.g., emin for ellipses sampled from a truncated powerlaw. If this parameter is non-zero, then the network will be generated with fractures down to the lower cutoff, but only those with a radius greater than removeFracturesLessThan will be output for meshing.
Description: Insert the largest fracture from each family into the domain prior to sampling sequential from family based on their respective probabilities.
+
Type: boolean
+
+
False: Do not force the largest fractures
+
True: Force the largest fractures
+
+
Default: False
+
DFN.params['forceLargeFractures']['value']=True
+
+
+
+
Warning
+
No Longer Supported. Fractures are sorted by size prior to being inserted into the domain. Larger fractures are inserted first to minimize rejections.
This section describes generation parameters for families of disc-shaped fractures. The number of elements in each parameter matches the number of ellipse families in nFamEll. The index of the elements corresponds to the fracture family. The first element of each parameter corresponds to the first family, the second element corresponds to the second family, etc.
+
+
Warning
+
The parameters shown in this section are automatically written to the parameter dictionary (DFN.params) when defining fracture families. Documentation here is meant to provide additional information on the definition of each parameter in the parameter dictionary. For information on setting these parameters see pydfnGen
DFN.params['eLayer']['value']=[0,2,1]
+# Family 1 is assigned to the whole domain
+# Family 2 is assigned to layer 2
+# Family 3 is assigned to layer 1
+
+
+
+
Note
+
Layer 0 is the entire domain. Numbers > 0 correspond to those defined in layers.
+
+
+
Warning
+
Families can only be assigned to either a layer or a region, not both.
DFN.params['eRegion']['value']=[0,2,1]
+# Family 1 is assigned to the whole domain
+# Family 2 is assigned to region 2
+# Family 3 is assigned to region 1
+
+
+
+
Note
+
Region 0 is the entire domain. Numbers > 0 correspond to those defined in regions.
+
+
+
Warning
+
Families can only be assigned to either a layer or a region, not both.
Description: Target fracture intensity per family. Fracture intensity is defined as total surface area of each fracture family divided by the total domain volume. Fractures from each family are inserted into the domain until provided target values are obtained. Generation stops once all desired fracture intensity are obtained.
DFN.params['e_p32Targets']['value']=[0.02,0.4,0.05]
+# Family 1 has a target p32 of 0.02
+# Family 2 has a target p32 of 0.4
+# Family 3 has a target p32 of 0.05
+
DFN.params['enumPoints']['value']=[8,12,16]
+# Fractures from family 1 are defined using 8 points
+# Fractures from family 2 are defined using 12 points
+# Fractures from family 3 are defined using 16 points
+
+
+
+
Note
+
+
1. Values must be greater than 4, which corresponds to a rectangle
+
2. Increasing this value lead to more challenging acceptance criteria via FRAM due to smaller edge lengths between vertices on the fracture boundary
DFN.params['easpect']['value']=[1,2,0.5]
+# Family 1 has an aspect ratio of 1 (circles)
+# Family 2 has an aspect ratio of 2 - y radius is twice the x radius
+# Family 3 has an aspect ratio of 0.5 - y radius is 1/2 the x radius
+
+
+
+
Note
+
A value of 1 makes circles
+
+
+
Tip
+
As the aspect ratio increases, the shape of the ellipse can degrade accuracy unless enumPoints is also increased
dfnGen accepts spherical coordinates (/), Trend/Plunge, or Dip/Strike to define the mean orientation of a fracture family.
+
+
Tip
+
orientationOption indicates which of these options are used. This is automatically set based on which option is used during fracture family parameter generation. The same option must be used for all families.
DFN.params['ekappa']['value']=[0.1,20,17]
+# Fracture Family 1 has a theta value of 45 degrees
+# Fracture Family 2 has a theta value of 78 degrees
+# Fracture Family 3 has a theta value of 0 degrees
+
+
+
+
Note
+
Values of approaching zero result in a uniform distribution of points on the sphere. Larger values create points with a small deviation from mean direction.
+
+
+
Warning
+
The numerical method for sampling the von Mises-Fisher distribution becomes unstable for values greater than 100.
DFN.params['etheta']['value']=[45,78,0]
+# Fracture Family 1 has a theta value of 45 degrees
+# Fracture Family 2 has a theta value of 78 degrees
+# Fracture Family 3 has a theta value of 0 degrees
+
+
+
+
Note
+
Both radians and degrees are supported. Use angleOption to select one. If angleOption is set to radians angleOption = ‘radian’, and the value provided must be less than .
DFN.params['ephi']['value']=[0,56,12]
+# Fracture Family 1 has a phi value of 0 degrees
+# Fracture Family 2 has a phi value of 56 degrees
+# Fracture Family 3 has a phi value of 12 degrees
+
+
+
+
Note
+
Both radians and degrees are supported. Use angleOption to select one. If angleOption is set to radians angleOption = ‘radian’, then the value provided must be less than .
DFN.params['etrend']['value']=[0,56,12]
+# Fracture Family 1 has a trend value of 0 degrees
+# Fracture Family 2 has a trend value of 56 degrees
+# Fracture Family 3 has a trend value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Trend & Plunge
DFN.params['eplunge']['value']=[0,56,12]
+# Fracture Family 1 has a plunge value of 0 degrees
+# Fracture Family 2 has a plunge value of 56 degrees
+# Fracture Family 3 has a plunge value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Trend & Plunge
DFN.params['estrike']['value']=[0,56,12]
+# Fracture Family 1 has a strike value of 0 degrees
+# Fracture Family 2 has a strike value of 56 degrees
+# Fracture Family 3 has a strike value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Dip & Strike
DFN.params['edip']['value']=[0,56,12]
+# Fracture Family 1 has a dip value of 0 degrees
+# Fracture Family 2 has a dip value of 56 degrees
+# Fracture Family 3 has a dip value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Dip & Strike
DFN.params['ebetaDistribution']['value']=[0,1,1]
+# Fracture Family 1 will have a random rotation
+# Fracture Family 2 will have a constant angle of rotation defined in the first entry of ebeta
+# Fracture Family 3 will have a constant angle of rotation defined in the second entry of ebeta
+
Description: Values for constant angle of rotation around the normal vector
+
Type: list of boolean (0/1)
+
Example:
+
DFN.params['ebetaDistribution']['value']=[45,270]# For ebetaDistribution: [0, 1, 1]
+# Fracture Family 2 will have a constant angle of rotation of 45 degrees
+# Fracture Family 3 will have a constant angle of rotation of 270 degrees
+
+
+
+
Note
+
+
1. The length of ebeta corresponds to the number of non-zero entries in ebetaDistribution
+
2. angleOption defines if the values are in radians or degrees
Fracture radii can be defined using four different distributions: (1) Log-Normal, (2) Truncated Power-law, (3) Exponential, and (4) Constant. Minimum and maximum values must be provided for 1-3 along with the distribution parameters.
DFN.params['edistr']['value']=[1,2,4]
+# Fracture Family 1 will use a LogNormal Distribution
+# Fracture Family 2 will use a Truncated powerlaw distribution
+# Fracture Family 3 will have a constant sized fractures
+
+
+
+
Note
+
Number of elements in the parameters for each distribution must match number of families assigned to that distribution.
Fracture radii are sampled from a Lognormal distribution with the following probability density function
+
+
+
with mean and variance .
+
+
Warning
+
dfnGen uses the mean and standard deviation of the underlying normal distribution that creates the lognormal distribution not the mean and variance of the lognormal distribution.
+
+
In order to produce a LogNormal distribution with a desired mean () and variance () one uses
Fracture radii are sampled from a truncated power-law distribution with lower bound , upper bound , and exponent defined by the following probability density function
Description: Upper cutoff of the exponential distribution families
+
Type: list of Positive floats. Length is the number of elements in edistr set to 3.
+
Example:
+
DFN.params['eExpMax']['value']=[527,89]
+# Exponential family 1 has a upper cutoff value of 527 m
+# Exponential family 2 has a upper cutoff value of 89 m
+
+
+
+
Note
+
eExpMax must be greater than eExpMin within each family.
This section describes generation parameters for families of rectangular fractures. The number of elements in each parameter matches the number of rectangle families in nFamRect. The index of the elements corresponds to the fracture family. The first element of each parameter corresponds to the first family, the second element corresponds to the second family, etc.
+
+
Warning
+
The parameters shown in this section are automatically written to the parameter dictionary (DFN.params) when defining fracture families. Documentation here is meant to provide additional information on the definition of each parameter in the parameter dictionary. For information on setting these parameters see pydfnGen
DFN.params['rLayer']['value']=[0,2,1]
+# Family 1 is assigned to the whole domain
+# Family 2 is assigned to layer 2
+# Family 3 is assigned to layer 1
+
+
+
+
Note
+
Layer 0 is the entire domain. Numbers > 0 correspond to those defined in layers.
+
+
+
Warning
+
Families can only be assigned to either a layer or a region, not both.
DFN.params['rRegion']['value']=[0,2,1]
+# Family 1 is assigned to the whole domain
+# Family 2 is assigned to region 2
+# Family 3 is assigned to region 1
+
+
+
+
Note
+
Region 0 is the entire domain. Numbers > 0 correspond to those defined in regions.
+
+
+
Warning
+
Families can only be assigned to either a layer or a region, not both.
Description: Target fracture intensity per family. Fracture intensity is defined as total surface area of each fracture family divided by the total domain volume. Fractures from each family are inserted into the domain until provided target values are obtained. Generation stops once all desired fracture intensity are obtained.
DFN.params['r_p32Targets']['value']=[0.02,0.4,0.05]
+# Family 1 has a target p32 of 0.02
+# Family 2 has a target p32 of 0.4
+# Family 3 has a target p32 of 0.05
+
DFN.params['raspect']['value']=[1,2,0.5]
+# Family 1 has an aspect ratio of 1 (circles)
+# Family 2 has an aspect ratio of 2 - y radius is twice the x radius
+# Family 3 has an aspect ratio of 0.5 - y radius is 1/2 the x radius
+
dfnGen accepts spherical coordinates (/), Trend/Plunge, or Dip/Strike to define the mean orientation of a fracture family.
+
+
Tip
+
orientationOption indicates which of these options are used. This is automatically set based on which option is used during fracture family parameter generation. The same option must be used for all families.
DFN.params['rkappa']['value']=[0.1,20,17]
+# Fracture Family 1 has a theta value of 45 degrees
+# Fracture Family 2 has a theta value of 78 degrees
+# Fracture Family 3 has a theta value of 0 degrees
+
+
+
+
Note
+
Values of approaching zero result in a uniform distribution of points on the sphere. Larger values create points with a small deviation from mean direction.
+
+
+
Warning
+
The numerical method for sampling the von Mises-Fisher distribution becomes unstable for values greater than 100.
DFN.params['rtheta']['value']=[45,78,0]
+# Fracture Family 1 has a theta value of 45 degrees
+# Fracture Family 2 has a theta value of 78 degrees
+# Fracture Family 3 has a theta value of 0 degrees
+
+
+
+
Note
+
Both radians and degrees are supported. Use angleOption to select one. If angleOption is set to radians angleOption = ‘radian’, and the value provided must be less than .
DFN.params['rphi']['value']=[0,56,12]
+# Fracture Family 1 has a phi value of 0 degrees
+# Fracture Family 2 has a phi value of 56 degrees
+# Fracture Family 3 has a phi value of 12 degrees
+
+
+
+
Note
+
Both radians and degrees are supported. Use angleOption to select one. If angleOption is set to radians angleOption = ‘radian’, then the value provided must be less than .
DFN.params['rtrend']['value']=[0,56,12]
+# Fracture Family 1 has a trend value of 0 degrees
+# Fracture Family 2 has a trend value of 56 degrees
+# Fracture Family 3 has a trend value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Trend & Plunge
DFN.params['rplunge']['value']=[0,56,12]
+# Fracture Family 1 has a plunge value of 0 degrees
+# Fracture Family 2 has a plunge value of 56 degrees
+# Fracture Family 3 has a plunge value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Trend & Plunge
DFN.params['rstrike']['value']=[0,56,12]
+# Fracture Family 1 has a strike value of 0 degrees
+# Fracture Family 2 has a strike value of 56 degrees
+# Fracture Family 3 has a strike value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Dip & Strike
DFN.params['rdip']['value']=[0,56,12]
+# Fracture Family 1 has a dip value of 0 degrees
+# Fracture Family 2 has a dip value of 56 degrees
+# Fracture Family 3 has a dip value of 12 degrees
+
+
+
+
Note
+
angleOption must be set to degree (angleOption = ‘degree’) to use Dip & Strike
DFN.params['rbetaDistribution']['value']=[0,1,1]
+# Fracture Family 1 will have a random rotation
+# Fracture Family 2 will have a constant angle of rotation defined in the first entry of ebeta
+# Fracture Family 3 will have a constant angle of rotation defined in the second entry of ebeta
+
Description: Values for constant angle of rotation around the normal vector
+
Type: list of boolean (0/1)
+
Example:
+
DFN.params['rbetaDistribution']['value']=[45,270]# For ebetaDistribution: [0, 1, 1]
+# Fracture Family 2 will have a constant angle of rotation of 45 degrees
+# Fracture Family 3 will have a constant angle of rotation of 270 degrees
+
+
+
+
Note
+
+
1. The length of rbeta corresponds to the number of non-zero entries in rbetaDistribution
+
2. :ref: angleOption defines if the values are in radians or degrees
Fracture radii can be defined using four different distributions: (1) Log-Normal, (2) Truncated Power-law, (3) Exponential, and (4) Constant. Minimum and maximum values must be provided for 1-3 along with the distribution parameters.
DFN.params['rdistr']['value']=[1,2,4]
+# Fracture Family 1 will use a LogNormal Distribution
+# Fracture Family 2 will use a Truncated powerlaw distribution
+# Fracture Family 3 will have a constant sized fractures
+
+
+
+
Note
+
Number of elements in the parameters for each distribution must match number of families assigned to that distribution.
Fracture radii are sampled from a Lognormal distribution with the following probability density function
+
+
+
with mean and variance .
+
+
Warning
+
dfnGen uses the mean and standard deviation of the underlying normal distribution that creates the lognormal distribution not the mean and variance of the lognormal distribution.
+
+
In order to produce a LogNormal distribution with a desired mean () and variance () one uses
Fracture radii are sampled from a truncated power-law distribution with lower bound , upper bound , and exponent defined by the following probability density function
Description: Upper cutoff of the exponential distribution families
+
Type: list of Positive floats. Length is the number of elements in rdistr set to 3.
+
Example:
+
DFN.params['rExpMax']['value']=[527,89]
+# Exponential family 1 has a upper cutoff value of 527 m
+# Exponential family 2 has a upper cutoff value of 89 m
+
+
+
+
Note
+
rExpMax must be greater than rExpMin within each family.
User defined deterministic features can be included into dfnWorks in a number of ways. There are two format options for ellipses and rectangles. One can also put in a selection of convex n vertex polygons.
+
+
Note
+
To incoperate user fractures into the domain see functions add_user_fract and add_user_fract_from_file in pydfnGen. Information in this section provides additional information about parameters as well as information for adding user fractures from a predefined file.
Description: Selection if general user defined ellipses are going to be used. If this option is activated, then the file UserEll_Input_File_Path is read. The path to that file must be valid.
/************************* USER DEFINED ELLIPSES ***************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+//Number of User Defined Ellipses
+/*****************************************************************************/
+nUserEll:4
+
+
+/*****************************************************************************/
+//Radius for each ellipse (one per line)
+/*****************************************************************************/
+Radii:
+0.5
+0.5
+0.4
+0.4
+
+
+/*****************************************************************************/
+//Aspect Ratio for each ellipse (one per line)
+/*****************************************************************************/
+Aspect_Ratio:
+1
+1
+1
+1
+
+
+/*****************************************************************************/
+//Angle Option: 0 - All angles in radians
+// 1 - All angles in degrees
+/*****************************************************************************/
+AngleOption:
+1
+1
+1
+1
+
+/*****************************************************************************/
+//Rotation around center for each ellipse (one per line)
+/*****************************************************************************/
+Beta:
+0
+0
+0
+0
+
+/*****************************************************************************/
+//Translation of each ellipse according to its center {x,y,z} (one per line)
+/*****************************************************************************/
+Translation:
+{-0.2,0,0}
+{0,0,0}
+{0.2,0,0.2}
+{0.2,0,-0.2}
+
+
+/*****************************************************************************/
+// userOrientationOption:
+// 0 - Normal Vector -> Normal {x,y,z}
+// 1 - Trend / Plunge -> Trend_Plunge {trend, plunge} -> Must be degrees
+// 2 - Dip / Strike - > Dip_Strike {dip, strike} -> Must be degrees
+/*****************************************************************************/
+
+userOrientationOption:0
+
+
+/*****************************************************************************/
+//Normal Vector for each ellipse (one per line)
+/*****************************************************************************/
+Normal:
+{0,0,1}
+{1,0,0}
+{0,0,1}
+{0,0,1}
+
+
+Number_of_Vertices:
+8
+8
+8
+8
+
Description: Selection if user defined ellipses by coordinate are going to be used. If this option is activated, then the file EllByCoord_Input_File_Path is read. The path to that file must be valid.
+
Type: boolean (0/1)
+
+
0: Do not include user defined ellipses by coordinate
+
1: Include user defined ellipses by coordinate
+
+
+
Warning
+
The same number of vertices must be used for all fractures.
/************************************************************************/
+/* ELLIPSES SPECIFIED BY COORDINATES */
+/************************************************************************/
+// NOTE: Coordinates must be listed in clockwise, or counterclockwise order
+// Coordinates must be co-planar
+
+/************************************************************************/
+// Number of Ellipses Defined
+/************************************************************************/
+
+nEllipses:2
+
+
+/************************************************************************/
+// Number of nodes for all ellipses
+/************************************************************************/
+
+nNodes:5
+
+
+/************************************************************************/
+// Coordinates (4 vertice coordinates per line/One rectangle per line)
+/************************************************************************/
+// One Ellipse per line (White space and new lines should not matter)
+// Format: {x1,y1,z1} {x2,y2,z2} {x3,y3,z3} {x4,y4,z4} ... {xn, yn, zn}
+
+Coordinates:
+
+{-2,-1,0}{1,-2,0}{2,0,0}{0,2,0}{-2,1,0}
+{0,-0.3,-1}{0,.5,-.7}{0,.7,1}{0,-.7,1}{0,-1,0}
+
Description: Selection if general user defined rectangles are going to be used. If this option is activated, then the file UserRect_Input_File_Path is read. The path to that file must be valid.
/************************* USER DEFINED rectangles ***************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+//Number of User Defined rectangles
+/*****************************************************************************/
+nUserRect:4
+
+
+/*****************************************************************************/
+//Radius for each rectangle (one per line)
+/*****************************************************************************/
+Radii:
+0.5
+0.5
+0.4
+0.4
+
+
+/*****************************************************************************/
+//Aspect Ratio for each rectangle (one per line)
+/*****************************************************************************/
+Aspect_Ratio:
+1
+1
+1
+1
+
+
+/*****************************************************************************/
+//Angle Option: 0 - All angles in radians
+// 1 - All angles in degrees
+/*****************************************************************************/
+AngleOption:
+1
+1
+1
+1
+
+/*****************************************************************************/
+//Rotation around center for each rectangle (one per line)
+/*****************************************************************************/
+Beta:
+0
+0
+0
+0
+
+/*****************************************************************************/
+//Translation of each rectangle according to its center {x,y,z} (one per line)
+/*****************************************************************************/
+Translation:
+{-0.2,0,0}
+{0,0,0}
+{0.2,0,0.2}
+{0.2,0,-0.2}
+
+
+/*****************************************************************************/
+// userOrientationOption:
+// 0 - Normal Vector -> Normal {x,y,z}
+// 1 - Trend / Plunge -> Trend_Plunge {trend, plunge} -> Must be degrees
+// 2 - Dip / Strike - > Dip_Strike {dip, strike} -> Must be degrees
+/*****************************************************************************/
+
+userOrientationOption:0
+
+
+/*****************************************************************************/
+//Normal Vector for each rectangle (one per line)
+/*****************************************************************************/
+Normal:
+{0,0,1}
+{1,0,0}
+{0,0,1}
+{0,0,1}
+
Description: Selection if user defined rectangles by coordinate are going to be used. If this option is activated, then the file RectByCoord_Input_File_Path is read. The path to that file must be valid.
+
Type: boolean (0/1)
+
+
0: Do not include user defined rectangles by coordinate
+
1: include user defined rectangles by coordinate
+
+
+
Warning
+
The same number of vertices must be used for all fractures.
/************************************************************************/
+/* rectangles SPECIFIED BY COORDINATES */
+/************************************************************************/
+// NOTE: Coordinates must be listed in clockwise, or counterclockwise order
+// Coordinates must be co-planar
+
+/************************************************************************/
+// Number of rectangles Defined
+/************************************************************************/
+
+nRectangles:2
+
+/************************************************************************/
+// Coordinates (4 vertices coordinates per line/One rectangle per line)
+/************************************************************************/
+// One rectangle per line (White space and new lines should not matter)
+// Format: {x1,y1,z1} {x2,y2,z2} {x3,y3,z3} {x4,y4,z4} ... {xn, yn, zn}
+
+Coordinates:
+
+{-2,-1,0}{1,-2,0}{2,0,0}{0,2,0}{-2,1,0}
+{0,-0.3,-1}{0,.5,-.7}{0,.7,1}{0,-.7,1}{0,-1,0}
+
Description: Selection if user defined polygons are going to be used. If this option is activated, then the file :refPolygonByCoord_Input_File_Path is read. The path to that file must be valid.
+
Type: boolean (0/1)
+
+
0: Do not include user defined polygons
+
1: Do include user defined polygons
+
+
+
Note
+
+
Each polygon can have a different number of vertices.
+
dfnGen automatically outputs the file polygons.dat which can be read back in using this option to create the same DFN.
The first line is a keyword nPolygons, the number of fractures in the file. Then each line after nPolygons is a different fracture. The first entry is an integer with the number of vertices in that fracture. The remaining entries are the vertex coordinates. The coordinate format is {x1,y1,z1} {x2,y2,z2} {x3,y3,z3} {x4,y4,z4} … {xn, yn, zn}
+
General Example:
+
nPolygons:1
+4{x1,y1,z1}{x2,y2,z2}{x3,y3,z3}{x4,y4,z4}// fracture 1 has 4 vertices with these coordinates
+
dfnTrans is a method for resolving solute transport using control volume flow
+solutions obtained from dfnFlow on the unstructured mesh generated using dfnGen.
+We adopt a Lagrangian approach and represent a non-reactive conservative solute
+as a collection of indivisible passive tracer particles. Particle tracking
+methods (a) provide a wealth of information about the local flow field, (b) do
+not suffer from numerical dispersion, which is inherent in the discretizations
+of advection–dispersion equations, and (c) allow for the computation of each
+particle trajectory to be performed in an intrinsically parallel fashion if
+particles are not allowed to interact with one another or the fracture network.
+However, particle tracking on a DFN poses unique challenges that arise from (a)
+the quality of the flow solution, (b) the unstructured mesh representation of
+the DFN, and (c) the physical phenomena of interest. The flow solutions obtained
+from dfnFlow are locally mass conserving, so the particle tracking method does
+not suffer from the problems inherent in using Galerkin finite element codes.
+
dfnTrans starts from reconstruction of local velocity field: Darcy fluxes
+obtained using dfnFlow are used to reconstruct the local velocity field, which
+is used for particle tracking on the DFN. Then, Lagrangian transport simulation
+is used to determine pathlines through the network and simulate transport. It is
+important to note that dfnTrans itself only solves for advective transport, but
+effects of longitudinal dispersion and matrix diffusion, sorption, and other
+retention processes are easily incorporated by post-processing particle
+trajectories.
This section contains a few examples of DFN generated using dfnWorks. All required input files for these examples are contained in the folder dfnWorks/examples/. The focus of this document is to provide visual confirmation that new users of dfnWorks have the code set up correctly, can carry out the following runs and reproduce the following images. All images are rendered using Paraview, which can be obtained for free at http : //www.paraview.org/. The first two examples are simplest so it is recommended that the user proceed in the order presented here.
+
All examples are in the examples/ directory. Within each subdirectory are the files required to run the example. The command line input is found in notes.txt. Be sure that you have created ~/test_output_files prior to running the examples.
This test case consists of four user defined rectangular fractures within a a cubic domain with sides of length one meter. The network of four fractures, each colored by material ID. The computational mesh is overlaid on the fractures. This image is created by loading the file full_mesh.inp. located in the job folder into Paraview.
+
+
High pressure (red) Dirichlet boundary conditions are applied on the edge of the single fracture along the boundary x = -0.5, and low pressure (blue) boundary conditions are applied on the edges of the two fractures at the boundary x = 0.5.
+This image is created by loading the file parsed_vtk/dfn_explicit-001.vtk into Paraview.
+
Particles are inserted uniformly along the inlet fracture on the left side of the image.
+Particles exit the domain through the two horizontal fractures on the right side of the image.
+Due to the stochastic nature of the particle tracking algorithm, your pathlines might not be exactly the same as in this image.
+Trajectories are colored by the current velocity magnitude of the particle’s velocity.
+Trajectories can be visualized by loading the files part_*.inp, in the folder 4_user_rectangles/traj/trajectories/
+
We have used the extract surface and tube filters in paraview for visual clarity.
This test case consists of four user defined elliptical fractures within a a cubic domain with sides of length one meter. In this case the ellipses are approximated using 8 vertices. We have set the meshing resolution to be uniform by including the argument slope=0 into the mesh_networks function in run_explicit.py.
This test case consists of a family of fractures whose size is exponentially distributed with a minimum size of 1m and a maximum size of 50m. The domain is cubic with an edge length of 10m. All input parameters for the generator can be found in tests/gen_exponential_dist.dat. We have changed the flow direction to be aligned with the y-axis by modifying the PFLOTRAN input card dfn_explicit.in
This test case consists of two families whose sizes have a truncated power law distribution with a minimum size of 1m and a maximum size of 5m an exponent 2.6. The domain size is cubic with an edge length of 15m.
This example uses a graph representation of a DFN to isolate the 2-core. The pruned DFN has all dead end fractures of the network are removed. This example has two run_explicit.py scripts. The first creates the original DFN and identifies the 2-core using networkx (https://networkx.github.io/). The second meshes the DFN corresponding to the 2-core of the graph and then runs flow and transport. The 2 core network is in a sub-directory 2-core. The original network has 207 fractures and the 2-core has 79 fractures.