diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..f538c3ef --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,27 @@ +name: documentation + +on: [push, pull_request, workflow_dispatch] + +permissions: + contents: write + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - name: Install dependencies + run: | + pip install sphinx sphinx_rtd_theme myst_parser + - name: Sphinx build + run: | + sphinx-build doc _build + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + with: + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: _build/ + force_orphan: true \ No newline at end of file diff --git a/docs/_images/4_user_ellipses.png b/docs/_images/4_user_ellipses.png new file mode 100644 index 00000000..ff54ce38 Binary files /dev/null and b/docs/_images/4_user_ellipses.png differ diff --git a/docs/_images/4_user_rectangles.png b/docs/_images/4_user_rectangles.png new file mode 100644 index 00000000..9d3d7283 Binary files /dev/null and b/docs/_images/4_user_rectangles.png differ diff --git a/docs/_images/TPL_pathlines.png b/docs/_images/TPL_pathlines.png new file mode 100644 index 00000000..ca89520a Binary files /dev/null and b/docs/_images/TPL_pathlines.png differ diff --git a/docs/_images/dead-end_velocity_field.png b/docs/_images/dead-end_velocity_field.png new file mode 100644 index 00000000..7e19a570 Binary files /dev/null and b/docs/_images/dead-end_velocity_field.png differ diff --git a/docs/_images/dfn_2_core.png b/docs/_images/dfn_2_core.png new file mode 100644 index 00000000..40c793d7 Binary files /dev/null and b/docs/_images/dfn_2_core.png differ diff --git a/docs/_images/dfn_graph.png b/docs/_images/dfn_graph.png new file mode 100644 index 00000000..0ee60196 Binary files /dev/null and b/docs/_images/dfn_graph.png differ diff --git a/docs/_images/exp_pressure.png b/docs/_images/exp_pressure.png new file mode 100644 index 00000000..00426abe Binary files /dev/null and b/docs/_images/exp_pressure.png differ diff --git a/docs/_images/forsmark_trajectories.png b/docs/_images/forsmark_trajectories.png new file mode 100644 index 00000000..24bf850d Binary files /dev/null and b/docs/_images/forsmark_trajectories.png differ diff --git a/docs/_images/in-fracture-variability_pathlines.png b/docs/_images/in-fracture-variability_pathlines.png new file mode 100644 index 00000000..68c854dd Binary files /dev/null and b/docs/_images/in-fracture-variability_pathlines.png differ diff --git a/docs/_images/math/02c849aad7960bc828467cc5933f8fe020201c4b.png b/docs/_images/math/02c849aad7960bc828467cc5933f8fe020201c4b.png new file mode 100644 index 00000000..2081048c Binary files /dev/null and b/docs/_images/math/02c849aad7960bc828467cc5933f8fe020201c4b.png differ diff --git a/docs/_images/math/261e3eb56c1757dd003745ee4cc53aa595565af1.png b/docs/_images/math/261e3eb56c1757dd003745ee4cc53aa595565af1.png new file mode 100644 index 00000000..1cf9581f Binary files /dev/null and b/docs/_images/math/261e3eb56c1757dd003745ee4cc53aa595565af1.png differ diff --git a/docs/_images/math/279e5f07d040abb28b85734dce062ae251eb1c7b.png b/docs/_images/math/279e5f07d040abb28b85734dce062ae251eb1c7b.png new file mode 100644 index 00000000..a7797207 Binary files /dev/null and b/docs/_images/math/279e5f07d040abb28b85734dce062ae251eb1c7b.png differ diff --git a/docs/_images/math/2e0bcb32a282b693f93afdd203ce17b9b558ff79.png b/docs/_images/math/2e0bcb32a282b693f93afdd203ce17b9b558ff79.png new file mode 100644 index 00000000..74be8ecc Binary files /dev/null and b/docs/_images/math/2e0bcb32a282b693f93afdd203ce17b9b558ff79.png differ diff --git a/docs/_images/math/2f5aa019312e1bbc969deab8dca8b00f76025404.png b/docs/_images/math/2f5aa019312e1bbc969deab8dca8b00f76025404.png new file mode 100644 index 00000000..b2f68817 Binary files /dev/null and b/docs/_images/math/2f5aa019312e1bbc969deab8dca8b00f76025404.png differ diff --git a/docs/_images/math/345f606ec4423cb6fa147f63a5da5a98ee48c1bb.png b/docs/_images/math/345f606ec4423cb6fa147f63a5da5a98ee48c1bb.png new file mode 100644 index 00000000..9f048201 Binary files /dev/null and b/docs/_images/math/345f606ec4423cb6fa147f63a5da5a98ee48c1bb.png differ diff --git a/docs/_images/math/36bc2e342149719661caf719641e17da4ee2dfe6.png b/docs/_images/math/36bc2e342149719661caf719641e17da4ee2dfe6.png new file mode 100644 index 00000000..576a0908 Binary files /dev/null and b/docs/_images/math/36bc2e342149719661caf719641e17da4ee2dfe6.png differ diff --git a/docs/_images/math/416cf0520ab360f9e5d8f8e778bb5657fe846cbb.png b/docs/_images/math/416cf0520ab360f9e5d8f8e778bb5657fe846cbb.png new file mode 100644 index 00000000..a910a873 Binary files /dev/null and b/docs/_images/math/416cf0520ab360f9e5d8f8e778bb5657fe846cbb.png differ diff --git a/docs/_images/math/49c53391daff97aa7d155674c12e14f9e6ba1c93.png b/docs/_images/math/49c53391daff97aa7d155674c12e14f9e6ba1c93.png new file mode 100644 index 00000000..bd22318d Binary files /dev/null and b/docs/_images/math/49c53391daff97aa7d155674c12e14f9e6ba1c93.png differ diff --git a/docs/_images/math/4a3598141469c2555591e66606a1b86d4ec6dca9.png b/docs/_images/math/4a3598141469c2555591e66606a1b86d4ec6dca9.png new file mode 100644 index 00000000..cec2dcc8 Binary files /dev/null and b/docs/_images/math/4a3598141469c2555591e66606a1b86d4ec6dca9.png differ diff --git a/docs/_images/math/4ed396e004eb768a13e92e60d6ec6ca2d8406bc3.png b/docs/_images/math/4ed396e004eb768a13e92e60d6ec6ca2d8406bc3.png new file mode 100644 index 00000000..25fe9568 Binary files /dev/null and b/docs/_images/math/4ed396e004eb768a13e92e60d6ec6ca2d8406bc3.png differ diff --git a/docs/_images/math/5406eadc281dbd20de843b0034c8497320dae5cb.png b/docs/_images/math/5406eadc281dbd20de843b0034c8497320dae5cb.png new file mode 100644 index 00000000..76f5792c Binary files /dev/null and b/docs/_images/math/5406eadc281dbd20de843b0034c8497320dae5cb.png differ diff --git a/docs/_images/math/5b514159f8b6bf75e6317d628c7caf2df737075c.png b/docs/_images/math/5b514159f8b6bf75e6317d628c7caf2df737075c.png new file mode 100644 index 00000000..a3542dea Binary files /dev/null and b/docs/_images/math/5b514159f8b6bf75e6317d628c7caf2df737075c.png differ diff --git a/docs/_images/math/5f01a802667a3ba5b8994a25f3fdf78bf8593563.png b/docs/_images/math/5f01a802667a3ba5b8994a25f3fdf78bf8593563.png new file mode 100644 index 00000000..80bceaae Binary files /dev/null and b/docs/_images/math/5f01a802667a3ba5b8994a25f3fdf78bf8593563.png differ diff --git a/docs/_images/math/5fd160b4cce0043bb255b69534567d66c7701e55.png b/docs/_images/math/5fd160b4cce0043bb255b69534567d66c7701e55.png new file mode 100644 index 00000000..d5e9f2ab Binary files /dev/null and b/docs/_images/math/5fd160b4cce0043bb255b69534567d66c7701e55.png differ diff --git a/docs/_images/math/64cfd851ec561560ab02a61550edfe6939966211.png b/docs/_images/math/64cfd851ec561560ab02a61550edfe6939966211.png new file mode 100644 index 00000000..ef1c1d0a Binary files /dev/null and b/docs/_images/math/64cfd851ec561560ab02a61550edfe6939966211.png differ diff --git a/docs/_images/math/68c7c8c65602677ab56cf7fd88002023f0edc575.png b/docs/_images/math/68c7c8c65602677ab56cf7fd88002023f0edc575.png new file mode 100644 index 00000000..30e812bc Binary files /dev/null and b/docs/_images/math/68c7c8c65602677ab56cf7fd88002023f0edc575.png differ diff --git a/docs/_images/math/6cf2be4eba28e4c7a7daec65d0075aae07bb173e.png b/docs/_images/math/6cf2be4eba28e4c7a7daec65d0075aae07bb173e.png new file mode 100644 index 00000000..fbb61739 Binary files /dev/null and b/docs/_images/math/6cf2be4eba28e4c7a7daec65d0075aae07bb173e.png differ diff --git a/docs/_images/math/73f4958e89c005c29b46fe025be4584c439caa4b.png b/docs/_images/math/73f4958e89c005c29b46fe025be4584c439caa4b.png new file mode 100644 index 00000000..e769e144 Binary files /dev/null and b/docs/_images/math/73f4958e89c005c29b46fe025be4584c439caa4b.png differ diff --git a/docs/_images/math/761a700ecec9ede79c472b840375d1398c81a1aa.png b/docs/_images/math/761a700ecec9ede79c472b840375d1398c81a1aa.png new file mode 100644 index 00000000..eaf672c8 Binary files /dev/null and b/docs/_images/math/761a700ecec9ede79c472b840375d1398c81a1aa.png differ diff --git a/docs/_images/math/7dfd566f04990e72a20ff38301773d0a0cf37845.png b/docs/_images/math/7dfd566f04990e72a20ff38301773d0a0cf37845.png new file mode 100644 index 00000000..9e3b9646 Binary files /dev/null and b/docs/_images/math/7dfd566f04990e72a20ff38301773d0a0cf37845.png differ diff --git a/docs/_images/math/8a87f04e7d7cca18343c084cceca5237fae62491.png b/docs/_images/math/8a87f04e7d7cca18343c084cceca5237fae62491.png new file mode 100644 index 00000000..4a9f9be4 Binary files /dev/null and b/docs/_images/math/8a87f04e7d7cca18343c084cceca5237fae62491.png differ diff --git a/docs/_images/math/8cf77d2801619424ae7a84211bb1a8cca8795192.png b/docs/_images/math/8cf77d2801619424ae7a84211bb1a8cca8795192.png new file mode 100644 index 00000000..578948f4 Binary files /dev/null and b/docs/_images/math/8cf77d2801619424ae7a84211bb1a8cca8795192.png differ diff --git a/docs/_images/math/9630132210b904754c9ab272b61cb527d12263ca.png b/docs/_images/math/9630132210b904754c9ab272b61cb527d12263ca.png new file mode 100644 index 00000000..fe54e244 Binary files /dev/null and b/docs/_images/math/9630132210b904754c9ab272b61cb527d12263ca.png differ diff --git a/docs/_images/math/a093d21e837b340ae5112488ff176fc6c8492c87.png b/docs/_images/math/a093d21e837b340ae5112488ff176fc6c8492c87.png new file mode 100644 index 00000000..4ea4381b Binary files /dev/null and b/docs/_images/math/a093d21e837b340ae5112488ff176fc6c8492c87.png differ diff --git a/docs/_images/math/a20864ec4268c5cf0fdd0c4e76a69bbb74733b54.png b/docs/_images/math/a20864ec4268c5cf0fdd0c4e76a69bbb74733b54.png new file mode 100644 index 00000000..4817d477 Binary files /dev/null and b/docs/_images/math/a20864ec4268c5cf0fdd0c4e76a69bbb74733b54.png differ diff --git a/docs/_images/math/a963655a01fb22852c131f0d8e66066026c39fb8.png b/docs/_images/math/a963655a01fb22852c131f0d8e66066026c39fb8.png new file mode 100644 index 00000000..a3ab94e9 Binary files /dev/null and b/docs/_images/math/a963655a01fb22852c131f0d8e66066026c39fb8.png differ diff --git a/docs/_images/math/a9a7d5b5b78fb63c537c462714d97ffd7c091829.png b/docs/_images/math/a9a7d5b5b78fb63c537c462714d97ffd7c091829.png new file mode 100644 index 00000000..fca7fb29 Binary files /dev/null and b/docs/_images/math/a9a7d5b5b78fb63c537c462714d97ffd7c091829.png differ diff --git a/docs/_images/math/af5573a7b3374f4e3287488adf152892c0f16488.png b/docs/_images/math/af5573a7b3374f4e3287488adf152892c0f16488.png new file mode 100644 index 00000000..0f1152a0 Binary files /dev/null and b/docs/_images/math/af5573a7b3374f4e3287488adf152892c0f16488.png differ diff --git a/docs/_images/math/b4ecdaef430093e2c63ec36545fdc953d7b1449c.png b/docs/_images/math/b4ecdaef430093e2c63ec36545fdc953d7b1449c.png new file mode 100644 index 00000000..d65283fa Binary files /dev/null and b/docs/_images/math/b4ecdaef430093e2c63ec36545fdc953d7b1449c.png differ diff --git a/docs/_images/math/b52df27bfb0b1e3af0c2c68a7b9da459178c2a7d.png b/docs/_images/math/b52df27bfb0b1e3af0c2c68a7b9da459178c2a7d.png new file mode 100644 index 00000000..7a4ae42d Binary files /dev/null and b/docs/_images/math/b52df27bfb0b1e3af0c2c68a7b9da459178c2a7d.png differ diff --git a/docs/_images/math/bf4d6dded1f2769dd9942a7e0071045945dbeb9a.png b/docs/_images/math/bf4d6dded1f2769dd9942a7e0071045945dbeb9a.png new file mode 100644 index 00000000..e26a15b5 Binary files /dev/null and b/docs/_images/math/bf4d6dded1f2769dd9942a7e0071045945dbeb9a.png differ diff --git a/docs/_images/math/cefc603e5658facb747581f9567192993f21c7ab.png b/docs/_images/math/cefc603e5658facb747581f9567192993f21c7ab.png new file mode 100644 index 00000000..749d5882 Binary files /dev/null and b/docs/_images/math/cefc603e5658facb747581f9567192993f21c7ab.png differ diff --git a/docs/_images/math/d549c46bd87138576a59f5cb8578b1e7c7ba4344.png b/docs/_images/math/d549c46bd87138576a59f5cb8578b1e7c7ba4344.png new file mode 100644 index 00000000..7a48fbce Binary files /dev/null and b/docs/_images/math/d549c46bd87138576a59f5cb8578b1e7c7ba4344.png differ diff --git a/docs/_images/math/e24906bf3a64d622f266503cd7146a8de74a258d.png b/docs/_images/math/e24906bf3a64d622f266503cd7146a8de74a258d.png new file mode 100644 index 00000000..2c550f19 Binary files /dev/null and b/docs/_images/math/e24906bf3a64d622f266503cd7146a8de74a258d.png differ diff --git a/docs/_images/math/e8dea8254118f111b5fb20895b03528c17566f06.png b/docs/_images/math/e8dea8254118f111b5fb20895b03528c17566f06.png new file mode 100644 index 00000000..f3b3569b Binary files /dev/null and b/docs/_images/math/e8dea8254118f111b5fb20895b03528c17566f06.png differ diff --git a/docs/_images/math/ef0f29bda011efb4abda29f5620732e335ad5e42.png b/docs/_images/math/ef0f29bda011efb4abda29f5620732e335ad5e42.png new file mode 100644 index 00000000..05ac3b5d Binary files /dev/null and b/docs/_images/math/ef0f29bda011efb4abda29f5620732e335ad5e42.png differ diff --git a/docs/_images/math/f709b2722911463d06271f2320d386c4f3ab88fe.png b/docs/_images/math/f709b2722911463d06271f2320d386c4f3ab88fe.png new file mode 100644 index 00000000..4da1399d Binary files /dev/null and b/docs/_images/math/f709b2722911463d06271f2320d386c4f3ab88fe.png differ diff --git a/docs/_images/math/ff4552ec4e1115499121b2cc24265a14c7aa7b1c.png b/docs/_images/math/ff4552ec4e1115499121b2cc24265a14c7aa7b1c.png new file mode 100644 index 00000000..42ec8a8a Binary files /dev/null and b/docs/_images/math/ff4552ec4e1115499121b2cc24265a14c7aa7b1c.png differ diff --git a/docs/_images/math/fffd2357ee88a9c50ba9e831ed64c39c73d54a07.png b/docs/_images/math/fffd2357ee88a9c50ba9e831ed64c39c73d54a07.png new file mode 100644 index 00000000..ba6e83da Binary files /dev/null and b/docs/_images/math/fffd2357ee88a9c50ba9e831ed64c39c73d54a07.png differ diff --git a/docs/_images/power_mesh.png b/docs/_images/power_mesh.png new file mode 100644 index 00000000..6ee3f416 Binary files /dev/null and b/docs/_images/power_mesh.png differ diff --git a/docs/_images/pruned_network.png b/docs/_images/pruned_network.png new file mode 100644 index 00000000..77a9f400 Binary files /dev/null and b/docs/_images/pruned_network.png differ diff --git a/docs/_images/time_co2.png b/docs/_images/time_co2.png new file mode 100644 index 00000000..1711076f Binary files /dev/null and b/docs/_images/time_co2.png differ diff --git a/docs/_images/well-pressure.png b/docs/_images/well-pressure.png new file mode 100644 index 00000000..f0f528b4 Binary files /dev/null and b/docs/_images/well-pressure.png differ diff --git a/docs/_modules/index.html b/docs/_modules/index.html new file mode 100644 index 00000000..79e1777d --- /dev/null +++ b/docs/_modules/index.html @@ -0,0 +1,140 @@ + + + + + + Overview: module code — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnFlow/fehm.html b/docs/_modules/pydfnworks/dfnFlow/fehm.html new file mode 100644 index 00000000..99c51e7c --- /dev/null +++ b/docs/_modules/pydfnworks/dfnFlow/fehm.html @@ -0,0 +1,258 @@ + + + + + + pydfnworks.dfnFlow.fehm — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pydfnworks.dfnFlow.fehm

+import os
+import subprocess
+import sys
+import glob
+import shutil
+from time import time
+import numpy as np
+"""
+Functions for using FEHM in dfnWorks
+"""
+
+
+
+[docs] +def correct_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 + if self.flow_solver != "FEHM": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + self.stor_file = self.inp_file[:-4] + '.stor' + self.mat_file = self.inp_file[:-4] + '_material.zone' + f = open("convert_stor_params.txt", "w") + 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) + f.close() + + t = time() + cmd = os.environ['CORRECT_STOR_EXE'] + ' convert_stor_params.txt' + failure = subprocess.call(cmd, shell=True) + if failure > 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)
+ + + +def correct_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 + if len(lines[-1].split()) != 0: + print("--> Adding line to perm.dat") + fp = open("perm.dat", "a") + fp.write("\n") + fp.close() + + +
+[docs] +def fehm(self): + """Run FEHM + + Parameters + ---------- + self : object + DFN Class + + Returns + ------- + None + + Notes + ----- + See https://fehm.lanl.gov/ for details about FEHM + + """ + print("--> Running FEHM") + if self.flow_solver != "FEHM": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + try: + shutil.copy(self.dfnFlow_file, os.getcwd()) + except: + error = "-->ERROR copying FEHM run file: %s" % self.dfnFlow_file + std.stderr.write(error) + sys.exit(1) + + path = self.dfnFlow_file.strip(self.local_dfnFlow_file) + fp = open(self.local_dfnFlow_file) + line = fp.readline() + fehm_input = line.split()[-1] + fp.close() + try: + shutil.copy(path + fehm_input, os.getcwd()) + except: + error = "-->ERROR copying FEHM input file:\n" % fehm_input + sys.stderr.write(error) + sys.exit(1) + + correct_perm_for_fehm() + tic = time() + subprocess.call(os.environ["FEHM_EXE"] + " " + self.local_dfnFlow_file, + shell=True) + print('=' * 80) + print("FEHM Complete") + print("Time Required %0.2f Seconds" % (time() - tic)) + print('=' * 80)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnFlow/flow.html b/docs/_modules/pydfnworks/dfnFlow/flow.html new file mode 100644 index 00000000..99d190da --- /dev/null +++ b/docs/_modules/pydfnworks/dfnFlow/flow.html @@ -0,0 +1,260 @@ + + + + + + pydfnworks.dfnFlow.flow — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pydfnworks.dfnFlow.flow

+import os
+import subprocess
+import sys
+import glob
+import shutil
+from time import time
+import numpy as np
+
+
+
+[docs] +def set_flow_solver(self, flow_solver): + """Sets flow solver to be used + + Parameters + ---------- + self : object + DFN Class + flow_solver: string + Name of flow solver. Currently supported flow sovlers are FEHM and PFLOTRAN + + Returns + --------- + + Notes + -------- + Default is PFLOTRAN + +""" + if flow_solver == "FEHM" or flow_solver == "PFLOTRAN": + print("Using flow solver %s" % flow_solver) + self.flow_solver = flow_solver + else: + error = "ERROR: Unknown flow solver requested %s\nCurrently supported flow solvers are FEHM and PFLOTRAN\nExiting dfnWorks\n" % flow_solver + sys.stderr.write(error) + sys.exit(1)
+ + + +
+[docs] +def dfn_flow(self, dump_vtk=True): + """ Run the dfnFlow portion of the workflow + + Parameters + ---------- + self : object + DFN Class + dump_vtk : bool + True - Write out vtk files for flow solutions + False - Does not write out vtk files for flow solutions + + Returns + --------- + + Notes + -------- + Information on individual functions is found therein + """ + + print('=' * 80) + print("dfnFlow Starting") + print('=' * 80) + + tic_flow = time() + + if self.flow_solver == "PFLOTRAN": + print("Using flow solver: %s" % self.flow_solver) + tic = time() + self.lagrit2pflotran() + self.dump_time('Function: lagrit2pflotran', time() - tic) + + tic = time() + self.pflotran() + self.dump_time('Function: pflotran', time() - tic) + + if dump_vtk: + tic = time() + self.parse_pflotran_vtk_python() + self.dump_time('Function: parse_pflotran_vtk', time() - tic) + tic = time() + self.pflotran_cleanup() + self.dump_time('Function: pflotran_cleanup', time() - tic) + + tic = time() + + elif self.flow_solver == "FEHM": + print("Using flow solver: %s" % self.flow_solver) + tic = time() + self.correct_stor_file() + self.fehm() + self.dump_time('Function: FEHM', time() - tic) + + delta_time = time() - tic_flow + self.dump_time('Process: dfnFlow', delta_time) + + print('=' * 80) + print("dfnFlow Complete") + print("Time Required for dfnFlow %0.2f seconds\n" % delta_time) + print('=' * 80)
+ + + + + +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnFlow/mass_balance.html b/docs/_modules/pydfnworks/dfnFlow/mass_balance.html new file mode 100644 index 00000000..6f0e8e09 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnFlow/mass_balance.html @@ -0,0 +1,469 @@ + + + + + + pydfnworks.dfnFlow.mass_balance — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnFlow.mass_balance

+#!/usr/bin/env python
+
+# Calculate mass balance across a boundary of a DFN mesh
+# Make sure that you run PFLOTRAN with MASS_FLOWRATE under OUTPUT
+# Satish Karra
+# March 17, 2016
+# Updated Jeffrey Hyman Dec 19 2018
+# Revision Jeffrey Hyman Oct 5 2020
+
+import os
+import numpy as np
+import glob
+
+__author__ = 'Satish Karra'
+__email__ = 'satkarra@lanl.gov'
+
+# def parse_pflotran_input(pflotran_input_file):
+#     ''' Walk through PFLOTRAN input file and find inflow boundary, inflow and outflow pressure, and direction of flow
+
+#     Parameters
+#     ----------
+#         pflotran_input_file : string
+#             Name of PFLOTRAN input file
+
+#     Returns
+#     -------
+#         inflow_pressure : double
+#             Inflow Pressure boundary condition
+#         outflow_pressure : float
+#             Outflow pressure boundary condition
+#         inflow_file : string
+#             Name of inflow boundary .ex file
+#         direction : string
+#             Primary direction of flow x, y, or z
+
+#     Notes
+#     -----
+#     Currently only works for Dirichlet Boundary Conditions
+# '''
+
+#     with open(pflotran_input_file) as fp:
+#         outflow_found = False
+#         inflow_found = False
+#         for line in fp.readlines():
+#             if "BOUNDARY_CONDITION OUTFLOW" in line:
+#                 outflow_found = True
+#             if outflow_found:
+#                 if "REGION" in line:
+#                     outflow = line.split()[-1]
+#                     outflow_found = False
+#             if "BOUNDARY_CONDITION INFLOW" in line:
+#                 inflow_found = True
+#             if inflow_found:
+#                 if "REGION" in line:
+#                     inflow = line.split()[-1]
+#                     inflow_found = False
+
+#     with open(pflotran_input_file) as fp:
+#         inflow_name_found = False
+#         outflow_name_found = False
+#         inflow_found = False
+#         outflow_found = False
+
+#         for line in fp.readlines():
+#             if "REGION " + inflow in line:
+#                 inflow_name_found = True
+#             if inflow_name_found:
+#                 if "FILE" in line:
+#                     inflow_file = line.split()[-1]
+#                     inflow_name_found = False
+#             if "FLOW_CONDITION " + inflow in line:
+#                 inflow_found = True
+#             if inflow_found:
+#                 if "PRESSURE " in line:
+#                     if "dirichlet" not in line:
+#                         tmp = line.split()[-1]
+#                         tmp = tmp.split('d')
+#                         inflow_pressure = float(tmp[0]) * 10**float(tmp[1])
+#                         inflow_found = False
+
+#             if "REGION " + outflow in line:
+#                 outflow_name_found = True
+#             if outflow_name_found:
+#                 if "FILE" in line:
+#                     outflow_file = line.split()[-1]
+#                     outflow_name_found = False
+#             if "FLOW_CONDITION " + outflow in line:
+#                 outflow_found = True
+#             if outflow_found:
+#                 if "PRESSURE " in line:
+#                     if "dirichlet" not in line:
+#                         tmp = line.split()[-1]
+#                         tmp = tmp.split('d')
+#                         outflow_pressure = float(tmp[0]) * 10**float(tmp[1])
+#                         outflow_found = False
+
+#     if inflow_file == 'pboundary_left_w.ex' or inflow_file == 'pboundary_right_e.ex':
+#         direction = 'x'
+#     if inflow_file == 'pboundary_front_n.ex' or inflow_file == 'pboundary_back_s.ex':
+#         direction = 'y'
+#     if inflow_file == 'pboundary_top.ex' or inflow_file == 'pboundary_bottom.ex':
+#         direction = 'z'
+
+#     print("Inflow file: %s" % inflow_file)
+#     print("Inflow Pressure %e" % inflow_pressure)
+#     print("Outflow Pressure %e" % outflow_pressure)
+#     print("Primary Flow Direction : %s" % direction)
+
+#     return inflow_pressure, outflow_pressure, inflow_file, direction
+
+
+def check_inputs(boundary_file, direction, inflow_pressure, outflow_pressure):
+
+    ## Check Pressure
+    if inflow_pressure < outflow_pressure:
+        print(
+            "--> ERROR! Inflow Pressure less the outflow pressure. Cannot compute effective permeability"
+        )
+        return False
+
+    if not os.path.exists(boundary_file):
+        print(f"--> ERROR! Boundary file not found. Please check path.")
+        return False
+
+    ## Check Direction
+    fail = False
+    if direction == 'x':
+        if not boundary_file in [
+                'pboundary_left_w.ex', 'pboundary_right_e.ex'
+        ]:
+            print(
+                f"--> ERROR! Direction {direction} is not consistent with boundary file {boundary_file}. Cannot compute effective permeability."
+            )
+            return False
+
+    elif direction == 'y':
+        if not boundary_file in [
+                'pboundary_front_n.ex', 'pboundary_back_s.ex'
+        ]:
+            print(
+                f"--> ERROR! Direction {direction} is not consistent with boundary file {boundary_file}. Cannot compute effective permeability."
+            )
+            return False
+
+    elif direction == 'z':
+        if not boundary_file in ['pboundary_top.ex', 'pboundary_bottom.ex']:
+            print(
+                f"--> ERROR! Direction {direction} is not consistent with boundary file {boundary_file}. Cannot compute effective permeability."
+            )
+            return False
+    return True
+
+
+def flow_rate(darcy_vel_file, boundary_file):
+    '''Calculates the flow rate across the inflow boundary
+
+    Parameters
+    ----------
+        darcy_vel_file : string
+            Name of concatenated Darcy velocity file
+        boundary_file : string
+             ex file for the inflow boundary
+
+    Returns
+    -------
+        mass_rate : float
+            Mass flow rate across the inflow boundary
+        volume_rate : float
+            Volumetric flow rate across the inflow boundary
+
+    Notes
+    -----
+    None
+'''
+    # Calculate the mass flow rate
+    mass_rate = 0.0  #kg/s
+    volume_rate = 0.0  #m^3/s
+
+    dat_boundary = np.genfromtxt(boundary_file, skip_header=1)
+    dat = np.genfromtxt(darcy_vel_file)
+    for cell in dat_boundary[:, 0]:
+        if (np.any(dat[:, 0] == int(cell))):
+            ids = np.where(dat[:, 0] == int(cell))[0]
+            for idx in ids:
+                cell_up = int(dat[idx, 0])
+                cell_down = int(dat[idx, 1])
+                mass_flux = dat[idx, 2]  # in m/s , darcy flux, right? m3/m2/s
+                density = dat[idx, 3]  # in kg/m3
+                area = dat[idx, 4]  # in m^2
+                if (cell_up == int(cell)):
+                    mass_rate = mass_flux * area * \
+                    density + mass_rate  # in kg/s
+                    volume_rate = mass_flux * area + volume_rate  #in m3/s
+                else:
+                    mass_rate = - mass_flux * area * \
+                    density + mass_rate  # in kg/s
+                    volume_rate = -mass_flux * area + volume_rate  #in m3/s
+                #print cell_up, cell_down, mass_flux, density, area, mass_rate, volume_rate
+        if (np.any(dat[:, 1] == int(cell))):
+            ids = np.where(dat[:, 1] == int(cell))[0]
+            for idx in ids:
+                cell_up = int(dat[idx, 0])
+                cell_down = int(dat[idx, 1])
+                mass_flux = dat[idx, 2]  # in m/s
+                density = dat[idx, 3]  # in kg/m3
+                area = dat[idx, 4]  # in m^2
+                if (cell_up == int(cell)):
+                    mass_rate = mass_flux * area * \
+                    density + mass_rate  # in kg/s
+                    volume_rate = mass_flux * area + volume_rate  #in m3/s
+                else:
+                    mass_rate = - mass_flux * area * \
+                    density + mass_rate  # in kg/s
+                    volume_rate = -mass_flux * area + volume_rate  #in m3/s
+                #print cell_up, cell_down, mass_flux, density, area, mass_rate, volume_rate
+    return mass_rate, volume_rate
+
+
+def dump_effective_perm(local_jobname, mass_rate, volume_rate, domain,
+                        direction, inflow_pressure, outflow_pressure):
+    '''Compute the effective permeability of the DFN and write it to screen and to the file local_jobname_effective_perm.dat
+
+    Parameters
+    ----------
+        local_jobname  : string
+            Jobname
+        mass_rate : float
+            Mass flow rate through inflow boundary
+        volume_rate : float
+            Volumetric flow rate through inflow boundary
+        direction : string
+            Primary direction of flow (x, y, or z)
+        domain : dict
+            Dictionary of domain sizes in x, y, z
+        inflow_pressure : float
+            Inflow boundary pressure
+        outflow_pressure : float
+            Outflow boundary pressure
+
+    Returns
+    -------
+        None
+
+    Notes
+    -----
+    Information is written into (local_jobname)_effective_perm.txt
+'''
+
+    Lm3 = domain['x'] * domain['y'] * domain['z']  #L/m^3
+    # if flow is in x direction, cross section is domain['y']*domain['z']
+    if direction == 'x':
+        surface = domain['y'] * domain['z']
+        L = domain['x']
+    if direction == 'y':
+        surface = domain['x'] * domain['z']
+        L = domain['y']
+    if direction == 'z':
+        surface = domain['x'] * domain['y']
+        L = domain['z']
+    # Print the calculated mass flow rate in kg/s
+    mu = 8.9e-4  #dynamic viscosity of water at 20 degrees C, Pa*s
+    spery = 3600. * 24. * 365.25  #seconds per year
+    # compute pressure gradient
+    pgrad = (inflow_pressure - outflow_pressure) / L
+    #darcy flux over entire face m3/m2/s
+    q = volume_rate / surface
+
+    output_string = f'''The mass flow rate [kg/s]: {mass_rate:0.5e}
+The volume flow rate [m^3/s]: {volume_rate:0.5e}
+'''
+
+    if direction == 'x':
+        output_string += f'''The Darcy flow rate over {domain['y']} x {domain['z']} m^2 area [m^3/m^2/s]: {q:0.5e}
+The Darcy flow rate over {domain['y']} x {domain['z']} m^2 area [m^3/m^2/y]: {spery*q:0.5e}
+'''
+    elif direction == 'y':
+        output_string += f'''The Darcy flow rate over {domain['x']} x {domain['z']} m^2 area [m^3/m^2/s]: {q:0.5e}
+The Darcy flow rate over {domain['x']} x {domain['z']} m^2 area [m^3/m^2/y]: {spery*q:0.5e}
+'''
+    elif direction == 'y':
+        output_string += f'''The Darcy flow rate over {domain['x']} x {domain['y']} m^2 area [m^3/m^2/s]: {q:0.5e}
+The Darcy flow rate over {domain['x']} x {domain['y']} m^2 area [m^3/m^2/y]: {spery*q:0.5e}
+'''
+    output_string += f'The effective permeability of the domain [m^2]: {q * mu / pgrad:0.5e}'
+    print("\n--> Effective Permeability Properties: ")
+    print(output_string)
+    fp = open(f'{local_jobname}_effective_perm.txt', "w")
+    fp.write(output_string)
+    fp.close()
+    keff = q * mu / pgrad
+    return keff
+
+
+
+[docs] +def effective_perm(self, inflow_pressure, outflow_pressure, boundary_file, + direction): + '''Computes the effective permeability of a DFN in the primary direction of flow using a steady-state PFLOTRAN solution. + + Parameters + ---------- + self : object + DFN Class + inflow_pressure: float + Pressure at the inflow boundary face. Units are Pascal + outflow_pressure: float + Pressure at the outflow boundary face. Units are Pascal + boundary_file: string + Name of inflow boundary file, e.g., pboundary_left.ex + direction: string + Primary direction of flow, x, y, or z + + Returns + ------- + None + + Notes + ----- + 1. Information is written to screen and to the file self.local_jobname_effective_perm.txt + 2. Currently, only PFLOTRAN solutions are supported + 3. Assumes density of water at 20c + +''' + print("--> Computing Effective Permeability of Block\n") + if not self.flow_solver == "PFLOTRAN": + print( + "Incorrect flow solver selected. Cannot compute effective permeability" + ) + return 0 + + darcy_vel_file = 'darcyvel.dat' + # pflotran_input_file = self.local_dfnFlow_file + + print(f"--> Inflow file name:\t\t{boundary_file}") + print(f"--> Darcy Velocity File:\t{darcy_vel_file}") + print(f"--> Inflow Pressure:\t\t{inflow_pressure:0.5e} Pa") + print(f"--> Outflow Pressure:\t\t{outflow_pressure:0.5e} Pa") + print(f"--> Primary Flow Direction:\t{direction}") + + if not check_inputs(boundary_file, direction, inflow_pressure, + outflow_pressure): + return 1 + + mass_rate, volume_rate = flow_rate(darcy_vel_file, boundary_file) + keff = dump_effective_perm(self.local_jobname, mass_rate, volume_rate, + self.domain, direction, inflow_pressure, + outflow_pressure) + print("--> Complete\n\n") + self.keff = keff
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnFlow/pflotran.html b/docs/_modules/pydfnworks/dfnFlow/pflotran.html new file mode 100644 index 00000000..0c7bb029 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnFlow/pflotran.html @@ -0,0 +1,654 @@ + + + + + + pydfnworks.dfnFlow.pflotran — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pydfnworks.dfnFlow.pflotran

+"""
+functions for using pflotran in dfnworks
+"""
+import os
+import subprocess
+import sys
+import h5py
+import glob
+import shutil
+import ntpath
+from time import time
+import numpy as np
+
+
+
+[docs] +def lagrit2pflotran(self): + """ Takes output from LaGriT and processes it for use in PFLOTRAN. + Calls the function write_perms_and_correct_volumes_areas() and zone2ex + + Parameters + -------------- + self : object + DFN Class + + Returns + -------- + None + + Notes + -------- + None + + """ + if self.flow_solver != "PFLOTRAN": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + print('=' * 80) + print("Starting conversion of files for PFLOTRAN ") + + if self.inp_file == '': + error = 'Error: inp filename not attached to object\n' + sys.stderr.write(error) + sys.exit(1) + + # Check if UGE file was created by LaGriT, if it does not exists, exit + self.uge_file = self.inp_file[:-4] + '.uge' + if not os.path.isfile(self.uge_file): + error = 'Error. Cannot file uge file\nExiting\n' + sys.stderr.write(error) + sys.exit(1) + + self.write_perms_and_correct_volumes_areas() + self.zone2ex(zone_file='all') + self.dump_h5_files() + print("Conversion of files for PFLOTRAN complete") + print('=' * 80) + print("\n\n")
+ + +
+[docs] +def zone2ex(self, zone_file='', face='', boundary_cell_area=1.e-1): + """ + Convert zone files from LaGriT into ex format for LaGriT + + Parameters + ----------- + self : object + DFN Class + zone_file : string + Name of zone file + Face : Face of the plane corresponding to the zone file + zone_file : string + Name of zone file to work on. Can be 'all' processes all directions, top, bottom, left, right, front, back + boundary_cell_area : double + should be a large value relative to the mesh size to force pressure boundary conditions. + + Returns + ---------- + None + + Notes + ---------- + the boundary_cell_area should be a function of h, the mesh resolution + """ + print('*' * 80) + print('--> Converting zone files to ex') + + if self.uge_file == '': + error = 'Error: uge filename not assigned to object yet\n' + sys.stderr.write(error) + sys.exit(1) + + # Opening uge file + print('\n--> Opening uge file') + with open(self.uge_file, 'r') as fuge: + # Reading cell ids, cells centers and cell volumes + line = fuge.readline() + line = line.split() + num_cells = int(line[1]) + + cell_id = np.zeros(num_cells, 'int') + cell_coord = np.zeros((num_cells, 3), 'float') + cell_vol = np.zeros(num_cells, 'float') + + for cells in range(num_cells): + line = fuge.readline() + line = line.split() + cell_id[cells] = int(line.pop(0)) + line = [float(id) for id in line] + cell_vol[cells] = line.pop(3) + cell_coord[cells] = line + + print('--> Finished processing uge file\n') + + # loop through zone files + if zone_file == 'all': + zone_files = ['pboundary_front_n.zone', 'pboundary_back_s.zone', 'pboundary_left_w.zone', \ + 'pboundary_right_e.zone', 'pboundary_top.zone', 'pboundary_bottom.zone'] + face_names = ['north', 'south', 'west', 'east', 'top', 'bottom'] + else: + if zone_file == '': + error = 'ERROR: Please provide boundary zone filename!\n' + sys.stderr.write(error) + sys.exit(1) + if face == '': + error = 'ERROR: Please provide face name among: top, bottom, north, south, east, west !\n' + sys.stderr.write(error) + sys.exit(1) + zone_files = [zone_file] + face_names = [face] + + for iface, zone_file in enumerate(zone_files): + face = face_names[iface] + # Ex filename + ex_file = zone_file.strip('zone') + 'ex' + + # Opening the input file + print('--> Opening zone file: ', zone_file) + with open(zone_file, 'r') as fzone: + print('--> Reading boundary node ids') + node_array = fzone.read() + node_array = node_array.split() + num_nodes = int(node_array[4]) + node_array = np.array(node_array[5:-1], dtype='int') + print('--> Finished reading zone file') + + Boundary_cell_area_array = np.zeros(num_nodes, 'float') + for i in range(num_nodes): + Boundary_cell_area_array[ + i] = boundary_cell_area # Fix the area to a large number + + print('--> Finished calculating boundary connections') + boundary_cell_coord = [ + cell_coord[cell_id[i - 1] - 1] for i in node_array + ] + epsilon = self.h * 10**-3 + + if (face == 'top'): + boundary_cell_coord = [[cell[0], cell[1], cell[2] + epsilon] + for cell in boundary_cell_coord] + elif (face == 'bottom'): + boundary_cell_coord = [[cell[0], cell[1], cell[2] - epsilon] + for cell in boundary_cell_coord] + elif (face == 'north'): + boundary_cell_coord = [[cell[0], cell[1] + epsilon, cell[2]] + for cell in boundary_cell_coord] + elif (face == 'south'): + boundary_cell_coord = [[cell[0], cell[1] - epsilon, cell[2]] + for cell in boundary_cell_coord] + elif (face == 'east'): + boundary_cell_coord = [[cell[0] + epsilon, cell[1], cell[2]] + for cell in boundary_cell_coord] + elif (face == 'west'): + boundary_cell_coord = [[cell[0] - epsilon, cell[1], cell[2]] + for cell in boundary_cell_coord] + elif (face == 'well'): + boundary_cell_coord = [[cell[0], cell[1], cell[2] + epsilon] + for cell in boundary_cell_coord] + elif (face == 'none'): + boundary_cell_coord = [[cell[0], cell[1], cell[2]] + for cell in boundary_cell_coord] + else: + error = 'ERROR: unknown face. Select one of: top, bottom, east, west, north, south.\n' + sys.stderr.write(error) + sys.exit(1) + ## Write out ex files + with open(ex_file, 'w') as f: + f.write('CONNECTIONS\t%i\n' % node_array.size) + for idx, cell in enumerate(boundary_cell_coord): + f.write( + f"{node_array[idx]}\t{cell[0]:.12e}\t{cell[1]:.12e}\t{cell[2]:.12e}\t{Boundary_cell_area_array[idx]:.12e}\n" + ) + + print( + f'--> Finished writing ex file {ex_file} corresponding to the zone file: {zone_file} \n' + ) + + print('--> Converting zone files to ex complete') + print('*' * 80) + print()
+ + +
+[docs] +def write_perms_and_correct_volumes_areas(self): + """ Write permeability values to perm_file, write aperture values to aper_file, and correct volume areas in uge_file + + Parameters + ---------- + self : object + DFN Class + + Returns + --------- + None + + Notes + ---------- + Calls executable correct_uge + """ + print('*' * 80) + print("--> Correcting UGE file: Starting") + if self.flow_solver != "PFLOTRAN": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + print("--> Writing Perms and Correct Volume Areas") + if self.inp_file == '': + error = 'ERROR: inp file must be specified!\n' + sys.stderr.write(error) + sys.exit(1) + + if self.uge_file == '': + error = 'ERROR: uge file must be specified!\n' + sys.stderr.write(error) + sys.exit(1) + + if self.perm_file == '' and self.perm_cell_file == '': + error = 'ERROR: perm file must be specified!\n' + sys.stderr.write(error) + sys.exit(1) + + if self.aper_file == '' and self.aper_cell_file == '': + error = 'ERROR: aperture file must be specified!\n' + sys.stderr.write(error) + sys.exit(1) + + t = time() + # Make input file for C UGE converter + with open("convert_uge_params.txt", "w") as fp: + fp.write(f"{self.inp_file}\n") + fp.write(f"{self.mat_file}\n") + fp.write(f"{self.uge_file}\n") + fp.write(f"{self.uge_file[:-4]}_vol_area.uge\n") + if self.cell_based_aperture: + fp.write(f"{self.aper_cell_file}\n") + fp.write("1\n") + else: + fp.write(f"{self.aper_file}\n") + fp.write("-1\n") + + ## dump aperture file + self.dump_aperture(self.aper_file, format='fehm') + ## execute convert uge C code + cmd = os.environ['CORRECT_UGE_EXE'] + ' convert_uge_params.txt' + print(f"\n>> {cmd}\n") + failure = subprocess.call(cmd, shell=True) + if failure > 0: + error = 'ERROR: UGE conversion failed\nExiting Program\n' + sys.stderr.write(error) + sys.exit(1) + elapsed = time() - t + print( + f'--> Time elapsed for UGE file conversion: {elapsed:0.3f} seconds\n') + + print("--> Correcting UGE file: Complete") + print('*' * 80) + print()
+ + + +def dump_h5_files(self): + """ Write permeability values to cell ids and permeability values to dfn_properties.h5 file for pflotran. + + Parameters + ---------- + self : object + DFN Class + + Returns + --------- + None + + Notes + ---------- + Hydraulic properties need to attached to the class prior to running this function. Use DFN.assign_hydraulic_properties() to do so. + """ + print('*' * 80) + print("--> Dumping h5 file") + filename = 'dfn_properties.h5' + print(f'--> Opening HDF5 File {filename}') + with h5py.File(filename, mode='w') as h5file: + print('--> Allocating cell index array') + print('--> Writing cell indices') + iarray = np.arange(1, self.num_nodes + 1) + dataset_name = 'Cell Ids' + h5dset = h5file.create_dataset(dataset_name, data=iarray) + print('--> Creating permeability array') + print('--> Note: This script assumes isotropic permeability') + for i in range(self.num_nodes): + self.perm_cell[i] = self.perm[self.material_ids[i] - 1] + print('--> Writting Permeability') + dataset_name = 'Permeability' + h5dset = h5file.create_dataset(dataset_name, data=self.perm_cell) + + print("--> Done writting h5 file") + print('*' * 80) + print() + +
+[docs] +def pflotran(self, transient=False, restart=False, restart_file=''): + """ Run PFLOTRAN. Copy PFLOTRAN run file into working directory and run with ncpus + + Parameters + ---------- + self : object + DFN Class + transient : bool + Boolean if PFLOTRAN is running in transient mode + restart : bool + Boolean if PFLOTRAN is restarting from checkpoint + restart_file : string + Filename of restart file + + Returns + ---------- + None + + Notes + ---------- + Runs PFLOTRAN Executable, see http://www.pflotran.org/ for details on PFLOTRAN input cards + """ + if self.flow_solver != "PFLOTRAN": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + try: + shutil.copy(os.path.abspath(self.dfnFlow_file), + os.path.abspath(os.getcwd())) + except: + error = "--> ERROR!! Unable to copy PFLOTRAN input file\n" + sys.stderr.write(error) + sys.exit(1) + + print("=" * 80) + print("--> Running PFLOTRAN") + + mpirun = os.environ['PETSC_DIR'] + '/' + os.environ[ + 'PETSC_ARCH'] + '/bin/mpirun' + + if not (os.path.isfile(mpirun) and os.access(mpirun, os.X_OK)): + # PETSc did not install MPI. Hopefully, the user has their own MPI. + mpirun = 'mpirun' + + cmd = mpirun + ' -np ' + str(self.ncpu) + \ + ' ' + os.environ['PFLOTRAN_EXE'] + ' -pflotranin ' + self.local_dfnFlow_file + + print(f"--> Running: {cmd}") + subprocess.call(cmd, shell=True) + + if restart: + try: + shutil.copy(os.path.abspath(restart_file), + os.path.abspath(os.getcwd())) + except: + error = "--> ERROR!! Unable to copy PFLOTRAN restart input file\n" + sys.stderr.write(error) + sys.exit(1) + + print("=" * 80) + print("--> Running PFLOTRAN") + cmd = os.environ['PETSC_DIR']+'/'+os.environ['PETSC_ARCH']+'/bin/mpirun -np ' + str(self.ncpu) + \ + ' ' + os.environ['PFLOTRAN_EXE'] + ' -pflotranin ' + ntpath.basename(restart_file) + print("Running: %s" % cmd) + subprocess.call(cmd, shell=True) + + print('=' * 80) + print("--> Running PFLOTRAN Complete") + print('=' * 80) + print("\n")
+ + + +
+[docs] +def pflotran_cleanup(self, index_start=0, index_finish=1, filename=''): + """pflotran_cleanup + Concatenate PFLOTRAN output files and then delete them + + Parameters + ----------- + self : object + DFN Class + index : int + If PFLOTRAN has multiple dumps use this to pick which dump is put into cellinfo.dat and darcyvel.dat + Returns + ---------- + None + + Notes + ---------- + Can be run in a loop over all pflotran dumps + """ + if self.flow_solver != "PFLOTRAN": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + if filename == '': + filename = self.local_dfnFlow_file[:-3] + else: + filename = ntpath.basename(filename[:-3]) + + print('--> Processing PFLOTRAN output') + + for index in range(index_start, index_finish + 1): + cmd = 'cat ' + filename + '-cellinfo-%03d-rank*.dat > cellinfo_%03d.dat' % ( + index, index) + print("Running >> %s" % cmd) + subprocess.call(cmd, shell=True) + + cmd = 'cat ' + filename + '-darcyvel-%03d-rank*.dat > darcyvel_%03d.dat' % ( + index, index) + print(f"--> Running >> {cmd}") + subprocess.call(cmd, shell=True) + + #for fl in glob.glob(self.local_dfnFlow_file[:-3]+'-cellinfo-000-rank*.dat'): + # os.remove(fl) + #for fl in glob.glob(self.local_dfnFlow_file[:-3]+'-darcyvel-000-rank*.dat'): + # os.remove(fl) + + for fl in glob.glob(filename + '-cellinfo-%03d-rank*.dat' % index): + os.remove(fl) + for fl in glob.glob(filename + '-darcyvel-%03d-rank*.dat' % index): + os.remove(fl) + try: + os.symlink("darcyvel_%03d.dat" % index_finish, "darcyvel.dat") + except: + print("--> WARNING!!! Unable to create symlink for darcyvel.dat") + try: + os.symlink("cellinfo_%03d.dat" % index_finish, "cellinfo.dat") + except: + print("--> WARNING!!! Unable to create symlink for cellinfo.dat")
+ + + +
+[docs] +def parse_pflotran_vtk_python(self, grid_vtk_file=''): + """ Adds CELL_DATA to POINT_DATA in the VTK output from PFLOTRAN. + Parameters + ---------- + self : object + DFN Class + grid_vtk_file : string + Name of vtk file with mesh. Typically local_dfnFlow_file.vtk + + Returns + -------- + None + + Notes + -------- + If DFN class does not have a vtk file, inp2vtk_python is called + """ + print('--> Parsing PFLOTRAN output with Python') + + if self.flow_solver != "PFLOTRAN": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + if grid_vtk_file: + self.vtk_file = grid_vtk_file + else: + self.inp2vtk_python() + + grid_file = self.vtk_file + + files = glob.glob('*-[0-9][0-9][0-9].vtk') + files.sort() + + with open(grid_file, 'r') as f: + grid = f.readlines()[3:] + + out_dir = 'parsed_vtk' + + for line in grid: + if 'POINTS' in line: + num_cells = line.strip(' ').split()[1] + + for file in files: + print(f"--> Processing file: {file}") + with open(file, 'r') as f: + pflotran_out = f.readlines()[4:] + pflotran_out = [ + w.replace('CELL_DATA', 'POINT_DATA ') for w in pflotran_out + ] + header = [ + '# vtk DataFile Version 2.0\n', 'PFLOTRAN output\n', 'ASCII\n' + ] + filename = out_dir + '/' + file + if not os.path.exists(os.path.dirname(filename)): + os.makedirs(os.path.dirname(filename)) + with open(filename, 'w') as f: + for line in header: + f.write(line) + for line in grid: + f.write(line) + f.write('\n') + f.write('\n') + if 'vel' in file: + f.write('POINT_DATA\t ' + num_cells + '\n') + for line in pflotran_out: + f.write(line) + os.remove(file) + print('--> Parsing PFLOTRAN output complete')
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGen/generation/generator.html b/docs/_modules/pydfnworks/dfnGen/generation/generator.html new file mode 100644 index 00000000..ccbfbf08 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/generation/generator.html @@ -0,0 +1,614 @@ + + + + + + pydfnworks.dfnGen.generation.generator — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.generation.generator

+import os
+import sys
+import numpy as np
+import shutil
+from time import time
+import subprocess
+
+
+
+[docs] +def dfn_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 : object + DFN Class 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() + if output: + self.output_report() + # Mesh Network + self.mesh_network() + print('=' * 80) + print('dfnGen Complete') + print('=' * 80)
+ + + +
+[docs] +def make_working_directory(self, delete=False): + ''' Make working directory for dfnWorks Simulation + + Parameters + ---------- + self : object + DFN Class object + + Returns + ------- + None + + Notes + ----- + If directory already exists, user is prompted if they want to overwrite and proceed. If not, program exits. + ''' + + if not delete: + try: + os.mkdir(self.jobname) + except OSError: + if os.path.isdir(self.jobname): + print('\nFolder ', self.jobname, ' exists') + keep = input('Do you want to delete it? [yes/no] \n') + if keep == 'yes' or keep == 'y': + print('Deleting', self.jobname) + shutil.rmtree(self.jobname) + print('Creating', self.jobname) + os.mkdir(self.jobname) + elif keep == '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: + if not os.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] +def create_network(self): + ''' Execute dfnGen + + Parameters + ---------- + self : object + DFN Class + + 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) + + if os.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)
+ + +def parse_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 + ------- + num_poly: int + Number of Polygons + h: float + Meshing length scale h + dudded_points: int + Expected number of dudded points in Filter (LaGriT) + visual_mode : bool + If True, reduced_mesh.inp is created (not suitable for flow and transport), if False, full_mesh.inp is created + domain: dict + x,y,z domain sizes + + Notes + ----- + None + """ + if not quiet: + 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())) + fparams.close() + + if not quiet: + print("--> Number of Fractures: %d" % self.num_frac) + print(f"--> h: {self.h:0.2e} m") + if self.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") + + +def gather_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 + if self.num_frac == 1: + data = np.array([data]) + + self.radii[:, :2] = data[:, :2] + for i in range(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 = [] + with open('dfnGen_output/translations.dat', "r") as fp: + fp.readline() # header + for i, line in enumerate(fp.readlines()): + if "R" not in line: + 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 + if self.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)) + for i in range(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') + +def read_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 + ''' + + try: + data = np.genfromtxt(file_path) + except IOError: + data = np.array([]) + + + try: + array_length = len(data) + except: + data = np.array([data]) + + return data + + +def assign_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 ") + for i in range(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'] + + if hy_variable is not None: + 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 + ##Logic here, loop through user defined fractures + ## first check flag to insert + fracture_num = 1 + if self.params['insertUserRectanglesFirst']['value'] == 0: + print('--> Inserting User Ellipse Hydraulic Params First') + for i in range(len(self.user_ell_params)): + for j in range(self.user_ell_params[i]['nPolygons']): + 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 + + for i in range(len(self.user_rect_params)): + for j in range(self.user_rect_params[i]['nPolygons']): + 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 + + for i in range(len(self.user_poly_params)): + for j in range(self.user_poly_params[i]['nPolygons']): + 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 + + else: + print('--> Inserting User Rectangles Hydraulic Params First') + for i in range(len(self.user_rect_params)): + for j in range(self.user_rect_params[i]['nPolygons']): + 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 + + for i in range(len(self.user_ell_params)): + for j in range(self.user_ell_params[i]['nPolygons']): + 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 + + for i in range(len(self.user_poly_params)): + for j in range(self.user_poly_params[i]['nPolygons']): + 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 + + # self.dump_hydraulic_values() + print("--> Assign hydraulic properties: Complete ") + +
+[docs] +def grab_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 + + if self.num_frac == 1: + polygon_data = np.array([polygon_data]) + + + for i in range(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 = [] + for j in range(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/docs/_modules/pydfnworks/dfnGen/generation/input_checking/check_input.html b/docs/_modules/pydfnworks/dfnGen/generation/input_checking/check_input.html new file mode 100644 index 00000000..a1a4d712 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/generation/input_checking/check_input.html @@ -0,0 +1,227 @@ + + + + + + pydfnworks.dfnGen.generation.input_checking.check_input — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.generation.input_checking.check_input

+import os
+import shutil
+import sys
+
+#pydfnworks modules
+import pydfnworks.dfnGen.generation.input_checking.helper_functions as hf
+from pydfnworks.dfnGen.generation.input_checking.parsing import parse_input
+from pydfnworks.dfnGen.generation.input_checking.verifications import verify_params
+from pydfnworks.dfnGen.generation.input_checking.write_input_file import dump_params
+from pydfnworks.dfnGen.generation.input_checking.add_fracture_family_to_params import write_fracture_families
+
+
+
+
+
+
+
+[docs] +def check_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 + if from_file: + # Copy input file + if os.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/docs/_modules/pydfnworks/dfnGen/generation/input_checking/fracture_family.html b/docs/_modules/pydfnworks/dfnGen/generation/input_checking/fracture_family.html new file mode 100644 index 00000000..4a8d69d6 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/generation/input_checking/fracture_family.html @@ -0,0 +1,584 @@ + + + + + + pydfnworks.dfnGen.generation.input_checking.fracture_family — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.generation.input_checking.fracture_family

+import sys
+
+
+def fracture_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}'
+            }
+        }
+    }
+    return family
+
+
+
+
+
+
+
+[docs] +def add_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() + if shape == "rect": + family['type']['value']['rect'] = True + family['number_of_points']['value'] = 4 + elif shape == "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 + + if p32: + family['p32']['value'] = p32 + family['probability']['value'] = p32 + elif probability: + 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 + if theta != None and phi != None: + if self.params['orientationOption']['value'] == None: + print('Setting orientationOption = 0 (theta/phi)') + self.params['orientationOption']['value'] = 0 + if self.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) + + if trend != None and plunge != None: + if self.params['orientationOption']['value'] == None: + print('Setting orientationOption = 1 (trend/plunge)') + self.params['orientationOption']['value'] = 1 + if self.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) + + if strike != None and dip != None: + if self.params['orientationOption']['value'] == None: + print('Setting orientationOption = 2 (strike/dip)') + self.params['orientationOption']['value'] = 2 + if self.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 + if distribution == "tpl": + family['distribution']['value']['tpl'] = True + if alpha != 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) + elif distribution == "log_normal": + family['distribution']['value']['log_normal'] = True + if log_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) + if log_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) + + elif distribution == "exp": + family['distribution']['value']['exp'] = True + if exp_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) + elif distribution == "constant": + family['distribution']['value']['constant'] = True + if constant != 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) + + if distribution != "constant": + if not min_radius or not max_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 + + if family_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/docs/_modules/pydfnworks/dfnGen/generation/input_checking/user_defined_fracture_functions.html b/docs/_modules/pydfnworks/dfnGen/generation/input_checking/user_defined_fracture_functions.html new file mode 100644 index 00000000..e61bf7e8 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/generation/input_checking/user_defined_fracture_functions.html @@ -0,0 +1,753 @@ + + + + + + pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions

+# -*- coding: utf-8 -*-
+"""
+Created on Mon Aug 29 09:17:14 2022
+
+@author: 369984
+"""
+
+import sys
+import os
+from numpy import pi
+
+from pydfnworks.dfnGen.generation.input_checking.helper_functions import print_error, print_warning
+
+
+def check_angle_option(angle_option, array):
+    for val in array:
+        if angle_option == "radian":
+            if val > 2 * pi:
+                print_warning(
+                    "Value greater than 2 PI, angle option of radians has been selected"
+                )
+        elif angle_option == "degree":
+            if val > 360:
+                print_warning(
+                    "Value greater than 2 PI, angle option of radians has been selected"
+                )
+
+
+
+
+
+
+
+[docs] +def add_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) + + Paramters + ---------------- + 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 + + + if aperture is not None: + if len(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") + if transmissivity is not None: + if len(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") + if permeability is not None: + if len(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") + + + if shape == 'rect': + self.params['RectByCoord_Input_File_Path']['value'] = filename + if by_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) + + elif shape == 'ell': + self.params['EllByCoord_Input_File_Path']['value'] = filename + if by_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) + elif shape == '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] +def add_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 + if filename: + fracture_dictionary['filename'] = filename + else: + filename = self.jobname + f"/dfnGen_output/user_defined_{shape}.dat" + fracture_dictionary['filename'] = filename + + # Check radius is positive. + if radii > 0: + fracture_dictionary['Radii:'] = radii + else: + print_error( + f"Error. Fracture radius must be positive. Value provided {radii}. Exiting." + ) + + # Check Aspect Ratio is positive + if aspect_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. + if beta >= 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'] + if angle_option in angle_options: + fracture_dictionary['AngleOption:'] = angle_option + else: + print_error( + f"Error. Unknown angle_option value provided: {angle_option}. Acceptable values are 'radian', 'degree'.\nExiting." + ) + + if len(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 + if orientation_option == 'normal': + fracture_dictionary['userOrientationOption:'] = 0 + if normal_vector: + fracture_dictionary['Normal:'] = normal_vector + else: + print_error( + "Error. Requested user fracture orienation 0, but normal vector was not provided. exiting." + ) + if len(normal_vector) != 3: + print_error( + f"Error. Normal vector must have 3 elements, only {len(normal_vector)} provided.\nNormal: {normal_vector}. Exiting" + ) + + elif orientation_option == 'trend_plunge': + fracture_dictionary['userOrientationOption:'] = 1 + if trend_plunge: + fracture_dictionary['Trend_Plunge:'] = trend_plunge + else: + print_error( + "Error. Requested user fracture orienation trend_plunge, but trend_plunge was not provided. exiting." + ) + + if len(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) + + elif orientation_option == 'dip_strike': + fracture_dictionary['userOrientationOption:'] = 2 + if dip_strike: + fracture_dictionary['Dip_Strike:'] = dip_strike + else: + print_error( + "Error. Requested user fracture orienation dip_strike, but dip_strike was not provided. exiting." + ) + if len(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 + if shape == '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) + + elif shape == 'ell': + if number_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)
+ + + +def write_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) + + if n_ells > 0: + print( + f"--> Writing user defined ellispes to file {self.params['UserEll_Input_File_Path']['value']}" + ) + with open(self.params['UserEll_Input_File_Path']['value'], + 'w+') as ell_file: + ell_file.write(f'nUserEll: {n_ells} \n \n') + orientation_option = self.user_ell_params[0][ + 'userOrientationOption:'] + for key in self.user_ell_params[0].keys(): + if key == 'userOrientationOption:': + value = self.user_ell_params[0][key] + ell_file.write(f'{key} {value} \n \n') + elif key == 'Normal:': + if orientation_option == 0: + ell_file.write(f'{key} \n') + for j in range(n_ells): + value = self.user_ell_params[j][key] + if value is not None: + 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 + + elif key == 'Trend_Plunge:': + + if orientation_option == 1: + ell_file.write(f'{key} \n') + for j in range(n_ells): + value = self.user_ell_params[j][key] + if value is not None: + 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 + + elif key == 'Dip_Strike:': + + if orientation_option == 2: + ell_file.write(f'{key} \n') + for j in range(n_ells): + value = self.user_ell_params[j][key] + if value is not None: + 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 + + elif key == 'filename' or key == 'shape': + continue + else: + + ell_file.write(f'{key} \n') + for j in range(n_ells): + value = self.user_ell_params[j][key] + ell_file.write(f'{value} \n') + ell_file.write('\n') + + if n_rects > 0: + + print( + f"--> Writing user defined rectangles to file {self.params['UserRect_Input_File_Path']['value']}" + ) + with open(self.params['UserRect_Input_File_Path']['value'], + 'w+') as rect_file: + + rect_file.write(f'nUserRect: {n_rects} \n \n') + + orientation_option = self.user_rect_params[0][ + 'userOrientationOption:'] + + for key in self.user_rect_params[0].keys(): + + if key == 'userOrientationOption:': + + value = self.user_rect_params[0][key] + rect_file.write(f'{key} {value} \n \n') + + elif key == 'Normal:': + + if orientation_option == 0: + rect_file.write(f'{key} \n') + for j in range(n_rects): + value = self.user_rect_params[j][key] + if value is not None: + 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 + + elif key == 'Trend_Plunge:': + + if orientation_option == 1: + rect_file.write(f'{key} \n') + for j in range(n_rects): + value = self.user_rect_params[j][key] + if value is not None: + 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 + + elif key == 'Dip_Strike:': + + if orientation_option == 2: + rect_file.write(f'{key} \n') + for j in range(n_rects): + value = self.user_rect_params[j][key] + if value is not None: + 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 + + elif key == 'filename': + continue + elif key == 'shape': + continue + + else: + + rect_file.write(f'{key} \n') + for j in range(n_rects): + value = self.user_rect_params[j][key] + rect_file.write(f'{value} \n') + rect_file.write('\n') + + +def determine_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 + + if aperture is not None: + hy_prop_type = 'aperture' + + if transmissivity is not None: + if hy_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' + + if permeability is not None: + if hy_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' + + if hy_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) + + return hy_prop_type +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGen/generation/output_report/gen_output.html b/docs/_modules/pydfnworks/dfnGen/generation/output_report/gen_output.html new file mode 100644 index 00000000..1a76212a --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/generation/output_report/gen_output.html @@ -0,0 +1,243 @@ + + + + + + pydfnworks.dfnGen.generation.output_report.gen_output — dfnWorks 2.7 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>
+"""
+
+import os
+import matplotlib
+
+matplotlib.use("Agg")
+import matplotlib.pylab as plt
+from matplotlib import rc
+
+rc('text', usetex=True)
+
+import pydfnworks.dfnGen.generation.generator
+from pydfnworks.dfnGen.generation.output_report.gather_information import *
+from pydfnworks.dfnGen.generation.output_report.plot_fracture_orientations import plot_fracture_orientations
+from pydfnworks.dfnGen.generation.output_report.plot_fracture_radii import plot_fracture_radii
+from pydfnworks.dfnGen.generation.output_report.plot_fracture_centers import plot_fracture_centers
+from pydfnworks.dfnGen.generation.output_report.plot_fram_information import plot_fram_information
+from pydfnworks.dfnGen.generation.output_report.plot_intersection_lengths import plot_intersection_lengths
+from pydfnworks.dfnGen.generation.output_report.make_pdf import make_pdf
+
+
+def setup_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
+
+
+  """
+
+    if not os.path.isdir(params["output_dir"]):
+        os.mkdir(params["output_dir"])
+    if not os.path.isdir(f"{params['output_dir']}/network"):
+        os.mkdir(f"{params['output_dir']}/network")
+    for i in range(1, params["num_families"] + 1):
+        if not os.path.isdir(f"{params['output_dir']}/family_{i}"):
+            os.mkdir(f"{params['output_dir']}/family_{i}")
+
+
+
+[docs] +def output_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 + if len(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/docs/_modules/pydfnworks/dfnGen/generation/stress.html b/docs/_modules/pydfnworks/dfnGen/generation/stress.html new file mode 100644 index 00000000..15f5ce90 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/generation/stress.html @@ -0,0 +1,274 @@ + + + + + + pydfnworks.dfnGen.generation.stress — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.generation.stress

+import math as m
+import numpy as np
+
+# from pydfnworks
+from pydfnworks.dfnGen.generation.hydraulic_properties import convert
+
+
+
+[docs] +def stress_based_apertures(self, + sigma_mat, + friction_angle=25.0, + dilation_angle=5, + critical_shear_displacement=0.003, + shear_modulus=10.e9, + min_b=1e-10, + shear_stiffness=400.e9): + """ Takes stress tensor as input (defined in dfn run file) and calculates new apertures based on Bandis equations. New aperture and permeability values are written to files. + + + Parameters + ---------------------- + sigma_mat : array + 3 x 3 stress tensor (units in Pa) + friction_angle : float + Friction angle (Degrees) + dilation_angle : float + Dilation angle (Degrees) + critical_shear_displacement : float + Critical shear displacement + shear_modulus : float + Shear modulus (Pa) + min_b : float + Minimum aperture (m) + shear_stiffness : float + Shear stiffness (Pa/m) + + Returns + ---------------------- + None + + Notes + ---------------------- + + For details of implementation see + + "Sweeney, Matthew Ryan, and J. D. Hyman. "Stress effects on flow and transport in three‐dimensional fracture networks." Journal of Geophysical Research: Solid Earth 125.8 (2020): e2020JB019754." + + and + + Baghbanan, Alireza, and Lanru Jing. "Stress effects on permeability in a fractured rock mass with correlated fracture length and aperture." International journal of rock mechanics and mining sciences 45.8 (2008): 1320-1334. + + and + + Zhao, Zhihong, et al. "Impact of stress on solute transport in a fracture network: A comparison study." Journal of Rock Mechanics and Geotechnical Engineering 5.2 (2013): 110-123. + + """ + print("--> Computing aperture based on stress tensor") + print("\n--> Stress Tensor (Pa):\n") + print( + f"\t{sigma_mat[0][0]:0.2e} {sigma_mat[0][1]:0.2e} {sigma_mat[0][2]:0.2e}" + ) + print( + f"\t{sigma_mat[1][0]:0.2e} {sigma_mat[1][1]:0.2e} {sigma_mat[1][2]:0.2e}" + ) + print( + f"\t{sigma_mat[2][0]:0.2e} {sigma_mat[2][1]:0.2e} {sigma_mat[2][2]:0.2e}" + ) + print() + + # write stress to file. + with open("stress.dat", "w") as fstress: + fstress.write( + f"\t{sigma_mat[0][0]:0.2e} {sigma_mat[0][1]:0.2e} {sigma_mat[0][2]:0.2e}" + ) + fstress.write( + f"\t{sigma_mat[1][0]:0.2e} {sigma_mat[1][1]:0.2e} {sigma_mat[1][2]:0.2e}" + ) + fstress.write( + f"\t{sigma_mat[2][0]:0.2e} {sigma_mat[2][1]:0.2e} {sigma_mat[2][2]:0.2e}" + ) + + # read fracture data: + initial_aperture = self.aperture + normals = self.normal_vectors + radii_frac = self.radii[:, + 0] #og in case of bugs np.genfromtxt('radii_Final.dat', skip_header=2)[:, 0] + num_frac = len(initial_aperture) + b = np.zeros(num_frac) + + # Cycle through fractures and compute new aperture base on stress field and user defined parameters + for i in range(num_frac): + # Magnitude of normal stress + sigma_mag = sigma_mat[0][0]*(normals[i][0])**2 + \ + sigma_mat[1][1]*(normals[i][1])**2 + \ + sigma_mat[2][2]*(normals[i][2])**2 + \ + 2*(sigma_mat[0][1]*normals[i][0]*normals[i][1] + \ + sigma_mat[1][2]*normals[i][1]*normals[i][2] + \ + sigma_mat[0][2]*normals[i][0]*normals[i][2]) + + T_1 = sigma_mat[0][0]*normals[i][0] + \ + sigma_mat[0][1]*normals[i][1] + \ + sigma_mat[0][2]*normals[i][2] + + T_2 = sigma_mat[1][0]*normals[i][0] + \ + sigma_mat[1][1]*normals[i][1] + \ + sigma_mat[1][2]*normals[i][2] + + T_3 = sigma_mat[2][0]*normals[i][0] + \ + sigma_mat[2][1]*normals[i][1] + \ + sigma_mat[2][2]*normals[i][2] + + stress_sqr = (T_1)**2 + (T_2)**2 + (T_3)**2 + # Magnitude of shear stress + shear_stress = np.sqrt(max(0, stress_sqr - (sigma_mag)**2)) + # Critical normal stress (see Zhao et al. 2013 JRMGE) + sigma_nc = (0.487 * initial_aperture[i] * 1e6 + 2.51) * 1e6 + # Normal displacement + normal_displacement = (9 * sigma_mag * initial_aperture[i]) / ( + sigma_nc + 10 * sigma_mag) + # Shear dilation + # print(normal_displacement) + shear_stress_critical = sigma_mag * m.tan(m.radians(friction_angle)) + # Fracture half length + l = radii_frac[i] + + # rock stiffness + rock_stiffness = 0.92 * shear_modulus / l + ks1 = shear_stiffness + rock_stiffness + ks2 = rock_stiffness + # + if shear_stress > shear_stress_critical: + dilation_tmp = (shear_stress - shear_stress_critical * + (1 - ks2 / ks1)) / (ks2) + else: + dilation_tmp = 0 + + dilation = min(dilation_tmp, critical_shear_displacement) * m.tan( + m.radians(dilation_angle)) + #dilation = dilation_tmp * m.tan(m.radians(dilation_angle)) + # take the max of the computed and provided minimum aperture. + b[i] = max(min_b, initial_aperture[i] - normal_displacement + dilation) + + diff = abs(b - initial_aperture) + diff2 = diff**2 + print(f"--> L2 change in apertures {np.sqrt(diff.sum()):0.2e}") + print(f"--> Maximum change in apertures {max(diff):0.2e}") + + self.perm = convert(b, 'aperture', 'permeability') + self.transmissivity = convert(b, 'aperture', 'transmissivity') + self.aperture = b + # self.dump_hydraulic_values() + + print("--> Computing aperture based on stress field complete ")
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGen/meshing/add_attribute_to_mesh.html b/docs/_modules/pydfnworks/dfnGen/meshing/add_attribute_to_mesh.html new file mode 100644 index 00000000..8ca15b3c --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/meshing/add_attribute_to_mesh.html @@ -0,0 +1,270 @@ + + + + + + pydfnworks.dfnGen.meshing.add_attribute_to_mesh — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.meshing.add_attribute_to_mesh

+import subprocess
+from numpy import genfromtxt, zeros, savetxt
+import sys
+import os
+
+from pydfnworks.dfnGen.meshing.mesh_dfn_helper import run_lagrit_script
+
+
+def create_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. Can 
+    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))
+    if not os.path.isfile(matid_file):
+        error = f"ERROR!!! Cannot locate the file '{matid_file}'\nExiting\n"
+        sys.stderr.write(error)
+        sys.exit(1)
+    nodes = genfromtxt(matid_file, skip_header=3).astype(int)
+    value_by_node = zeros(len(nodes))
+    for i, n in enumerate(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")
+    return variable_file_by_node
+
+
+def create_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"
+    fp = open(lagrit_file, "w")
+    fp.write(lagrit_script)
+    fp.flush()
+    fp.close()
+    print("Complete")
+    return lagrit_file
+
+
+
+[docs] +def add_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 + if not os.path.isfile(variable_file): + error = f"Error -- in function 'add_variable_to_mesh'. The file {variable_file} is not in current directory. Please check the filename." + sys.stderr.write(error) + sys.exit(1) + + if not os.path.isfile(mesh_file_in): + error = f"Error -- in function 'add_variable_to_mesh'. The mesh file {mesh_file_in} is not in current directory. Please check the filename." + sys.stderr.write(error) + sys.exit(1) + + # if an output mesh file is not provided, set target mesh to be the source mesh. + if mesh_file_out is None: + 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}" + ) + + if node_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/docs/_modules/pydfnworks/dfnGen/meshing/mesh_dfn.html b/docs/_modules/pydfnworks/dfnGen/meshing/mesh_dfn.html new file mode 100644 index 00000000..44264814 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/meshing/mesh_dfn.html @@ -0,0 +1,292 @@ + + + + + + pydfnworks.dfnGen.meshing.mesh_dfn — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.meshing.mesh_dfn

+"""
+.. module:: mesh_dfn.py
+   :synopsis: meshing driver for DFN 
+.. moduleauthor:: Jeffrey Hyman <jhyman@lanl.gov>
+
+"""
+
+import os
+import sys
+from numpy import genfromtxt, sort
+# pydfnworks Modules
+from pydfnworks.dfnGen.meshing import mesh_dfn_helper as mh
+from pydfnworks.dfnGen.meshing import lagrit_scripts_poisson_disc as lagrit
+from pydfnworks.dfnGen.meshing import run_meshing as run_mesh
+from pydfnworks.dfnGen.meshing.poisson_disc.poisson_functions import single_fracture_poisson, dump_poisson_params
+
+
+
+[docs] +def mesh_network(self, + prune=False, + uniform_mesh=False, + production_mode=True, + coarse_factor=8, + slope=0.1, + min_dist=1, + max_dist=40, + concurrent_samples=10, + grid_size=10, + well_flag=False): + ''' Mesh fracture network using LaGriT + + Parameters + ---------- + self : object + DFN Class + prune : bool + If prune is False, mesh entire network. If prune is True, mesh only fractures in self.prune_file + uniform_mesh : bool + If true, mesh is uniform resolution. If False, mesh is spatially variable + production_mode : bool + If True, all working files while meshing are cleaned up. If False, then working files will not be deleted + 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 + coarse_factor: float + Maximum resolution of the mesh. Given as a factor of h + slope : float + slope of variable coarsening resolution. + min_dist : float + Range of constant min-distance around an intersection (in units of h). + max_dist : float + Range over which the min-distance between nodes increases (in units of h) + concurrent_samples : int + number of new candidates sampled around an accepted node at a time. + grid_size : float + side length of the occupancy grid is given by H/occupancy_factor + well_flag : bool + If well flag is true, higher resolution around the points in + + Returns + ------- + None + + Notes + ------ + 1. For uniform resolution mesh, set slope = 0 + 2. All fractures in self.prune_file must intersect at least 1 other fracture + + ''' + + print('=' * 80) + print("Meshing DFN using LaGriT : Starting") + print('=' * 80) + + if uniform_mesh: + slope = 0 # Setting slope = 0, results in a uniform mesh + + if prune: + if self.prune_file == "": + error = "ERROR!! User requested pruning in meshing but \ +did not provide file of fractures to keep.\nExiting program.\n" + + sys.stderr.write(error) + sys.exit(1) + + self.create_mesh_links(self.path) + + if self.visual_mode: + print("\n--> Running in Visual Mode\n") + print( + f"Loading list of fractures to remain in network from {self.prune_file}" + ) + fracture_list = sort(genfromtxt(self.prune_file).astype(int)) + print(fracture_list) + if not self.visual_mode: + lagrit.edit_intersection_files(self.num_frac, fracture_list, + self.path) + self.num_frac = len(fracture_list) + + else: + fracture_list = range(1, self.num_frac + 1) + + # if number of fractures is greater than number of CPUS, + # only use num_poly CPUs. This change is only made here, so ncpus + # is still used in PFLOTRAN + ncpu = min(self.ncpu, self.num_frac) + + print('=' * 80) + if self.visual_mode: + print("\n--> Running in Visual Mode\n") + else: + print("\n--> Running in Full Meshing Mode\n") + print('=' * 80) + + lagrit.create_parameter_mlgi_file(fracture_list, self.h, slope=slope) + if self.visual_mode: + lagrit.create_lagrit_scripts_reduced_mesh(fracture_list) + else: + + # Check for well points well. + if well_flag: + if not os.path.isfile("well_points.dat"): + error = "ERROR!!! Well flag is set to True in DFN.mesh_network(), but file 'well_points.dat' cannot be found.\nPlease run DFN.find_well_intersection_points() for each well prior to meshing\nOr set well_flag = False\nExiting Program\n" + sys.stderr.write(error) + sys.exit(1) + + dump_poisson_params(self.h, coarse_factor, slope, min_dist, max_dist, + concurrent_samples, grid_size, well_flag) + + lagrit.create_lagrit_scripts_poisson(fracture_list) + ##### FOR SERIAL DEBUG ###### + # for f in fracture_list: + # run_mesh.mesh_fracture(f, visual_mode, len(fracture_list)) + # exit() + + print('=' * 80) + + failure = run_mesh.mesh_fractures_header(fracture_list, ncpu, + self.visual_mode, self.h) + if failure: + mh.cleanup_dir() + error = "One or more fractures failed to mesh properly.\nExiting Program\n" + sys.stderr.write(error) + sys.exit(1) + + n_jobs = lagrit.create_merge_poly_files(ncpu, self.num_frac, fracture_list, + self.h, self.visual_mode, + self.domain, self.flow_solver) + + run_mesh.merge_the_meshes(self.num_frac, ncpu, n_jobs, self.visual_mode) + + if (not self.visual_mode and not prune): + if not mh.check_dudded_points(self.dudded_points): + mh.cleanup_dir() + error = "ERROR!!! Incorrect Number of dudded points.\nExiting Program\n" + sys.stderr.write(error) + sys.exit(1) + + if production_mode: + mh.cleanup_dir() + + if not self.visual_mode: + lagrit.define_zones() + + if prune: + mh.clean_up_files_after_prune(self) + + self.gather_mesh_information() + print('=' * 80) + print("Meshing DFN using LaGriT : Complete") + print('=' * 80)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGen/meshing/mesh_dfn_helper.html b/docs/_modules/pydfnworks/dfnGen/meshing/mesh_dfn_helper.html new file mode 100644 index 00000000..509f90e5 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/meshing/mesh_dfn_helper.html @@ -0,0 +1,695 @@ + + + + + + pydfnworks.dfnGen.meshing.mesh_dfn_helper — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.meshing.mesh_dfn_helper

+"""
+.. module:: mesh_dfn_helper.py
+   :synopsis: helper functions for meshing DFN using LaGriT  
+.. moduleauthor:: Jeffrey Hyman <jhyman@lanl.gov>
+
+"""
+import os
+import sys
+import glob
+import numpy as np
+import subprocess
+import pyvtk as pv
+
+# def parse_params_file(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
+#     -------
+#         num_poly: int
+#             Number of Polygons
+#         h: float
+#             Meshing length scale h
+#         dudded_points: int
+#             Expected number of dudded points in Filter (LaGriT)
+#         visual_mode : bool
+#             If True, reduced_mesh.inp is created (not suitable for flow and transport), if False, full_mesh.inp is created
+#         domain: dict
+#              x,y,z domain sizes
+
+#     Notes
+#     -----
+#         None
+#     """
+#     if not quiet:
+#         print("\n--> Parsing  params.txt")
+#     fparams = open('params.txt', 'r')
+#     # Line 1 is the number of polygons
+#     num_poly = int(fparams.readline())
+#     #Line 2 is the h scale
+#     h = float(fparams.readline())
+#     # Line 3 is the visualization mode: '1' is True, '0' is False.
+#     visual_mode = int(fparams.readline())
+#     # line 4 dudded points
+#     dudded_points = int(fparams.readline())
+
+#     # Dict domain contains the length of the domain in x,y, and z
+#     domain = {'x': 0, 'y': 0, 'z': 0}
+#     #Line 5 is the x domain length
+#     domain['x'] = (float(fparams.readline()))
+
+#     #Line 5 is the x domain length
+#     domain['y'] = (float(fparams.readline()))
+
+#     #Line 5 is the x domain length
+#     domain['z'] = (float(fparams.readline()))
+#     fparams.close()
+
+#     if not quiet:
+#         print("--> Number of Polygons: %d" % num_poly)
+#         print("--> H_SCALE %f" % h)
+#         if visual_mode > 0:
+#             visual_mode = True
+#             print("--> Visual mode is on")
+#         else:
+#             visual_mode = False
+#             print("--> Visual mode is off")
+#         print(f"--> Expected Number of dudded points: {dudded_points}")
+#         print(f"--> X Domain Size {domain['x']} m")
+#         print(f"--> Y Domain Size {domain['y']} m")
+#         print(f"--> Z Domain Size {domain['z']} m")
+#         print("--> Parsing params.txt complete\n")
+
+#     return (num_poly, h, visual_mode, dudded_points, domain)
+
+
+def check_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")
+    with open("lagrit_logs/log_merge_all.out", "r") as fp:
+        for line in fp.readlines():
+            if 'Dudding' in line:
+                print(f'--> From LaGriT: {line}')
+                try:
+                    pts = int(line.split()[1])
+                except:
+                    pts = int(line.split()[-1])
+            if 'RMPOINT:' in line:
+                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}")
+    if diff == 0:
+        print('--> The correct number of points were removed. Onward!\n')
+        return True
+    elif diff > 0:
+        ## compare with total number poins
+        print(
+            '--> WARNING!!! Number of points removed does not match the expected value'
+        )
+        diff_ratio = 100 * (float(diff) / float(total_points))
+        if diff_ratio < 0.01 and hard == False:
+            print(f"--> However value is small: {diff}")
+            print("--> Proceeding\n")
+            return True
+        else:
+            print('ERROR! Incorrect Number of points removed')
+            print(f"Over 0.01% of nodes removed. Value is {diff_ratio:.2f}")
+            return False
+
+
+def cleanup_dir():
+    """ Removes meshing files
+
+    Parameters
+    ----------
+        None
+
+    Returns
+    -------
+        None
+
+    Notes
+    -----
+    Only runs if production_mode is True
+    """
+
+    files_to_remove = [
+        'part*', 'log_merge*', 'merge*', 'mesh_poly_CPU*', 'mesh*inp',
+        'mesh*lg'
+    ]
+    for name in files_to_remove:
+        for fl in glob.glob(name):
+            os.remove(fl)
+
+
+def gather_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 
+"""
+
+    if self.visual_mode:
+        with open('reduced_mesh.inp', 'r') as finp:
+            header = finp.readline()
+            header = header.split()
+            self.num_nodes = int(header[0])
+        print(
+            f"--> The reduced mesh in full_mesh.inp has {self.num_nodes} nodes and {int(header[1])} triangular elements"
+        )
+    else:
+        with open('full_mesh.inp', 'r') as finp:
+            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)
+
+
+def clean_up_files_after_prune(self):
+    ''' After pruning a DFN to only include the fractures in prune_file this function removes references to those fractures from params.txt, perm.dat, aperature.dat, and poly_info.dat 
+    
+    Parameters
+    ----------
+        self : DFN object
+         
+    Returns
+    -------
+        None
+
+    Notes
+    -----
+        This function should always be run after pruning if flow solution is going to be run. 
+ 
+    '''
+
+    print("--> Editing DFN file based on fractures in %s" % self.prune_file)
+    keep_list = np.sort(np.genfromtxt(self.prune_file).astype(int))
+    num_frac = len(keep_list)
+
+    print("--> Editing params.txt file")
+    fin = open(self.path + '/params.txt')
+    try:
+        os.unlink('params.txt')
+    except:
+        pass
+    fout = open('params.txt', 'w')
+    line = fin.readline()
+    fout.write('%d\n' % num_frac)
+    for i in range(7):
+        line = fin.readline()
+        fout.write(line)
+    fin.close()
+    fout.close()
+    print("--> Complete")
+
+    print("--> Editing poly_info.dat file")
+    poly_info = self.poly_info[
+        keep_list -
+        1, :]  #np.genfromtxt(self.path + 'poly_info.dat')[keep_list - 1, :]
+    try:
+        os.unlink('poly_info.dat')
+    except:
+        pass
+
+    with open('poly_info.dat', 'w') as fp:
+        for i in range(num_frac):
+            fp.write('%d %d %f %f %f %d %f %f %d\n' %
+                    (i + 1, poly_info[i, 1], poly_info[i, 2], poly_info[i, 3],
+                    poly_info[i, 4], poly_info[i, 5], poly_info[i, 6],
+                    poly_info[i, 7], poly_info[i, 8]))
+    self.poly_info = poly_info
+
+    print("--> Complete")
+
+    # print("--> Editing perm.dat file")
+    # perm = self.perm  #np.genfromtxt(self.path + 'perm.dat', skip_header=1)[keep_list - 1, -1]
+    # f = open('perm.dat', 'w+')
+    # f.write('permeability\n')
+    # for i in range(num_frac):
+    #     f.write('-%d 0 0 %e %e %e\n' % (7 + i, perm[i], perm[i], perm[i]))
+    # f.close()
+    # print("--> Complete")
+
+    # print("--> Editing aperture.dat file")
+    # aperture = self.aperture  #np.genfromtxt(self.path + 'aperture.dat', skip_header=1)[keep_list - 1, -1]
+    # f = open('aperture.dat', 'w+')
+    # f.write('aperture\n')
+    # for i in range(num_frac):
+    #     f.write('-%d 0 0 %e \n' % (7 + i, aperture[i]))
+    # f.close()
+    # print("--> Complete")
+
+    print("--> Editing radii_Final.dat file")
+    fin = open(self.path + 'dfnGen_output/radii_Final.dat')
+    fout = open('dfnGen_output/radii_Final.dat', 'w')
+    # copy header
+    line = fin.readline()
+    fout.write(line)
+    line = fin.readline()
+    fout.write(line)
+    fin.close()
+    # write radii from remaining fractures
+    radii = self.radii[
+        keep_list -
+        1, :]  #np.genfromtxt(self.path + 'radii_Final.dat', skip_header=2)[keep_list - 1, :]
+    for i in range(num_frac):
+        fout.write('%f %f %d\n' % (radii[i, 0], radii[i, 1], radii[i, 2]))
+    fout.close()
+    print("--> Complete")
+
+    print("--> Editing normal_vectors.dat file")
+    fin = open(self.path + 'dfnGen_output/normal_vectors.dat')
+    fout = open('dfnGen_output/normal_vectors.dat', 'w')
+    # copy header
+    normal_vect = self.normal_vectors[
+        keep_list -
+        1, :]  #np.genfromtxt(self.path + 'normal_vectors.dat')[keep_list - 1, :]
+    for i in range(num_frac):
+        fout.write('%f %f %f\n' %
+                   (normal_vect[i, 0], normal_vect[i, 1], normal_vect[i, 2]))
+    fout.close()
+    print("--> Complete")
+
+    print("--> Editing translations.dat file")
+    fin = open(self.path + 'dfnGen_output/translations.dat')
+    fout = open('dfnGen_output/translations.dat', 'w')
+    # copy header
+    line = fin.readline()
+    fout.write(line)
+    points = []
+    for line in fin.readlines():
+        tmp = line.split(' ')
+        if tmp[-1] != 'R':
+            points.append((float(tmp[0]), float(tmp[1]), float(tmp[2])))
+    points = np.asarray(points)
+    points = points[keep_list - 1, :]
+    for i in range(num_frac):
+        fout.write('%f %f %f\n' % (points[i, 0], points[i, 1], points[i, 2]))
+    fout.close()
+
+    print("--> Complete")
+
+    print("--> Editing translations.dat file")
+    with open(self.path + 'dfnGen_output/translations.dat', 'r') as fin:
+        with open('dfnGen_output/translations.dat', 'w') as fout:
+            # copy header
+            line = fin.readline()
+            fout.write(line)
+            points = []
+            for line in fin.readlines():
+                tmp = line.split(' ')
+                if tmp[-1] != 'R':
+                    points.append((float(tmp[0]), float(tmp[1]), float(tmp[2])))
+            points = np.asarray(points)
+            points = points[keep_list - 1, :]
+            for i in range(num_frac):
+                fout.write('%f %f %f\n' % (points[i, 0], points[i, 1], points[i, 2]))
+
+
+    fout = open('dfnGen_output/surface_area_Final.dat', 'w')
+    fout.write('Fracture Surface Area After Isolated Fracture and Cluster Removal')
+    # copy header
+    surface_area = self.surface_area[
+        keep_list -
+        1] 
+    for i in range(num_frac):
+        fout.write(f'{surface_area[i]}\n')
+    fout.close()
+    print("--> Complete")
+
+    print("--> Editing polygons.dat file")
+    with open(self.path + 'dfnGen_output/polygons.dat', 'r') as fin:
+        header = fin.readline()
+        data = fin.read().strip()
+        with open('dfnGen_output/polygons.dat', 'w') as fout:
+            # new header
+            fout.write(f'nPolygons: {self.num_frac}')
+            for fracture, line in enumerate(data.split('\n')):
+                if fracture - 1 in keep_list:
+                    fout.write(line + "\n")
+
+    self.families = self.families[keep_list - 1]
+    self.perm = self.perm[keep_list - 1]
+    self.aperture = self.aperture[keep_list - 1]
+
+    print("--> Editing Fracture Files Complete")
+
+
+
+
+
+
+
+[docs] +def inp2gmv(self, inp_file=''): + """ 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 + --------- + """ + + if inp_file: + self.inp_file = inp_file + else: + inp_file = self.inp_file + + if inp_file == '': + error = 'ERROR: inp file must be specified in inp2gmv!\n' + sys.stderr.write(error) + sys.exit(1) + + gmv_file = inp_file[:-4] + '.gmv' + + with open('inp2gmv.lgi', 'w') as fid: + 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') + + if failure: + error = 'ERROR: Failed to run LaGrit to get gmv from inp file!\n' + sys.stderr.write(error) + sys.exit(1) + print("--> Finished writing gmv format from avs format")
+ + + +
+[docs] +def inp2vtk_python(self): + """ Using Python VTK library, convert inp file to VTK file. + + Parameters + ---------- + self : object + DFN Class + + Returns + -------- + None + + Notes + -------- + For a mesh base.inp, this dumps a VTK file named base.vtk + """ + + if self.flow_solver != "PFLOTRAN": + error = "ERROR! Wrong flow solver requested\n" + sys.stderr.write(error) + sys.exit(1) + + print("--> Using Python to convert inp files to VTK files") + if self.inp_file: + inp_file = self.inp_file + + if inp_file == '': + error = 'ERROR: Please provide inp filename!\n' + sys.stderr.write(error) + sys.exit(1) + + if self.vtk_file: + vtk_file = self.vtk_file + else: + vtk_file = inp_file[:-4] + self.vtk_file = vtk_file + '.vtk' + + print("--> Reading inp data") + + with open(inp_file, 'r') as f: + line = f.readline() + num_nodes = int(line.strip(' ').split()[0]) + num_elems = int(line.strip(' ').split()[1]) + + coord = np.zeros((num_nodes, 3), 'float') + elem_list_tri = [] + elem_list_tetra = [] + + for i in range(num_nodes): + line = f.readline() + coord[i, 0] = float(line.strip(' ').split()[1]) + coord[i, 1] = float(line.strip(' ').split()[2]) + coord[i, 2] = float(line.strip(' ').split()[3]) + + for i in range(num_elems): + line = f.readline().strip(' ').split() + line.pop(0) + line.pop(0) + elem_type = line.pop(0) + if elem_type == 'tri': + elem_list_tri.append([int(i) - 1 for i in line]) + if elem_type == 'tet': + elem_list_tetra.append([int(i) - 1 for i in line]) + + print('--> Writing inp data to vtk format') + vtk = pv.VtkData( + pv.UnstructuredGrid(coord, + tetra=elem_list_tetra, + triangle=elem_list_tri), + 'Unstructured pflotran grid') + + vtk.tofile(vtk_file)
+ + + +
+[docs] +def run_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. + + """ + if output_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" + if not quiet: + print(f"--> Running: {cmd}") + failure = subprocess.call(cmd, shell=True) + if failure: + error = f"ERROR running LaGriT on script {lagrit_file}. Exiting Program.\n" + sys.stderr.write(error) + sys.exit(1) + else: + print(f"--> Running LaGriT on script {lagrit_file} successful.\n") + return failure
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGen/meshing/udfm/false_connections.html b/docs/_modules/pydfnworks/dfnGen/meshing/udfm/false_connections.html new file mode 100644 index 00000000..59d6398c --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/meshing/udfm/false_connections.html @@ -0,0 +1,224 @@ + + + + + + pydfnworks.dfnGen.meshing.udfm.false_connections — dfnWorks 2.7 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>
+
+"""
+
+import pickle
+import os
+
+
+
+[docs] +def check_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"] + for f in files: + 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() + for u, v in H.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 = [key for key in fmc.keys()] + + # walk through the cells and add edges to graph H + # if two fractures are in the same cell + cell_false = [False] * len(cells) + for i, cell in enumerate(cells): + num_conn = len(fmc[cell]) + # If more than one fracture intersects the mesh cell + # add edges + if num_conn > 1: + # add edges between all fractures in a cell + for j in range(num_conn): + id1 = fmc[cell][j][0] + for k in range(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 = [] + for u, v, in H.edges(): + if not G.has_edge(u, v): + print(f"--> False connection between fractures {u} and {v}") + false_connections.append((u, v)) + + if len(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/docs/_modules/pydfnworks/dfnGen/meshing/udfm/map2continuum.html b/docs/_modules/pydfnworks/dfnGen/meshing/udfm/map2continuum.html new file mode 100644 index 00000000..c859aa7a --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/meshing/udfm/map2continuum.html @@ -0,0 +1,1137 @@ + + + + + + pydfnworks.dfnGen.meshing.udfm.map2continuum — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.meshing.udfm.map2continuum

+"""
+.. module:: map2continuum.py 
+   :synopsis: Produce octree-refined continuum mesh replacing dfn 
+.. moduleauthor:: Matthew Sweeney <msweeney2796@lanl.gov>
+
+"""
+
+import os
+import sys
+import subprocess
+import shutil
+import numpy as np
+from pydfnworks.dfnGen.meshing import mesh_dfn_helper as mh
+import time
+import multiprocessing as mp
+import pickle
+
+
+
+[docs] +def map_to_continuum(self, l, orl, path="./", dir_name="octree"): + """ This function generates an octree-refined continuum mesh using the + reduced_mesh.inp as input. To generate the reduced_mesh.inp, one must + turn visualization mode on in the DFN input card. + + Parameters + ---------- + self : object + DFN Class + l : float + Size (m) of level-0 mesh element in the continuum mesh + orl : int + Number of total refinement levels in the octree + path : string + path to primary DFN directory + dir_name : string + name of directory where the octree mesh is created + + Returns + ------- + None + + Notes + ----- + octree_dfn.inp : Mesh file + Octree-refined continuum mesh + fracX.inp : Mesh files + Octree-refined continuum meshes, which contain intersection areas + + """ + print('=' * 80) + print("Meshing Continuum Using LaGrit : Starting") + print('=' * 80) + + if type(orl) is not int or orl < 1: + error = "ERROR: orl must be positive integer. Exiting" + sys.stderr.write(error) + sys.exit(1) + + # Extent of domain + x0 = 0 - (self.domain['x'] / 2.0) + x1 = 0 + (self.domain['x'] / 2.0) + y0 = 0 - (self.domain['y'] / 2.0) + y1 = 0 + (self.domain['y'] / 2.0) + z0 = 0 - (self.domain['z'] / 2.0) + z1 = 0 + (self.domain['z'] / 2.0) + + # Number of cell elements in each direction at coarse level + nx = self.domain['x'] / l + 1 + ny = self.domain['y'] / l + 1 + nz = self.domain['z'] / l + 1 + + if nx * ny * nz > 1e8: + error = "ERROR: Number of elements > 1e8. Exiting" + sys.stderr.write(error) + sys.exit(1) + + print("\nCreating *.lgi files for octree mesh\n") + try: + os.mkdir(dir_name) + os.mkdir(dir_name + os.sep + "lagrit_scripts") + os.mkdir(dir_name + os.sep + "lagrit_logs") + except OSError: + shutil.rmtree(dir_name) + os.mkdir(dir_name) + os.mkdir(dir_name + os.sep + "lagrit_scripts") + os.mkdir(dir_name + os.sep + "lagrit_logs") + + + ## gather points on polygons + points = self.gather_points() + if self.num_frac == 1: + self.normal_vectors = np.array([self.normal_vectors]) + + lagrit_driver(dir_name, nx, ny, nz, self.num_frac, self.normal_vectors,points) + + #lagrit_driver(dir_name, nx, ny, nz, self.num_frac, self.normal_vectors, + # self.centers) + + lagrit_parameters(dir_name, orl, x0, x1, y0, y1, z0, z1, nx, ny, nz, + self.h) + lagrit_build(dir_name) + lagrit_intersect(dir_name) + lagrit_hex_to_tet(dir_name) + lagrit_remove(dir_name) + lagrit_run(self, self.num_frac, path, dir_name) + lagrit_strip(self.num_frac) + driver_parallel(self, self.num_frac) + build_dict(self, self.num_frac, delete_files=True) + dir_cleanup() + ## set object variable name + self.inp_file = "octree_dfn.inp"
+ + +def lagrit_driver(dir_name, nx, ny, nz, num_poly, normal_vectors, points): + """ This function creates the main lagrit driver script, which calls all + lagrit scripts. + + Parameters + ---------- + dir_name : string + Name of working directory + ni : int + Number of cells in each direction + num_poly : int + Number of fractures + normal_vectors : array + Array containing normal vectors of each fracture + points : array + Array containing a point on each fracture + + Returns + ------- + None + + Notes + ----- + None + + """ + xn, yn, zn, xp, yp, zp = 0, 0, 0, 0, 0, 0 + j = num_poly + floop = "" + for i in range(1, int(num_poly + 1)): + xn = normal_vectors[j - 1][0] + yn = normal_vectors[j - 1][1] + zn = normal_vectors[j - 1][2] + xp = points[j - 1][0] + yp = points[j - 1][1] + zp = points[j - 1][2] + floop = """read / avs / mohex2.inp / mohex2 + cmo / DELATT / mohex2 / if_int + read / avs / MOTET_np1.inp / MOTET_np1 + read / avs / MOTET.inp / MOTET + read / avs / reduced_mesh.inp / MODFN + cmo / create / FRACTURE{0} + cmo / copy / FRACTURE{0} / MODFN + cmo / select / FRACTURE{0} + pset / pdel / attribute imt / 1 0 0 / ne / {0} + rmpoint pset, get, pdel + rmpoint compress + resetpts itp + pset / pdel / delete + eltset / edel / itetclr / ne / {0} + rmpoint element eltset, get, edel + rmpoint compress + resetpts itp + eltset / edel / delete + + intersect_elements / mohex2 / FRACTURE{0} / if_int + eltset / erefine / if_int / ne / 0 + pset / pinter / eltset erefine + + cmo / create / temp{0} + cmo / copy / temp{0} / mohex2 + cmo / select / temp{0} + pset / pdel / if_int / 1 0 0 / eq / 0 + rmpoint, pset, get, pdel + rmpoint compress + resetpts itp + pset / pdel / delete + eltset / edel / if_int / eq / 0 + rmpoint element eltset, get, edel + rmpoint compress + resetpts itp + eltset / edel / delete + cmo / setatt / temp{0} / itetclr / 1 0 0 / {0} + cmo / setatt / temp{0} / imt / 1 0 0 / {0} + + cmo / create / TETCOPY + cmo / copy / TETCOPY / MOTET + cmo / select / TETCOPY + interpolate / map / TETCOPY / itetclr / 1 0 0 / temp{0} / itetclr + compute / distance_field / TETCOPY / temp{0} /dfield + cmo / select / TETCOPY + pset / pfrac / attribute / dfield / 1 0 0 / le / 1.e-8 + cmo / setatt / TETCOPY / imt / 1 0 0 / {1} + cmo / setatt / TETCOPY / imt / pset get pfrac / {0} + + cmo / set_id / MOTET_np1 / element / id_cell + cmo / set_id / MOTET_np1 / node / id_vertex + + extract / plane / ptnorm / & + {2} {3} {4} / & + {5} {6} {7} / & + 1 0 0 / moext / MOTET_np1 + cmo / status / brief + + createpts / median + cmo / DELATT / moext / id_cell + cmo / DELATT / moext / id_parent + dump / avs2 / ex_xyz{0}.table / moext 0 0 0 1 + + cmo / addatt / moext / volume / area_tri + cmo / DELATT / moext / xmed + cmo / DELATT / moext / ymed + cmo / DELATT / moext / zmed + dump / avs2 / ex_area{0}.table / moext 0 0 0 1 + cmo / delete / moext + + cmo / select / TETCOPY + cmo / DELATT / TETCOPY / icr1 + cmo / DELATT / TETCOPY / itp1 + cmo / DELATT / TETCOPY / isn1 + + dump / avs2 / frac{0}.inp / TETCOPY + cmo / delete / temp{0} + cmo / delete / TETCOPY + + cmo / select / mohex2 + cmo / setatt / mohex2 / itetclr / eltset get erefine / {0} + cmo / setatt / mohex2 / imt / pset get pinter / {0} + eltset / erefine / delete + pset / pinter / delete + cmo / DELATT / mohex2 / if_int + cmo / delete / FRACTURE{0} + finish + """.format(j, num_poly + 1, xp, yp, zp, xn, yn, zn) + f_name = f'{dir_name}/driver_frac{j}.lgi' + f = open(f_name, 'w') + f.write(floop) + f.flush() + f.close() + j = j - 1 + + f_name = f'{dir_name}/driver_octree_start.lgi' + f = open(f_name, 'w') + fin = ("""# +# LaGriT control files to build an octree refined hex mesh with refinement +# based on intersection of hex mesh with a DFN triangulation mesh +# +# driver_octree.lgi +# parameters_octree_dfn.mlgi +# build_octree.mlgi +# intersect_refine.mlgi +# hex_to_tet.mlgi +# remove_cells.mlgi +# +# Define some parameters +# +infile parameters_octree_dfn.mlgi +# +# Read in DFN mesh +# +read / FTYPE / FNAME / MODFN +cmo / printatt / MODFN / -xyz- / minmax +# +# Octree refined orthogonal mesh based on intersection with DFN mesh +# +infile build_octree.mlgi +# +# Identify cells in hex mesh that are intersected by DFN mesh +# +# This is the last pass through intersect_elements in order to figure out +# which cells in the fully refined hex mesh are intersected by the dfn mesh +# +intersect_elements / MOHEX / MODFN / if_int +eltset / einter / if_int / ne / 0 +pset / pinter / eltset einter +# +# Use the itetclr(cell) and imt(vertex) attribute to hold the information +# +cmo / setatt / MOHEX / itetclr / 1 0 0 / 1 +cmo / setatt / MOHEX / itetclr / eltset get einter / 2 +cmo / setatt / MOHEX / imt / 1 0 0 / 1 +cmo / setatt / MOHEX / imt / pset get pinter / 2 +# +# Output final hex mesh +# +#dump / avs2 / tmp_hex_refine.inp / MOHEX +# +# Same as above but for np1 hex mesh +# +intersect_elements / MOHEX_np1 / MODFN / if_int +eltset / einter / if_int / ne / 0 +pset / pinter / eltset einter +# +# See above +# +cmo / setatt / MOHEX_np1 / itetclr / 1 0 0 / 1 +cmo / setatt / MOHEX_np1 / itetclr / eltset get einter / 2 +cmo / setatt / MOHEX_np1 / imt / 1 0 0 / 1 +cmo / setatt / MOHEX_np1 / imt / pset get pinter / 2 +#dump / avs2 / tmp_hex_np1_refine.inp / MOHEX_np1 +# +# Convert the hex mesh to a tet mesh +# +infile hex_to_tet.mlgi +# +# Modify the hex data structure from a full octree data structure +# to one in which only the highest level of refined hex is maintained +# and all parent cells are stripped out of the data structure +# +grid2grid / tree_to_fe / mohex2 / MOHEX +#dump / avs / octree_hex_mesh.inp / MOHEX +# +cmo / delete / MOHEX +cmo / select / mohex2 +# +# Remove all but the most refined hex cells +# +loop / do / NTIMEs / 0 N_OCTREE_REFINE_M1 1 / loop_end & +infile remove_cells.mlgi +# +cmo / select / mohex2 +cmo / DELATT / mohex2 / if_int +intersect_elements / mohex2 / MODFN / if_int +cmo / select / mohex2 +eltset / edelete / if_int / eq / 0 +rmpoint / element / eltset get edelete +eltset / edelete / release +rmpoint / compress +# +dump / avs / mohex2.inp / mohex2 +dump / avs / MOTET_np1.inp / MOTET_np1 +dump / avs / MOTET.inp / MOTET +# +cmo / select / MOTET +# +# +cmo / modatt / MOTET / itp / ioflag / l +cmo / modatt / MOTET / isn / ioflag / l +cmo / modatt / MOTET / icr / ioflag / l +# + +define / ZONE / 1 +define / FOUT / pboundary_top +pset / top / attribute / zic / 1 0 0 / gt / ZMAX +pset / top / zone / FOUT / ascii / ZONE + +define / ZONE / 2 +define / FOUT / pboundary_bottom +pset / bottom / attribute / zic / 1 0 0 / lt / ZMIN +pset / bottom / zone / FOUT / ascii / ZONE + +define / ZONE / 3 +define / FOUT / pboundary_left_w +pset / left_w / attribute / xic / 1 0 0 / lt / XMIN +pset / left_w / zone / FOUT / ascii / ZONE + +define / ZONE / 4 +define / FOUT / pboundary_front_n +pset / front_n / attribute / yic / 1 0 0 / gt / YMAX +pset / front_n / zone / FOUT / ascii / ZONE + +define / ZONE / 5 +define / FOUT / pboundary_right_e +pset / right_e / attribute / xic / 1 0 0 / gt / XMAX +pset / right_e / zone / FOUT / ascii / ZONE + +define / ZONE / 6 +define / FOUT / pboundary_back_s +pset / back_s / attribute / yic / 1 0 0 / lt / YMIN +pset / back_s / zone / FOUT / ascii / ZONE + +dump / pflotran / full_mesh / MOTET / nofilter_zero +dump / avs2 / octree_dfn.inp / MOTET +dump / coord / octree_dfn / MOTET +dump / stor / octree_dfn / MOTET +dump / zone_imt / octree_dfn / MOTET +dump / zone_outside / octree_dfn / MOTET +finish +""") + f.write(fin) + f.flush() + f.close() + print("Creating driver_octree_start.lgi file: Complete\n") + + +def lagrit_parameters(dir_name, orl, x0, x1, y0, y1, z0, z1, nx, ny, nz, h): + """ This function creates the parameters_octree_dfn.mlgi lagrit script. + + Parameters + ---------- + dir_name : string + Name of working directory + orl : int + Number of total refinement levels in the octree + i0, i1 : float + Extent of domain in x, y, z directions + ni : int + Number of cells in each direction + Returns + ------- + None + + Notes + ----- + None + + """ + f_name = f'{dir_name}/parameters_octree_dfn.mlgi' + f = open(f_name, 'w') + fin = """# +# Define some parameters +# +# Input DFN mesh +# +define / FTYPE / avs +define / FNAME / reduced_mesh.inp +# +define / MOHEX / mohex +define / MOTET / motet +# +# Set AMR refinement. 123 means refine in x,y,z directions +# See LaGriT refine command for more options +# +define / REFINE_AMR / 123 +# + """ + f.write(fin) + eps = h * 10**-3 + f.write('define / N_OCTREE_REFINE / %d \n' % orl) + f.write('define / N_OCTREE_REFINE_M1 / %d \n' % (orl - 1)) + f.write('define / N_OCTREE_np1 / %d \n' % (orl + 1)) + f.write('define / N_OCTREE_np1_M1 / %d \n' % (orl)) + f.write('define / X0 / %0.12f \n' % x0) + f.write('define / X1 / %0.12f \n' % x1) + f.write('define / Y0 / %0.12f \n' % y0) + f.write('define / Y1 / %0.12f \n' % y1) + f.write('define / Z0 / %0.12f \n' % z0) + f.write('define / Z1 / %0.12f \n' % z1) + f.write('define / XMAX / %0.12f \n' % (x1 - eps)) + f.write('define / XMIN / %0.12f \n' % (x0 + eps)) + f.write('define / YMAX / %0.12f \n' % (y1 - eps)) + f.write('define / YMIN / %0.12f \n' % (y0 + eps)) + f.write('define / ZMAX / %0.12f \n' % (z1 - eps)) + f.write('define / ZMIN / %0.12f \n' % (z0 + eps)) + f.write('define / NX / %d \n' % nx) + f.write('define / NY / %d \n' % ny) + f.write('define / NZ / %d \n' % nz) + f.write('finish\n') + f.flush() + f.close() + print("Creating parameters_octree_dfn.mlgi file: Complete\n") + + +def lagrit_build(dir_name): + """ This function creates the build_octree.mlgi lagrit script. + + Parameters + ---------- + dir_name : string + name of working directory + + Returns + ------- + None + + Notes + ----- + None + + """ + f_name = f'{dir_name}/build_octree.mlgi' + f = open(f_name, 'w') + fin = """cmo / create / MOHEX / / / hex +createpts / brick / xyz / NX NY NZ / X0 Y0 Z0 / X1 Y1 Z1 / 1 1 1 +cmo / setatt / MOHEX / imt / 1 0 0 / 1 +cmo / setatt / MOHEX / itetclr / 1 0 0 / 1 +resetpts / itp +# +# Print to screen and logfiles the extents of the hex mesh and dfn mesh +# +cmo / printatt / MOHEX / -xyz- / minmax +cmo / select / MODFN +cmo / printatt / MODFN / -xyz- / minmax +# +# Generate copy of hex mesh for upscaling +# +cmo / copy / MOHEX_np1 / MOHEX +# +# Loop through steps to intersect dfn mesh (MODFN) with hex mesh (MOHEX) +# and refine hex mesh based on cell intersections. Loop through +# N_OCTREE_REFINE times +# +loop / do / NTIMEs / 1 N_OCTREE_REFINE 1 / loop_end & +infile intersect_refine.mlgi +# +# See above - except do it once additional time ("np1") +# +loop / do / NTIMEs / 1 N_OCTREE_np1 1 / loop_end & +infile intersect_refine_np1.mlgi +# +finish + """ + f.write(fin) + f.flush() + f.close() + print("Creating build_octree.mlgi file: Complete\n") + + +def lagrit_intersect(dir_name): + """ This function creates the intersect_refine.mlgi lagrit scripts. + + Parameters + ---------- + dir_name : string + name of working directory + + Returns + ------- + None + + Notes + ----- + None + + """ + f_name = f'{dir_name}/intersect_refine.mlgi' + f = open(f_name, 'w') + fin = """# +# Compute mesh to mesh intersection and refine hex mesh +# +intersect_elements / MOHEX / MODFN / if_int +eltset / erefine / if_int / ne / 0 +pset / prefine / eltset erefine +# +# If one wants anisotropic mesh refinement, then the +# variable REFINE_AMR can be set in parameters_octree_dfn.mlgi +# +refine/constant/imt1/linear/element/pset,get,prefine/ & + -1.,0.,0./exclusive/amr REFINE_AMR +# +# Clean up eltset, pset, and if_int attribute +# +eltset / erefine / delete +pset / prefine / delete +cmo / DELATT / MOHEX / if_int +# +# Print out diagnostics +# +quality +cmo / status / brief +# +finish + """ + f.write(fin) + f.flush() + f.close() + print("Creating intersect_refine.mlgi file: Complete\n") + f_name = f'{dir_name}/intersect_refine_np1.mlgi' + f = open(f_name, 'w') + fin = """# +# For comments see intersect_refine.mlgi +# +intersect_elements / MOHEX_np1 / MODFN / if_int +eltset / erefine / if_int / ne / 0 +pset / prefine / eltset erefine +refine/constant/imt1/linear/element/pset,get,prefine/ & +-1.,0.,0./exclusive/amr REFINE_AMR +# +eltset / erefine / delete +pset / prefine / delete +cmo / DELATT / MOHEX_np1 / if_int +# +quality +cmo / status / brief +# +finish + """ + f.write(fin) + f.flush() + f.close() + print("Creating intersect_refine_np1.mlgi file: Complete\n") + + +def lagrit_hex_to_tet(dir_name): + """ This function creates the hex_to_tet.mlgi lagrit script. + + Parameters + ---------- + dir_name : string + name of working directory + + Returns + ------- + None + + Notes + ----- + None + + """ + f_name = f'{dir_name}/hex_to_tet.mlgi' + f = open(f_name, 'w') + fin = """# +# Convert the octree hex mesh to tet mesh by connecting the +# octree vertex collection to a Delaunay mesh +# +cmo / create / MOTET +copypts / MOTET / MOHEX +cmo / select / MOTET +filter 1 0 0 +rmpoint / compress +cmo / setatt / MOTET / imt / 1 0 0 / 1 +cmo / setatt / MOTET / itp / 1 0 0 / 0 +# +# Sort and reorder the nodes based on coordinates +# +sort / MOTET / index / ascending / ikey / xic yic zic +reorder / MOTET / ikey +cmo / DELATT / MOTET / ikey +# +# Connect +# +connect / noadd +cmo / setatt / MOTET / itetclr / 1 0 0 / 1 +resetpts / itp +# +# Do the same for np1 mesh +# +cmo / create / MOTET_np1 +copypts / MOTET_np1 / MOHEX_np1 +cmo / select / MOTET_np1 +filter 1 0 0 +rmpoint / compress +cmo / setatt / MOTET_np1 / imt / 1 0 0 / 1 +cmo / setatt / MOTET_np1 / itp / 1 0 0 / 0 +# +# See above +# +sort / MOTET_np1 / index / ascending / ikey / xic yic zic +reorder / MOTET_np1 / ikey +cmo / DELATT / MOTET_np1 / ikey +# +connect / noadd +cmo / setatt / MOTET_np1 / itetclr / 1 0 0 / 1 +resetpts / itp +# +finish + """ + f.write(fin) + f.flush() + f.close() + print("Creating hex_to_tet.mlgi file: Complete\n") + + +def lagrit_remove(dir_name): + """ This function creates the remove_cells.mlgi lagrit script. + + Parameters + ---------- + dir_name : string + name of working directory + + Returns + ------- + None + + Notes + ----- + None + + """ + f_name = f'{dir_name}/remove_cells.mlgi' + f = open(f_name, 'w') + fin = """# +# Remove cells from hex mesh based on the level of refinement +# itetlev is the refinement level. Original mesh itetlev=0 +# +eltset / edelete / itetlev / eq / NTIMEs +rmpoint / element / eltset get edelete +eltset / edelete / release +rmpoint / compress +# +finish + """ + f.write(fin) + f.flush() + f.close() + print("Creating remove_cells.mlgi file: Complete\n") + + +def lagrit_run(self, num_poly, path, dir_name): + """ This function executes the lagrit scripts. + + Parameters + ---------- + path : string + path to primary DFN directory + dir_name : string + name of directory where the octree mesh is created + + Returns + ------- + None + + Notes + ----- + None + + """ + # Run LaGriT + os.chdir(dir_name) + + if os.path.isfile(path + "reduced_mesh.inp"): + os.symlink(path + "reduced_mesh.inp", "reduced_mesh.inp") + elif os.path.isfile(path + "../" + "reduced_mesh.inp"): + os.symlink(path + "../" + "reduced_mesh.inp", "reduced_mesh.inp") + else: + error = "ERROR!!! Reduced Mesh not found. Please run mesh_dfn with visual_mode=True.\nExiting" + sys.stderr.write(error) + sys.exit(1) + + mh.run_lagrit_script("driver_octree_start.lgi") + + driver_interpolate_parallel(self, num_poly) + + +def lagrit_strip(num_poly): + """ This function strips and replaces the headers of the files, which is + needed to assign the fracture areas to a mesh object. + + Parameters + ---------- + num_poly : int + Number of fractures + + Returns + ------- + None + + Notes + ----- + None + + """ + node_dict = {} + for i in range(1, num_poly + 1): + with open('ex_xyz{0}.table'.format(i), 'r') as infile: + for k, l in enumerate(infile): + pass + node_dict.setdefault(i, []).append(k - 4) + + +# print(node_dict) + + for i in range(1, num_poly + 1): + with open('ex_xyz{0}.table'.format(i), 'r') as infile: + with open('ex_xyz{0}_2.inp'.format(i), 'w') as outfile: + for j, line in enumerate(infile): + if j == 0: + outfile.write("{0} 0 0 0 0\n".format(node_dict[i][0])) + elif j > 4: + outfile.write(line) + outfile.close() + infile.close() + os.remove(f"ex_xyz{i}.table") + with open('ex_area{0}.table'.format(i), 'r') as infile: + with open('ex_area{0}_2.table'.format(i), 'w') as outfile: + for j, line in enumerate(infile): + if j > 2: + outfile.write(line.split()[1]) + outfile.write("\n") + outfile.close() + infile.close() + os.remove(f"ex_area{i}.table") + + +def driver_interpolate_parallel(self, num_poly): + """ This function drives the parallelization of the area sums upscaling. + + Parameters + ---------- + self : object + DFN Class + num_poly : int + Number of fractures + + Returns + ------- + None + + Notes + ----- + None + + """ + frac_index = range(1, int(num_poly + 1)) + number_of_task = len(frac_index) + number_of_processes = self.ncpu + tasks_to_accomplish = mp.Queue() + tasks_that_are_done = mp.Queue() + processes = [] + + for i in range(number_of_task): + tasks_to_accomplish.put(i + 1) + + # Creating processes + for w in range(number_of_processes): + p = mp.Process(target=worker_interpolate, + args=(tasks_to_accomplish, tasks_that_are_done)) + processes.append(p) + p.start() + tasks_to_accomplish.put('STOP') + + for p in processes: + p.join() + + while not tasks_that_are_done.empty(): + print(tasks_that_are_done.get()) + + return True + + +def driver_parallel(self, num_poly): + """ This function drives the parallelization of the area sums upscaling. + + Parameters + ---------- + self : object + DFN Class + num_poly : int + Number of fractures + + Returns + ------- + None + + Notes + ----- + None + + """ + frac_index = range(1, int(num_poly + 1)) + number_of_task = len(frac_index) + number_of_processes = self.ncpu + tasks_to_accomplish = mp.Queue() + tasks_that_are_done = mp.Queue() + processes = [] + + for i in range(number_of_task): + tasks_to_accomplish.put(i + 1) + + # Creating processes + for w in range(number_of_processes): + p = mp.Process(target=worker, + args=(tasks_to_accomplish, tasks_that_are_done)) + processes.append(p) + p.start() + tasks_to_accomplish.put('STOP') + + for p in processes: + p.join() + + while not tasks_that_are_done.empty(): + print(tasks_that_are_done.get()) + + return True + + +def upscale_parallel(f_id): + """ Generates lagrit script that makes mesh files with area sums. + + Parameters + ---------- + f_id : int + Fracture index + + Returns + ------- + None + + Notes + ----- + None + + """ + fname = 'driver{0}.lgi'.format(f_id) + fin = "" + f = open(fname, 'w') + fin += """read / avs / ex_xyz{0}_2.inp / mo_vertex + cmo / addatt / mo_vertex / area_tri / vdouble / scalar / nnodes + cmo / readatt / mo_vertex / area_tri / 1 0 0 / ex_area{0}_2.table + + read / avs / frac{0}.inp / frac + cmo / addatt / frac / area_sum / vdouble / scalar / nnodes + + upscale / sum / frac, area_sum / 1 0 0 / mo_vertex, area_tri + + cmo / DELATT / frac / itp1 + cmo / DELATT / frac / icr1 + cmo / DELATT / frac / isn1 + cmo / DELATT / frac / dfield + + dump / avs / area_sum{0}.table / frac / 0 0 2 0 + + cmo / delete / mo_vertex + cmo / delete / frac + """.format(f_id) + fin += "finish" + #print(fin) + f.write(fin) + f.flush() + f.close() + + mh.run_lagrit_script( + f"driver{f_id}.lgi", + f"lagrit_logs/driver{f_id}", + ) + # Delete files + os.remove(f"ex_xyz{f_id}_2.inp") + os.remove(f"ex_area{f_id}_2.table") + os.remove(f"frac{f_id}.inp") + shutil.copy(f"driver{f_id}.lgi", "lagrit_scripts") + os.remove(f"driver{f_id}.lgi") + + +def worker(tasks_to_accomplish, tasks_that_are_done): + """ Worker function for python parallel. See multiprocessing module + documentation for details. + + Parameters + ---------- + tasks_to_accomplish : ? + Processes still in queue + tasks_that_are_done : ? + Processes complete + + Notes + ----- + None + + """ + try: + for f_id in iter(tasks_to_accomplish.get, 'STOP'): + upscale_parallel(f_id) + except: + pass + return True + + +def worker_interpolate(tasks_to_accomplish, tasks_that_are_done): + """ Worker function for python parallel. See multiprocessing module + documentation for details. + + Parameters + ---------- + tasks_to_accomplish : ? + Processes still in queue + tasks_that_are_done : ? + Processes complete + + Notes + ----- + None + + """ + try: + for f_id in iter(tasks_to_accomplish.get, 'STOP'): + interpolate_parallel(f_id) + except: + pass + return True + + +def interpolate_parallel(f_id): + mh.run_lagrit_script(f"driver_frac{f_id}.lgi", + f"lagrit_logs/driver_frac{f_id}") + shutil.copy(f"driver_frac{f_id}.lgi", "lagrit_scripts") + os.remove(f"driver_frac{f_id}.lgi") + + +def build_dict(self, num_poly, delete_files): + f_dict = {} + for i in range(1, num_poly + 1): + imts = np.genfromtxt(f"area_sum{i}.table", skip_header=4)[:, 0] + area_sums = np.genfromtxt(f"area_sum{i}.table", skip_header=4)[:, 1] + for j in range(len(imts)): + if int(float(imts[j])) != (num_poly + 1) and float( + area_sums[j]) > 0: + f_dict.setdefault(j + 1, []).append((i, float(area_sums[j]))) + if delete_files: + os.remove(f"area_sum{i}.table") + p_out = open("connections.p", "wb") + pickle.dump(f_dict, p_out, pickle.HIGHEST_PROTOCOL) + p_out.close() + + +def dir_cleanup(): + os.rename("build_octree.mlgi", "lagrit_scripts/build_octree.mlgi") + os.rename("driver_octree_start.lgi", + "lagrit_scripts/driver_octree_start.lgi") + os.rename("driver_octree_start.lgi.log", + "lagrit_logs/driver_octree_start.lgi.log") + os.rename("driver_octree_start.lgi.out", + "lagrit_logs/driver_octree_start.lgi.out") + os.rename("hex_to_tet.mlgi", "lagrit_scripts/hex_to_tet.mlgi") + os.rename("parameters_octree_dfn.mlgi", + "lagrit_scripts/parameters_octree_dfn.mlgi") + os.rename("remove_cells.mlgi", "lagrit_scripts/remove_cells.mlgi") + os.rename("intersect_refine.mlgi", "lagrit_scripts/intersect_refine.mlgi") + os.rename("intersect_refine_np1.mlgi", + "lagrit_scripts/intersect_refine_np1.mlgi") + os.remove("mohex2.inp") + os.remove("MOTET.inp") + os.remove("MOTET_np1.inp") +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGen/meshing/udfm/upscale.html b/docs/_modules/pydfnworks/dfnGen/meshing/udfm/upscale.html new file mode 100644 index 00000000..82da41e3 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/meshing/udfm/upscale.html @@ -0,0 +1,393 @@ + + + + + + pydfnworks.dfnGen.meshing.udfm.upscale — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.meshing.udfm.upscale

+"""
+.. module:: upscale.py
+   :synopsis: Generates PFLOTRAN or FEHM input from octree refined continuum mesh
+.. moduleauthor:: Matthew Sweeney <msweeney2796@lanl.gov>
+
+"""
+
+import os
+import numpy as np
+import sys
+import subprocess
+import shutil
+import h5py
+import time
+import math as m
+import glob
+import pickle
+
+
+
+[docs] +def upscale(self, mat_perm, mat_por, path='../'): + """ Generate permeabilities and porosities based on output of map2continuum. + + Parameters + ---------- + self : object + DFN Class + mat_perm : float + Matrix permeability (in m^2) + mat_por: float + Matrix porosity + + Returns + ------- + perm_fehm.dat : text file + Contains permeability data for FEHM input + rock_fehm.dat : text file + Contains rock properties data for FEHM input + mesh_permeability.h5 : h5 file + Contains permeabilites at each node for PFLOTRAN input + mesh_porosity.h5 : h5 file + Contains porosities at each node for PFLOTRAN input + + Notes + ----- + None + + """ + + print('=' * 80) + print("Generating permeability and porosity for octree mesh: Starting") + print('=' * 80) + + # Check values of porosity and permeability + if mat_por < 0 or mat_por > 1: + error = "Matrix porosity must be between 0 and 1. Exiting\n" + sys.stderr.write(error) + sys.exit(1) + + if self.flow_solver == "FEHM": + with open("perm_fehm.dat", "w") as f: + f.write("perm\n") + with open("rock_fehm.dat", "w") as g: + g.write("rock\n") + + # Bring in f_dict dictionary + f_dict = pickle.load(open("connections.p", "rb")) + + with open('full_mesh.uge') as f: + num_nodes = int(f.readline().strip().split()[1]) + cv_vol = np.zeros(num_nodes, 'float') + iarray = np.zeros(num_nodes, '=i4') + for i in range(num_nodes): + fline = f.readline().strip().split() + cv_vol[i] = float(fline[4]) + iarray[i] = int(fline[0]) + f.close() + + perm_var = np.zeros(num_nodes, 'float') + por_var = np.zeros(num_nodes, 'float') + #cv_vol = np.zeros(num_nodes, 'float') + #iarray = np.zeros(num_nodes, '=i4') + frac_vol = np.zeros(num_nodes, 'float') + permX = np.zeros(num_nodes, 'float') + permY = np.zeros(num_nodes, 'float') + permZ = np.zeros(num_nodes, 'float') + + # Populate permeability and porosity arrays here + for i in range(1, num_nodes + 1): + if i in f_dict: + # Get porosity: + for j in range(len(f_dict[i])): + # Calculate total volume of fractures in cv cell i + frac_vol[i - 1] += self.aperture[f_dict[i][j][0] - + 1] * f_dict[i][j][1] + por_var[i - 1] = frac_vol[i - 1] / cv_vol[i - 1] + if por_var[i - 1] == 0: + por_var[i - 1] = mat_por + if por_var[i - 1] > 1.0: + por_var[i - 1] = 1.0 + + # Get permeability: + perm_tensor = np.zeros([3, 3]) + phi_sum = 0 + for j in range(len(f_dict[i])): + phi = (self.aperture[f_dict[i][j][0] - 1] * + f_dict[i][j][1]) / cv_vol[i - 1] + if phi > 1.0: + phi = 1.0 + phi_sum += phi + if phi_sum > 1.0: + phi_sum = 1.0 + b = self.aperture[f_dict[i][j][0] - 1] + # Construct tensor Omega + Omega = np.zeros([3, 3]) + n1 = self.normal_vectors[f_dict[i][j][0] - 1][0] + n2 = self.normal_vectors[f_dict[i][j][0] - 1][1] + n3 = self.normal_vectors[f_dict[i][j][0] - 1][2] + Omega[0][0] = (n2)**2 + (n3)**2 + Omega[0][1] = -n1 * n2 + Omega[0][2] = -n3 * n1 + Omega[1][0] = -n1 * n2 + Omega[1][1] = (n3)**2 + (n1)**2 + Omega[1][2] = -n2 * n3 + Omega[2][0] = -n3 * n1 + Omega[2][1] = -n2 * n3 + Omega[2][2] = (n1)**2 + (n2)**2 + perm_tensor += (phi * (b)**2 * Omega) + perm_tensor *= 1. / 12 + + # Calculate eigenvalues + permX[i - 1] = np.linalg.eigvals(perm_tensor)[0] + permY[i - 1] = np.linalg.eigvals(perm_tensor)[1] + permZ[i - 1] = np.linalg.eigvals(perm_tensor)[2] + + # Arithmetic average of matrix perm + permX[i - 1] += (1 - phi_sum) * mat_perm + permY[i - 1] += (1 - phi_sum) * mat_perm + permZ[i - 1] += (1 - phi_sum) * mat_perm + + # Correction factor + + # Actual value doesn't matter here, just needs to be high + min_n1 = 1e6 + min_n2 = 1e6 + min_n3 = 1e6 + + # See Sweeney et al. 2019 Computational Geoscience + for j in range(len(f_dict[i])): + n1_temp = self.normal_vectors[f_dict[i][j][0] - 1][0] + theta1_t = m.degrees(m.acos(n1_temp)) % 90 + if abs(theta1_t - 45) <= min_n1: + theta1 = theta1_t + min_n1 = theta1_t + n2_temp = self.normal_vectors[f_dict[i][j][0] - 1][1] + theta2_t = m.degrees(m.acos(n2_temp)) % 90 + if abs(theta2_t - 45) <= min_n2: + theta2 = theta2_t + min_n2 = theta2_t + n3_temp = self.normal_vectors[f_dict[i][j][0] - 1][2] + theta3_t = m.degrees(m.acos(n3_temp)) % 90 + if abs(theta3_t - 45) <= min_n3: + theta3 = theta3_t + min_n3 = theta3_t + + sl = (2 * 2**(1. / 2) - 1) / -45.0 + b = 2 * 2**(1. / 2) + + cf_x = sl * abs(theta1 - 45) + b + cf_y = sl * abs(theta2 - 45) + b + cf_z = sl * abs(theta3 - 45) + b + + permX[i - 1] *= cf_x + permY[i - 1] *= cf_y + permZ[i - 1] *= cf_z + + perm_var[i - 1] = max(permX[i - 1], permY[i - 1], permZ[i - 1]) + else: + # Assign matrix properties + por_var[i - 1] = mat_por + perm_var[i - 1] = mat_perm + + # Note these aren't needed if not using anisotropic perm + permX[i - 1] = mat_perm + permY[i - 1] = mat_perm + permZ[i - 1] = mat_perm + + if self.flow_solver == "FEHM": + with open("perm_fehm.dat", "a") as f: + f.write( + str(i) + " " + str(i) + " " + "1" + " " + + str(perm_var[i - 1]) + " " + str(perm_var[i - 1]) + " " + + str(perm_var[i - 1]) + "\n") + with open("rock_fehm.dat", "a") as g: + g.write( + str(i) + " " + str(i) + " " + "1" + " " + "2165." + " " + + "931." + " " + str(por_var[i - 1]) + "\n") + + # Need an extra space at end for FEHM + if self.flow_solver == "FEHM": + with open("perm_fehm.dat", "a") as f: + f.write("\n") + with open("rock_fehm.dat", "a") as g: + g.write("\n") + + if self.flow_solver == "PFLOTRAN": + perm_filename = 'mesh_permeability.h5' + poros_filename = 'mesh_porosity.h5' + + h5file = h5py.File(perm_filename, mode='w') + dataset_name = 'Cell Ids' + h5dset = h5file.create_dataset(dataset_name, data=iarray) + + dataset_name = 'Permeability' + h5dset = h5file.create_dataset(dataset_name, data=perm_var) + + #dataset_name = 'Perm_X' + #h5dset = h5file.create_dataset(dataset_name, data = permX) + + #dataset_name = 'Perm_Y' + #h5set = h5file.create_dataset(dataset_name, data = permY) + + #dataset_name = 'Perm_Z' + #h5set = h5file.create_dataset(dataset_name, data = permZ) + h5file.close() + + h5file = h5py.File(poros_filename, mode='w') + dataset_name = 'Cell Ids' + h5dset = h5file.create_dataset(dataset_name, data=iarray) + + dataset_name = 'Porosity' + h5dset = h5file.create_dataset(dataset_name, data=por_var) + h5file.close() + + + #upscale_cleanup() + + # What nodes are fractures vs. matrix + tag = perm_var > mat_perm + tag = tag.astype("uint8") + # Add 1 since PFLOTRAN doesn't like mat id = 0 + tag += 1 + np.savetxt("tag_frac.dat", tag, '%d', delimiter=",") + if self.flow_solver == "PFLOTRAN": + # Save as h5 + h5file = h5py.File("materials.h5", mode="w") + dataset_name = 'Materials/Cell Ids' + h5dset = h5file.create_dataset(dataset_name, data=iarray) + + dataset_name = 'Materials/Material Ids' + h5dset = h5file.create_dataset(dataset_name, data=tag) + h5file.close() + + + if self.flow_solver == "PFLOTRAN": + self.uge_file = "full_mesh.uge" + elif self.flow_solver == "FEHM": + self.uge_file = "full_mesh.stor" + + print('=' * 80) + print("Generating permeability and porosity for octree mesh: Finished") + print('=' * 80)
+ + + +#def upscale_cleanup(): +# files_to_remove = [ +# "area*", "build*", "driver*", "ex*", "frac*", "hex*", "intersect*", +# "log*", "out*", "parame*", "remove*", "time*", "tmp*" +# ] +# for name in files_to_remove: +# for fl in glob.glob(name): +# os.remove(fl) +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGen/well_package/wells.html b/docs/_modules/pydfnworks/dfnGen/well_package/wells.html new file mode 100644 index 00000000..04cbb11e --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGen/well_package/wells.html @@ -0,0 +1,1367 @@ + + + + + + pydfnworks.dfnGen.well_package.wells — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGen.well_package.wells

+import os
+import numpy as np
+import shutil
+
+from pydfnworks.dfnGen.meshing import mesh_dfn_helper as mh
+
+
+[docs] +def tag_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 + + """ + + if type(wells) is dict: + 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 + if not os.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 + if self.flow_solver == "PFLOTRAN": + self.zone2ex(zone_file=f"well_{well['name']}.zone", face='well') + + if self.flow_solver == "FEHM": + print(f"--> Well nodes are in well_{well['name']}.zone") + + print(f"--> Well creation for {well['name']} complete\n\n") + + if type(wells) is list: + for well in 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 + if not os.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 + if self.flow_solver == "PFLOTRAN": + self.zone2ex(zone_file=f"well_{well['name']}.zone", + face='well') + + if self.flow_solver == "FEHM": + print(f"--> Well nodes are in well_{well['name']}.zone") + + print(f"--> Well creation for {well['name']} complete\n\n")
+ + + +def convert_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 + + for i in range(1, n): + distance = np.linalg.norm(pts[i, :] - pts[i - 1, :]) + if distance < 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 + for j in range(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) + del interp + 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") + for i in range(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" + ) + for i in range(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") + + +def expand_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 + with open(f"expand_well_{well['name']}.lgi", "w") as fp: + 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.") + + +def get_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 + with open(f"get_well_{well['name']}_zone.lgi", "w") as fp: + 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) + + with open(f"well_{well['name']}.zone", "r") as fp: + number_of_nodes = int(fp.readlines()[3]) + if number_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] +def find_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") + if not os.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 + if type(wells) is dict: + self.run_find_well_intersection_points(wells) + # using a list of wells, loop over them. + elif type(wells) is list: + for well in wells: + self.run_find_well_intersection_points(well) + + # Run cross check + cross_check_pts(self.h)
+ + + +def run_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 + if not os.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) + + +def find_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 + with open(f"find_well_{well['name']}_segment.lgi", "w") as fp: + 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) + + +def well_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. + if not os.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) + + if len(fracture_list) == 0: + print( + f"\n--> Warning. The well {well['name']} did not intersect the DFN!!!\n" + ) + + points = self.gather_points() + for elem in elems: # 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() + + +def cross_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 = [] + for i in range(num_pts): + fracture_number = pts[i, 0] + for j in range(i + 1, num_pts): + # Check if points are on the same fracture to reduce number of distance checks + if fracture_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. + if dist < 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") + with open("well_points.dat", "w") as fwell: + fwell.write("fracture_id x y z\n") + for i in range(num_pts): + fwell.write(f"{int(pts[i,0])} {pts[i,1]} {pts[i,2]} {pts[i,3]}\n") + print("--> Cross Checking Complete") + + +def get_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 + """ + + with open(well_line_file, "r") as fp: + header = fp.readline().split() + num_pts = int(header[0]) + num_elem = int(header[1]) + pts = [] + for i in range(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 = [] + for i in range(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 = [] + for i in range(num_elem): + if not elems[i]["frac"] in fracture_list: + fracture_list.append(elems[i]["frac"]) + + return pts, 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, :] + + +def rotation_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 + if equal_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] + + return R + + +def rotate_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] + return v + + +
+[docs] +def cleanup_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") + + if not os.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"] + + if type(wells) is dict: + well = wells + for file in files: + try: + shutil.move(file.format(well['name']), + "well_data/" + file.format(well['name'])) + except: + print("Error moving " + file.format(well['name'])) + pass + + + if type(wells) is list: + for well in wells: + for file in files: + 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] +def combine_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 + if type(wells) is dict: + os.symlink(f"well_{well['name']}.zone", "well_nodes.zone") + + if type(wells) is list: + number_of_wells = len(wells) + fall = open("well_nodes.zone", "w") + for index, well in enumerate(wells): + if index == 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) + if index > 0 and index < 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) + if index == 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/docs/_modules/pydfnworks/dfnGraph/dfn2graph.html b/docs/_modules/pydfnworks/dfnGraph/dfn2graph.html new file mode 100644 index 00000000..fdd0019b --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGraph/dfn2graph.html @@ -0,0 +1,553 @@ + + + + + + pydfnworks.dfnGraph.dfn2graph — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGraph.dfn2graph

+import networkx as nx
+import numpy as np
+import json
+import sys
+
+from networkx.readwrite import json_graph
+import matplotlib
+
+matplotlib.use('Agg')
+
+import matplotlib.pylab as plt
+
+from pydfnworks.dfnGraph.intersection_graph import create_intersection_graph
+from pydfnworks.dfnGraph.fracture_graph import create_fracture_graph
+from pydfnworks.dfnGraph.bipartite_graph import create_bipartite_graph
+
+
+
+[docs] +def create_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() + + if graph_type == "fracture": + G = create_fracture_graph(inflow, outflow) + elif graph_type == "intersection": + G = create_intersection_graph(inflow, outflow) + elif graph_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." + ) + return nx.Graph() + return G
+ + + +def boundary_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: + return bc_dict[bc_name] + except: + error = f"Error. Unknown boundary condition: {bc_name} \nExiting\n" + sys.stderr.write(error) + sys.exit(1) + + +
+[docs] +def add_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 + + """ + + if not type(source) == list: + source = [source] + + print("--> Adding new source connections") + print("--> Warning old source will be removed!!!") + + if G.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 + + for u in source: + G.add_edge(u, 's') + + elif G.graph['representation'] == "intersection": + # removing old source term and all connections + nodes_to_remove = ['s'] + for u, d in G.nodes(data=True): + if u != 's' and u != 't': + f1, f2 = d["frac"] + #print("node {0}: f1 {1}, f2 {2}".format(u,f1,f2)) + if f2 == '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') + for u, d in G.nodes(data=True): + if u != 's' and u != 't': + f1 = d["frac"][0] + f2 = d["frac"][1] + if f1 in source: + 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.) + elif f2 in source: + 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.) + + elif G.graph['representation'] == "bipartite": + print("--> Not supported for bipartite graph") + print("--> Returning unchanged graph") + return G
+ + + +
+[docs] +def add_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 + + """ + + if not type(target) == list: + source = [target] + + print("--> Adding new target connections") + print("--> Warning old target will be removed!!!") + + if G.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 + + for u in target: + G.add_edge(u, 't') + + elif G.graph['representation'] == "intersection": + # removing old target term and all connections + nodes_to_remove = ['t'] + for u, d in G.nodes(data=True): + if u != 's' and u != 't': + f1, f2 = d["frac"] + #print("node {0}: f1 {1}, f2 {2}".format(u,f1,f2)) + if f2 == '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') + for u, d in G.nodes(data=True): + if u != 's' and u != 't': + f1 = d["frac"][0] + f2 = d["frac"][1] + if f1 in target: + 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.) + elif f2 in target: + 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.) + + elif G.graph['representation'] == "bipartite": + print("--> Not supported for bipartite graph") + print("--> Returning unchanged graph") + return G
+ + + +def pull_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 + ----- + +""" + for node in [source, target]: + try: + nodes.remove(node) + except: + pass + return nodes + + +
+[docs] +def dump_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 + ----- + """ + + if G.graph['representation'] == "fracture": + nodes = list(G.nodes()) + elif G.graph['representation'] == "intersection": + nodes = [] + for u, v, d in G.edges(data=True): + nodes.append(G[u][v]['frac']) + nodes = list(set(nodes)) + elif G.graph['representation'] == "bipartite": + nodes = [] + for u, v, d in G.edges(data=True): + nodes.append(G[u][v]['frac']) + nodes = list(set(nodes)) + + nodes = pull_source_and_target(nodes) + fractures = [int(i) for i in nodes] + fractures = sorted(fractures) + print("--> Dumping %s" % filename) + np.savetxt(filename, fractures, fmt="%d")
+ + + +
+[docs] +def plot_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] +def dump_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) + with open(name + '.json', 'w') as fp: + json.dump(jsondata, fp) + print("--> Complete")
+ + + +
+[docs] +def load_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") + return G
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGraph/graph_flow.html b/docs/_modules/pydfnworks/dfnGraph/graph_flow.html new file mode 100644 index 00000000..ba22afa7 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGraph/graph_flow.html @@ -0,0 +1,499 @@ + + + + + + pydfnworks.dfnGraph.graph_flow — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnGraph.graph_flow

+import networkx as nx
+import numpy as np
+import sys
+import scipy.sparse
+import h5py
+
+# pydfnworks modules
+from pydfnworks.dfnGraph.intersection_graph import create_intersection_graph
+from pydfnworks.dfnGraph.graph_attributes import add_perm, add_area, add_weight
+from pydfnworks.dfnGen.meshing import mesh_dfn_helper as mh
+
+
+def get_laplacian_sparse_mat(G,
+                             nodelist=None,
+                             weight=None,
+                             dtype=None,
+                             format='lil'):
+    """ Get the matrices D, A that make up the Laplacian sparse matrix in desired sparsity format. Used to enforce boundary conditions by modifying rows of L = D - A
+
+    Parameters
+    ----------
+        G : object
+            NetworkX graph equipped with weight attribute
+
+        nodelist : list
+            list of nodes of G for which laplacian is desired. Default is None in which case, all the nodes
+        
+        weight : string
+            For weighted Laplacian, else all weights assumed unity
+        
+        dtype :  default is None, cooresponds to float
+        
+        format: string
+            sparse matrix format, csr, csc, coo, lil_matrix with default being lil
+
+    Returns
+    -------
+        D : sparse 2d float array       
+            Diagonal part of Laplacian
+            
+        A : sparse 2d float array
+            Adjacency matrix of graph
+    """
+
+    # A = nx.to_scipy_sparse_matrix(G,
+    #                               nodelist=nodelist,
+    #                               weight=weight,
+    #                               dtype=dtype,
+    #                               format=format)
+    A = nx.to_scipy_sparse_array(G,
+                                nodelist=nodelist,
+                                weight=weight,
+                                dtype=dtype,
+                                format=format)
+
+    (n, n) = A.shape
+    data = np.asarray(A.sum(axis=1).T)
+    D = scipy.sparse.spdiags(data, 0, n, n, format=format)
+    return D, A
+
+
+def prepare_graph_with_attributes(inflow, outflow, G=None):
+    """ Create a NetworkX graph, prepare it for flow solve by equipping edges with  attributes, renumber vertices, and tag vertices which are on inlet or outlet
+    
+    Parameters
+    ----------
+        inflow : string
+            name of file containing list of DFN fractures on inflow boundary
+
+        outflow: string
+            name of file containing list of DFN fractures on outflow boundary
+
+    Returns
+    -------
+        Gtilde : NetworkX graph
+    """
+
+    if G == None:
+        G = create_intersection_graph(inflow, outflow)
+
+        Gtilde = G.copy()
+        # need to add aperture
+        add_perm(Gtilde)
+        add_area(Gtilde)
+        add_weight(Gtilde)
+
+    else:
+        Gtilde = G
+        #add_perm(Gtilde)
+        #add_area(Gtilde)
+        add_weight(Gtilde)
+
+    for v in nx.nodes(Gtilde):
+        Gtilde.nodes[v]['inletflag'] = False
+        Gtilde.nodes[v]['outletflag'] = False
+
+    if len(list(nx.neighbors(Gtilde, 's'))) == 0:
+        error = "Error. There are no nodes in the inlet.\nExiting"
+        sys.stderr.write(error)
+        sys.exit(1)
+
+    for v in nx.neighbors(Gtilde, 's'):
+        Gtilde.nodes[v]['inletflag'] = True
+
+    if len(list(nx.neighbors(Gtilde, 't'))) == 0:
+        error = "Error. There are no nodes in the outlet.\nExiting"
+        sys.stderr.write(error)
+        sys.exit(1)
+
+    for v in nx.neighbors(Gtilde, 't'):
+        Gtilde.nodes[v]['outletflag'] = True
+
+    Gtilde.remove_node('s')
+    Gtilde.remove_node('t')
+
+    Gtilde = nx.convert_node_labels_to_integers(Gtilde,
+                                                first_label=0,
+                                                ordering="sorted",
+                                                label_attribute="old_label")
+
+    return Gtilde
+
+
+def solve_flow_on_graph(G, pressure_in, pressure_out, fluid_viscosity, phi):
+    """ Given a NetworkX graph prepared  for flow solve, solve for vertex pressures, and equip edges with attributes (Darcy) flux  and time of travel
+
+    Parameters
+    ----------
+        G : NetworkX graph
+
+        pressure_in : double
+            Value of pressure (in Pa) at inlet
+        
+        pressure_out : double
+            Value of pressure (in Pa) at outlet
+        
+        fluid_viscosity : double
+            optional, in Pa-s, default is for water
+
+        phi : double
+            Porosity, default is 1
+
+    Returns
+    -------
+        H : Acyclic Directed NetworkX graph 
+            H is updated with vertex pressures, edge fluxes and travel times. The only edges that exists are those with postive flow rates. 
+
+    Notes
+    ----------
+        None
+
+    """
+
+    print("--> Starting Graph flow")
+
+    Inlet = [v for v in nx.nodes(G) if G.nodes[v]['inletflag']]
+    Outlet = [v for v in nx.nodes(G) if G.nodes[v]['outletflag']]
+
+    if not set(Inlet).isdisjoint(set(Outlet)):
+        error = "Incompatible graph: Vertex connected to both source and target\n"
+        sys.stderr.write(error)
+        sys.exit(1)
+
+    D, A = get_laplacian_sparse_mat(G, weight='weight', format='lil')
+
+    b = np.zeros(G.number_of_nodes())
+
+    for v in Inlet:
+        b[v] = pressure_in
+        A[v, :] = 0
+        D[v, v] = 1.0
+    for v in Outlet:
+        b[v] = pressure_out
+        A[v, :] = 0
+        D[v, v] = 1.0
+    L = D - A  # automatically converts to csr when returning L
+
+    print("--> Solving Linear System for pressure at nodes")
+    pressure = scipy.sparse.linalg.spsolve(L, b)
+    print("--> Updating graph edges with flow solution")
+
+    for v in nx.nodes(G):
+        G.nodes[v]['pressure'] = pressure[v]
+
+    H = nx.DiGraph()
+    H.add_nodes_from(G.nodes(data=True))
+
+    for u, v in nx.edges(G):
+        # Find direction of flow
+        if G.nodes[u]['pressure'] > G.nodes[v]['pressure']:
+            upstream = u
+            downstream = v
+        elif G.nodes[v]['pressure'] >= G.nodes[u]['pressure']:
+            upstream = v
+            downstream = u
+
+        delta_p = G.nodes[upstream]['pressure'] - G.nodes[downstream][
+            'pressure']
+        if delta_p > 1e-16:
+            ## Create new edge in DiGraph
+            H.add_edge(upstream, downstream)
+            # Transfer edge attributes
+            for att in [
+                    'perm', 'iperm', 'length', 'weight', 'area', 'frac', 'b'
+            ]:
+                H.edges[upstream, downstream][att] = G.edges[upstream,
+                                                             downstream][att]
+
+            H.edges[upstream, downstream]['flux'] = (
+                H.edges[upstream, downstream]['perm'] / fluid_viscosity) * (
+                    delta_p / H.edges[upstream, downstream]['length'])
+
+            H.edges[upstream, downstream]['vol_flow_rate'] = H.edges[
+                upstream, downstream]['flux'] * H.edges[upstream,
+                                                        downstream]['area']
+
+            # H.edges[downstream, upstream]['vol_flow_rate'] =  -1*H.edges[upstream, downstream]['vol_flow_rate']
+
+            H.edges[upstream,
+                    downstream]['velocity'] = H.edges[upstream,
+                                                      downstream]['flux'] / phi
+            H.edges[upstream,
+                    downstream]['time'] = H.edges[upstream, downstream][
+                        'length'] / (H.edges[upstream, downstream]['velocity'])
+
+    print("--> Graph flow complete")
+    return H
+
+
+def compute_dQ(self, G):
+    """ Computes the DFN fracture intensity (p32) and flow channeling density indicator from the graph flow solution on G
+
+    Parameters
+    -----------------
+        self : object
+            DFN Class
+
+        G : networkX graph 
+            Output of run_graph_flow
+
+    Returns
+    ---------------
+        p32 : float
+            Fracture intensity
+        dQ : float flow channeling density indicator 
+
+    Notes
+    ------------
+        For definitions of p32 and dQ along with a discussion see " Hyman, Jeffrey D. "Flow channeling in fracture networks: characterizing the effect of density on preferential flow path formation." Water Resources Research 56.9 (2020): e2020WR027986. "
+
+    """
+    print(
+        "--> Computing fracture intensity (p32) and flow channeling density indicator (dQ)"
+    )
+
+    fracture_surface_area = 2*self.surface_area
+    domain_volume = self.domain['x'] * self.domain['y'] * self.domain['z']
+
+    Qf = np.zeros(self.num_frac)
+    ## convert to undirected
+    H = G.to_undirected()
+    ## walk through fractures
+    for curr_frac in range(1, self.num_frac + 1):
+        # print(f"\nstarting on fracture {curr_frac}")
+        # Gather nodes on current fracture
+        current_nodes = []
+        for u, d in H.nodes(data=True):
+            for f in d["frac"]:
+                if f == curr_frac:
+                    current_nodes.append(u)
+        # cycle through nodes on the fracture and get the outgoing / incoming
+        # volumetric flow rates
+        for u in current_nodes:
+            neighbors = H.neighbors(u)
+            for v in neighbors:
+                if v not in current_nodes:
+                    # outgoing vol flow rate
+                    Qf[curr_frac - 1] += abs(H[u][v]['vol_flow_rate'])
+                    for f in H.nodes[v]['frac']:
+                        if f != curr_frac and f != 's' and f != 't':
+                            # incoming vol flow rate
+                            Qf[f - 1] += abs(H[u][v]['vol_flow_rate'])
+    # Divide by 1/2 to remove up double counting
+    Qf *= 0.5
+    p32 = fracture_surface_area.sum() / domain_volume
+    top = sum(fracture_surface_area * Qf)**2
+    bottom = sum(fracture_surface_area * Qf**2)
+    print(top, bottom)
+    dQ = (1.0 / domain_volume) * (top / bottom)
+    print(f"--> P32: {p32:0.2e} [1/m]")
+    print(f"--> dQ: {dQ:0.2e} [1/m]")
+    print(f"--> Active surface percentage {100*dQ/p32:0.2f}")
+    print(f"--> Geometric equivalent fracture spacing {1/p32:0.2e} m")
+    print(f"--> Hydrological equivalent fracture spacing {1/dQ:0.2e} m")
+    print("--> Complete \n")
+    return p32, dQ, Qf
+
+
+def dump_graph_flow_values(G):
+
+    num_edges = G.number_of_edges()
+    velocity = np.zeros(num_edges)
+    lengths = np.zeros_like(velocity)
+    vol_flow_rate = np.zeros_like(velocity)
+    area = np.zeros_like(velocity)
+    aperture = np.zeros_like(velocity)
+    volume = np.zeros_like(velocity)
+
+    for i, val in enumerate(G.edges(data=True)):
+        u, v, d = val
+        velocity[i] = d['velocity']
+        lengths[i] = d['length']
+        vol_flow_rate[i] = d['vol_flow_rate']
+        area[i] = d['area']
+        aperture[i] = d['b']
+        volume[i] = area[i] * aperture[i]
+
+    with h5py.File(f"graph_flow.hdf5", "w") as f5file:
+        h5dset = f5file.create_dataset('velocity', data=velocity)
+        h5dset = f5file.create_dataset('length', data=lengths)
+        h5dset = f5file.create_dataset('vol_flow_rate', data=vol_flow_rate)
+        h5dset = f5file.create_dataset('area', data=area)
+        h5dset = f5file.create_dataset('aperture', data=aperture)
+        h5dset = f5file.create_dataset('volume', data=volume)
+    f5file.close()
+
+
+
+[docs] +def run_graph_flow(self, + inflow, + outflow, + pressure_in, + pressure_out, + fluid_viscosity=8.9e-4, + phi=1, + G=None): + """ Solve for pressure driven steady state flow on a graph representation of the DFN. + + Parameters + ---------- + self : object + DFN Class + + inflow : string + name of file containing list of DFN fractures on inflow boundary + + outflow: string + name of file containing list of DFN fractures on outflow boundary + + pressure_in : double + Value of pressure at inlet [Pa] + + pressure_out : double + Value of pressure at outlet [Pa] + + fluid_viscosity : double + optional, default is for water. [Pa*s] + + phi : double + Fracture porosity, default is 1 [-] + + G : Input Graph + + Returns + ------- + Gtilde : NetworkX graph + Gtilde is a directed acyclic graph with vertex pressures, fluxes, velocities, volumetric flow rates, and travel times + + """ + if G == None: + G = self.create_graph("intersection", inflow, outflow) + + Gtilde = prepare_graph_with_attributes(inflow, outflow, G) + Gtilde = solve_flow_on_graph(Gtilde, pressure_in, pressure_out, + fluid_viscosity, phi) + + dump_graph_flow_values(Gtilde) + return Gtilde
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGraph/graph_transport.html b/docs/_modules/pydfnworks/dfnGraph/graph_transport.html new file mode 100644 index 00000000..0a3379de --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGraph/graph_transport.html @@ -0,0 +1,577 @@ + + + + + + pydfnworks.dfnGraph.graph_transport — dfnWorks 2.7 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>
+
+"""
+
+import timeit
+import sys
+import networkx as nx
+import numpy as np
+import multiprocessing as mp
+
+# pydfnworks graph modules modules
+import pydfnworks.dfnGraph.particle_io as io
+from pydfnworks.dfnGraph.graph_tdrw import set_up_limited_matrix_diffusion
+from pydfnworks.dfnGraph.particle_class import Particle
+
+
+def track_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
+
+    """
+    if verbose:
+        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
+    global nbrs_dict
+    global G_global
+    particle.track(G_global, nbrs_dict)
+    if verbose:
+        print(
+            f"--> Particle {data['particle_number']} is complete on worker {cpu_id}"
+        )
+    return particle
+
+
+def get_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 = [v for v in nx.nodes(G) if G.nodes[v]['inletflag']]
+    cnt = len(inlet_nodes)
+    print(f"--> There are {cnt} inlet nodes")
+    if cnt == 0:
+        error = "Error. There are no nodes in the inlet.\nExiting"
+        sys.stderr.write(error)
+        sys.exit(1)
+
+    # Uniform Distribution for particles
+    if initial_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
+        for i in range(nparticles):
+            ip[i] = inlet_nodes[inflow_idx]
+            inflow_cnt += 1
+            if inflow_cnt >= n:
+                inflow_idx += 1
+                inflow_cnt = 0
+
+    ## flux weighted initial positions for particles
+    elif initial_positions == "flux":
+        print("--> Using flux-weighted initial positions.\n")
+        flux = np.zeros(cnt)
+        for i, u in enumerate(inlet_nodes):
+            for v in G.successors(u):
+                flux[i] += G.edges[u, v]['flux']
+        flux /= flux.sum()
+        flux_cnts = [np.ceil(nparticles * i) for i in flux]
+        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
+        for i in range(nparticles):
+            ip[i] = inlet_nodes[inflow_idx]
+            inflow_cnt += 1
+            if inflow_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)
+
+    return ip, nparticles
+
+
+def create_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 = {}
+
+    for u in nx.nodes(G):
+
+        if G.nodes[u]['outletflag']:
+            continue
+
+        node_list = []
+        prob_list = []
+        nbrs_dict[u] = {}
+
+        for v in G.successors(u):
+            node_list.append(v)
+            prob_list.append(G.edges[u, v]['vol_flow_rate'])
+
+        if node_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
+
+    return nbrs_dict
+
+
+def check_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
+    
+    """
+
+    if matrix_porosity is None:
+        error = f"Error. Requested TDRW but no value for matrix_porosity was provided\n"
+        sys.stderr.write(error)
+        sys.exit(1)
+    elif matrix_porosity < 0 or matrix_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)
+    if matrix_diffusivity is None:
+        error = f"Error. Requested TDRW but no value for matrix_diffusivity was provided\n"
+        sys.stderr.write(error)
+        sys.exit(1)
+
+    if fracture_spacing is not None:
+        if fracture_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)
+
+
+def check_control_planes(control_planes, direction):
+    control_plane_flag = False
+    if not type(control_planes) is list:
+        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
+
+    if direction is None:
+        error = f"Error. Primary direction not provided. Required for control planes\n"
+        sys.stderr.write(error)
+        sys.exit(1)
+    elif direction not in ['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}")
+    return control_plane_flag
+
+
+
+[docs] +def run_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. + global G_global + G_global = nx.Graph() + G_global = G.copy() + + if not format in ['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 + if tdrw_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" + ) + + if control_planes is None: + 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") + global nbrs_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") + + if dump_traj: + print(f"--> Writing trajectory information to file") + + if fracture_spacing is not None: + 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 + if self.ncpu == 1: + tic = timeit.default_timer() + particles = [] + for i in range(nparticles): + if i % 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) + if control_plane_flag: + io.dump_control_planes(particles, control_planes, cp_filename, + format) + + if dump_traj: + io.dump_trajectories(particles, 1) + + if self.ncpu > 1: + print(f"--> Using {self.ncpu} processors") + ## Prepare input data + inputs = [] + + tic = timeit.default_timer() + pool = mp.Pool(min(self.ncpu, nparticles)) + + particles = [] + + def gather_output(output): + particles.append(output) + + for i in range(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) + if control_plane_flag: + io.dump_control_planes(particles, control_planes, cp_filename, + format) + + if dump_traj: + io.dump_trajectories(particles, min(self.ncpu, nparticles)) + + if stuck_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 + del G_global + del nbrs_dict + + return particles
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnGraph/pruning.html b/docs/_modules/pydfnworks/dfnGraph/pruning.html new file mode 100644 index 00000000..9642f8ba --- /dev/null +++ b/docs/_modules/pydfnworks/dfnGraph/pruning.html @@ -0,0 +1,316 @@ + + + + + + pydfnworks.dfnGraph.pruning — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pydfnworks.dfnGraph.pruning

+import numpy as np
+import networkx as nx
+
+from networkx.algorithms.flow.shortestaugmentingpath import *
+from networkx.algorithms.flow.edmondskarp import *
+from networkx.algorithms.flow.preflowpush import *
+
+from itertools import islice
+
+
+def current_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), d in cf.items() if d > 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")
+    return H
+
+
+def k_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
+"""
+    return list(
+        islice(nx.shortest_simple_paths(G, source, target, weight=weight), k))
+
+
+
+[docs] +def k_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([]) + for path in k_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)) + for n in secondary: + H.remove_node(n) + return H + print("--> Complete\n")
+ + + +
+[docs] +def greedy_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") + if G.graph['representation'] != "intersection": + print( + "--> ERROR!!! Wrong type of DFN graph representation\nRepresentation must be intersection\nReturning Empty Graph\n" + ) + return nx.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')) + if k == '' or k > min_cut: + k = min_cut + + while nx.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 + if cnt > k: + break + print("--> Complete") + return Hprime
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/_modules/pydfnworks/dfnTrans/transport.html b/docs/_modules/pydfnworks/dfnTrans/transport.html new file mode 100644 index 00000000..59bd1e60 --- /dev/null +++ b/docs/_modules/pydfnworks/dfnTrans/transport.html @@ -0,0 +1,485 @@ + + + + + + pydfnworks.dfnTrans.transport — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pydfnworks.dfnTrans.transport

+import os
+import sys
+import shutil
+from time import time
+import subprocess
+
+
+
+[docs] +def dfn_trans(self): + """Primary driver for dfnTrans. + + Parameters + --------- + self : object + DFN Class + + Returns + -------- + None + """ + print('=' * 80) + print("\ndfnTrans Starting\n") + print('=' * 80) + tic = time() + self.copy_dfn_trans_files() + self.check_dfn_trans_run_files() + self.run_dfn_trans() + delta_time = time() - tic + self.dump_time('Process: dfnTrans', delta_time) + print('=' * 80) + print("\ndfnTrans Complete\n") + print("Time Required for dfnTrans: %0.2f Seconds\n" % delta_time) + print('=' * 80)
+ + + +
+[docs] +def copy_dfn_trans_files(self): + """Creates symlink to dfnTrans Execuateble and copies input files for dfnTrans into working directory + + Parameters + --------- + self : object + DFN Class + + Returns + -------- + None + """ + + print("Attempting to Copy %s\n" % self.dfnTrans_file) + try: + shutil.copy(self.dfnTrans_file, os.path.abspath(os.getcwd())) + except OSError: + print("--> Problem copying %s file" % self.local_dfnTrans_file) + print("--> Trying to delete and recopy") + os.remove(self.local_dfnTrans_file) + shutil.copy(self.dfnTrans_file, os.path.abspath(os.getcwd())) + except: + print("--> ERROR: Problem copying %s file\n" % self.dfnTrans_file) + error = "Unable to replace. Exiting Program\n" + sys.stderr.write(error) + sys.exit(1)
+ + + +
+[docs] +def run_dfn_trans(self): + """ Execute dfnTrans + + Parameters + --------- + self : object + DFN Class + + Returns + -------- + None + """ + tic = time() + failure = subprocess.call(os.environ['DFNTRANS_EXE'] + ' ' + + self.local_dfnTrans_file, + shell=True) + self.dump_time("Function: DFNTrans ", time() - tic) + if failure != 0: + error = "--> ERROR: dfnTrans did not complete\n" + sys.stderr.write(error) + sys.exit(1)
+ + + + + + + +
+[docs] +def check_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:"] + + if self.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:") + + if self.flow_solver == "FEHM": + params["FEHM_fin:"] = None + files.append("FEHM_fin:") + + # Parse DFNTrans input and fill dictionary + keys = params.keys() + with open(self.local_dfnTrans_file) as fp: + + for line in fp.readlines(): + if "/*" in line: + comment = line + line = line[:line.index( + "/*")] ## only process text before '/*' comment + if "//" in line: + line = line[:line.index("//")] + + if len(line) > 0: + for key in keys: + if key in line: + if params[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 + for key in files: + if params[key] is None: + error = f"ERROR!!!!!\nRequired file {key} was not provided.\nPlease check DFNTrans control file\nExiting Program\n" + sys.stderr.write(error) + sys.exit(1) + elif not os.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" + ) + + for required in [ + "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:" + ]: + if params[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 = [] + for ic in initial_conditions: + #print(ic,params[ic[0]]) + if params[ic[0]] == "yes": + ic_selected.append(ic[0]) + for i in ic: + if params[i] == None: + error = "Initial condition %s selected but %s not provided\n" % ( + ic[0], i) + sys.stderr.write(error) + sys.exit(1) + if len(ic_selected) > 1: + error = "ERROR!!! More than one initial condition defined\nExiting\n" + sys.stderr.write(error) + print("Selected Initial Conditions:\n:") + for ic in ic_selected: + print(ic) + print() + sys.exit(1) + elif len(ic_selected) == 0: + error = "ERROR!!! No initial condition defined\nExiting\n" + sys.stderr.write(error) + sys.exit(1) + + if params["ControlPlane:"] != None: + for required in ["control_out:", "delta_Control:", "flowdir:"]: + if params[required] == None: + error = "Parameter %s required for ControlPlane\n" % required + sys.stderr.write(error) + sys.exit(1) + + if params["tdrw:"] == "yes": + if params["time_units:"] != "seconds": + error = "ERROR!!!You must use seconds for the time_units to run TDRW" + sys.stderr.write(error) + sys.exit(1) + + for required in ["tdrw_porosity:", "tdrw_diffcoeff:"]: + if params[required] == None: + error = "Parameter %s required for tdrw\n" % required + sys.stderr.write(error) + sys.exit(1) + + if params["aperture:"] == "yes": + if params["aperture_type:"] == None: + error = "Parameter aperture_type: required for aperture: yes\n" + sys.stderr.write(error) + sys.exit(1) + + else: + if not os.path.isfile(params["aperture_file:"]) or os.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: + if params["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/docs/_sources/applications.rst.txt b/docs/_sources/applications.rst.txt new file mode 100644 index 00000000..62b7d7be --- /dev/null +++ b/docs/_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/docs/_sources/dfnflow.rst.txt b/docs/_sources/dfnflow.rst.txt new file mode 100644 index 00000000..082bf27a --- /dev/null +++ b/docs/_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/docs/_sources/dfngen.rst.txt b/docs/_sources/dfngen.rst.txt new file mode 100644 index 00000000..6eaadacc --- /dev/null +++ b/docs/_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/docs/_sources/dfntrans.rst.txt b/docs/_sources/dfntrans.rst.txt new file mode 100644 index 00000000..87a66bc8 --- /dev/null +++ b/docs/_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/docs/_sources/examples.rst.txt b/docs/_sources/examples.rst.txt new file mode 100644 index 00000000..15065d38 --- /dev/null +++ b/docs/_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/docs/_sources/gallery.rst.txt b/docs/_sources/gallery.rst.txt new file mode 100644 index 00000000..73c5f8bb --- /dev/null +++ b/docs/_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_docs.rst.txt b/docs/_sources/index_docs.rst.txt new file mode 100644 index 00000000..4a5576d9 --- /dev/null +++ b/docs/_sources/index_docs.rst.txt @@ -0,0 +1,28 @@ +.. 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. + +Welcome to dfnWorks 2.7 documentation! +======================================= + +Contents: + +.. toctree:: + :maxdepth: 3 + + intro + applications + setup + examples + pydfnworks + pydfnGen + pydfnFlow + pydfnTrans + pydfnGraph + pydfnWorks-well + dfngen + dfnflow + dfntrans + output + publications + gallery diff --git a/docs/_sources/intro.rst.txt b/docs/_sources/intro.rst.txt new file mode 100644 index 00000000..5a449ad5 --- /dev/null +++ b/docs/_sources/intro.rst.txt @@ -0,0 +1,253 @@ +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://hub.docker.com/r/ees16/dfnworks + +dfnWorks can be downloaded from https://github.com/lanl/dfnWorks/ + +v1.0 can be downloaded from https://github.com/dfnWorks/dfnWorks-Version1.0 + + +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.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/docs/_sources/output.rst.txt b/docs/_sources/output.rst.txt new file mode 100644 index 00000000..9de1a066 --- /dev/null +++ b/docs/_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/docs/_sources/publications.rst.txt b/docs/_sources/publications.rst.txt new file mode 100644 index 00000000..7d8f99db --- /dev/null +++ b/docs/_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/docs/_sources/pydfnFlow.rst.txt b/docs/_sources/pydfnFlow.rst.txt new file mode 100644 index 00000000..0dadd340 --- /dev/null +++ b/docs/_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/docs/_sources/pydfnGen.rst.txt b/docs/_sources/pydfnGen.rst.txt new file mode 100644 index 00000000..e03042ea --- /dev/null +++ b/docs/_sources/pydfnGen.rst.txt @@ -0,0 +1,186 @@ +.. _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 + :members: mesh_network + + +Meshing helper methods +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. automodule:: pydfnworks.dfnGen.meshing.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 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. 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 + + diff --git a/docs/_sources/pydfnGraph.rst.txt b/docs/_sources/pydfnGraph.rst.txt new file mode 100644 index 00000000..4965abfb --- /dev/null +++ b/docs/_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/docs/_sources/pydfnTrans.rst.txt b/docs/_sources/pydfnTrans.rst.txt new file mode 100644 index 00000000..93f99325 --- /dev/null +++ b/docs/_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/docs/_sources/pydfnWorks-well.rst.txt b/docs/_sources/pydfnWorks-well.rst.txt new file mode 100644 index 00000000..1d6276a1 --- /dev/null +++ b/docs/_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/docs/_sources/pydfnworks.rst.txt b/docs/_sources/pydfnworks.rst.txt new file mode 100644 index 00000000..0913ea68 --- /dev/null +++ b/docs/_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/docs/_sources/setup.rst.txt b/docs/_sources/setup.rst.txt new file mode 100644 index 00000000..0d3a4f7c --- /dev/null +++ b/docs/_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/docs/_static/_sphinx_javascript_frameworks_compat.js b/docs/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 00000000..81415803 --- /dev/null +++ b/docs/_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/docs/_static/basic.css b/docs/_static/basic.css new file mode 100644 index 00000000..30fee9d0 --- /dev/null +++ b/docs/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 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/docs/_static/css/badge_only.css b/docs/_static/css/badge_only.css new file mode 100644 index 00000000..c718cee4 --- /dev/null +++ b/docs/_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/docs/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 00000000..6cb60000 Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 00000000..7059e231 Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/docs/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 00000000..f815f63f Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 00000000..f2c76e5b Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.eot b/docs/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 00000000..e9f60ca9 Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.svg b/docs/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 00000000..855c845e --- /dev/null +++ b/docs/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/css/fonts/fontawesome-webfont.ttf b/docs/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 00000000..35acda2f Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.woff b/docs/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 00000000..400014a4 Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.woff2 b/docs/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 00000000..4d13fc60 Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/docs/_static/css/fonts/lato-bold-italic.woff b/docs/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 00000000..88ad05b9 Binary files /dev/null and b/docs/_static/css/fonts/lato-bold-italic.woff differ diff --git a/docs/_static/css/fonts/lato-bold-italic.woff2 b/docs/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 00000000..c4e3d804 Binary files /dev/null and b/docs/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/docs/_static/css/fonts/lato-bold.woff b/docs/_static/css/fonts/lato-bold.woff new file mode 100644 index 00000000..c6dff51f Binary files /dev/null and b/docs/_static/css/fonts/lato-bold.woff differ diff --git a/docs/_static/css/fonts/lato-bold.woff2 b/docs/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 00000000..bb195043 Binary files /dev/null and b/docs/_static/css/fonts/lato-bold.woff2 differ diff --git a/docs/_static/css/fonts/lato-normal-italic.woff b/docs/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 00000000..76114bc0 Binary files /dev/null and b/docs/_static/css/fonts/lato-normal-italic.woff differ diff --git a/docs/_static/css/fonts/lato-normal-italic.woff2 b/docs/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 00000000..3404f37e Binary files /dev/null and b/docs/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/docs/_static/css/fonts/lato-normal.woff b/docs/_static/css/fonts/lato-normal.woff new file mode 100644 index 00000000..ae1307ff Binary files /dev/null and b/docs/_static/css/fonts/lato-normal.woff differ diff --git a/docs/_static/css/fonts/lato-normal.woff2 b/docs/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 00000000..3bf98433 Binary files /dev/null and b/docs/_static/css/fonts/lato-normal.woff2 differ diff --git a/docs/_static/css/theme.css b/docs/_static/css/theme.css new file mode 100644 index 00000000..19a446a0 --- /dev/null +++ b/docs/_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/docs/_static/dfnworks_logo.png b/docs/_static/dfnworks_logo.png new file mode 100644 index 00000000..43da41a8 Binary files /dev/null and b/docs/_static/dfnworks_logo.png differ diff --git a/docs/_static/doctools.js b/docs/_static/doctools.js new file mode 100644 index 00000000..d06a71d7 --- /dev/null +++ b/docs/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 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/docs/_static/documentation_options.js b/docs/_static/documentation_options.js new file mode 100644 index 00000000..0e1fafab --- /dev/null +++ b/docs/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '2.7', + 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/docs/_static/file.png b/docs/_static/file.png new file mode 100644 index 00000000..a858a410 Binary files /dev/null and b/docs/_static/file.png differ diff --git a/docs/_static/jquery-3.5.1.js b/docs/_static/jquery-3.5.1.js new file mode 100644 index 00000000..50937333 --- /dev/null +++ b/docs/_static/jquery-3.5.1.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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.

+
+alternate text +
+

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.

+
+alternate text +
+

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.

+
+alternate text +
+

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.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/dfnflow.html b/docs/dfnflow.html new file mode 100644 index 00000000..3d3048eb --- /dev/null +++ b/docs/dfnflow.html @@ -0,0 +1,277 @@ + + + + + + + dfnFlow — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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.

+
# 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
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/dfngen.html b/docs/dfngen.html new file mode 100644 index 00000000..034dc308 --- /dev/null +++ b/docs/dfngen.html @@ -0,0 +1,2578 @@ + + + + + + + dfnGen - C++ Generation Code — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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.

+
+

Domain Parameters

+

The following parameters define the domain. All units are in SI (meters for length).

+
+

Note

+

Parameters with (Mandatory) label must be present in python driver script for simulations to run.

+
+
+
+

domainSize

+

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.

+
+
+
+
+

domainSizeIncrease

+

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.

+
+
+
+
+

numOfLayers

+

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
+
+
+
+
+
+

layers

+

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)
+
Layers can overlap
+
+
+
+
+
+

numOfRegions

+

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
+
+
+
+
+
+

regions

+

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
+
Regions can overlap
+
+
+
+
+
+

ignoreBoundaryFaces

+

Description: Selection of using the boundary faces option.

+

Type: boolean

+
+
False: use boundaryFaces option
+
True: ignore boundaryFaces option and keep all clusters
+
+

Example:

+
DFN.params['ignoreBoundaryFaces']['value'] = True
+
+
+
+

Warning

+

All clusters are retained only if keepOnlyLargestCluster is set to 0.

+
+
+
+
+

boundaryFaces

+

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
+
+
+
+

Warning

+

ignoreBoundaryFaces must be False (default) when using this feature

+
+
+
+
+
+

General Network Generation Parameters

+

The following parameters define the general network properties.

+
+

Note

+

parameters with (Mandatory) label must be present in python driver script for simulations to run.

+
+
+
+

stopCondition

+

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).

+
+
+
+
+

nPoly

+

Description: The total number of fractures requested in the domain. dfnGen will stop generation once there are nPoly number of fractures.

+

Type: Positive Integer (nPoly > 0)

+

Default: None

+

Example:

+
DFN.params['nPoly']['value'] = 100 # Stop generation once 100 fractures are accepted into the network
+
+
+
+

Note

+

Only used if stopCondition is set to 0

+
+
+
+
+

famProb

+

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.

+
+
+
+
+

orientationOption

+

Description: Selection of fracture family orientation definition.

+

Type: Value of 0,1,2

+
+
0 : Spherical Coordinates
+
1 : Trend / Plunge
+
2 : Dip / Strike
+
+

This value is set automatically with the definition of fracture families (See pydfnGen).

+
+

Note

+
+
For spherical coordinates, values are defined using the function parameters theta/phi.
+
For Trend/Plunge, values are defined using trend/plunge.
+
For Dip/Strike, values are defined using dip/strike.
+
+
+
+

Warning

+
+
When using Trend / Plunge or Dip / Strike, angleOption must be set to degree (default), angleOption = ‘degree’
+
+
+
+
+
+

angleOption

+

Description: Selection of fracture family angle units. All stochastically generated fracture families will use the specified units

+

Type: String of either ‘degree’ or ‘radian’

+

Default: ‘degree’

+

Example:

+
DFN.params['angleOption']['value'] = 'radian'
+
+
+
+

Warning

+
+
When using Trend / Plunge or Dip / Strike, angleOption must be set to degree (default), angleOption = ‘degree’
+
+
+
+
+
+

h

+

Description: (Mandatory) Minimum feature size accepted into the network.

+

Type: Positive float

+
+

Note

+
+
The following constraints are imposed on h to keep the final mesh size reasonable, unless disableFram is turned on.
+
+
+
1. h must be greater than 10^{-5} \cdot \sqrt{x^2 + y^2 + z^2} where x,y,z are the elements of domainSize.
+
2. h must be smaller than 1/10th the minimum fracture size
+
3. h must be larger than 1/1000th than minimum fracture size
+
4. h must be non-zero
+
+
+

Default: None

+

Example:

+
DFN.params['h']['value'] = .1
+
+
+ +
+
+
+

disableFram

+
+

Danger

+

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.

+
+
+
+
+

printRejectReasons

+

Description: Option to print rejection information to screen during network generation.

+

Type: boolean

+
+
False: off. Limited rejection information will be printed to screen during generation.
+
True: on. Detailed fracture information will be printed to screen during generation.
+
+

Default: False

+

Example:

+
DFN.params['printRejectReasons']['value'] = True
+
+
+
+

Tip

+

Turning this feature on is useful for debugging and initial network construction. Having this turned off is more efficient for network generation.

+
+
+
+
+

rejectsPerFracture

+

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
+
+
+
+

Note

+

Default is 10. Set equal to 1 to ignore.

+
+
+
+
+

radiiListIncrease

+

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.

+
+
+
+
+

visualizationMode

+
+

Warning

+

No longer supported. Selection of visual mode should be done using the pydfnWorks function DFN.mesh_network(visual_mode=True)

+
+

Description: Selection if you want to mesh to be coarse, for quick visualization but cannot run flow and transport, or standard mesh.

+

Type: boolean

+
+
False: Create full DFN mesh (full_mesh.inp) for flow and transport
+
True: Create reduced DFN mesh (reduced_mesh.inp) for quick visualization
+
+

Default: False

+

Example:

+
DFN.params['visualizationMode']['value'] = False
+
+
+
+
+
+

seed

+

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.

+
+
+
+
+

keepOnlyLargestCluster

+

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
+
+

Default: True

+

Example:

+
DFN.params['keepOnlyLargestCluster']['value'] = False
+
+
+
+
+
+

keepIsolatedFractures

+

Description: Selection to keep isolated fractures in the domain after generation is complete.

+

Type: boolean

+
+
False: Remove isolated fractures from the domain
+
True: Keep isolated in the domain.
+
+

Default: False

+

Example:

+
DFN.params['keepIsolatedFractures']['value'] = False
+
+
+
+

Note

+

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.

+
+
+
+
+

tripleIntersections

+

Description: Selection of whether triple intersection are accepted into the network.

+

Type: boolean

+
+
False: Reject all triple intersections
+
True: Accept triple intersections that meet FRAM criteria.
+
+

Default: False

+

Example:

+
DFN.params['tripleIntersections']['value'] = True
+
+
+
+

Note

+

Even if tripleIntersections = True, triple intersections can be rejected if they create a feature on the network smaller than h.

+
+
+

Warning

+

dfnTrans does not support triple intersections.

+
+
+
+
+

removeFracturesLessThan

+

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.

+
+
+
+
+

insertUserRectanglesFirst

+

Description: Select order for how user defined rectangles and user defined ellipses are inserted into the domain.

+

Type: boolean

+
+
False: Insert user defined ellipses first
+
True: Insert user defined rectangles first
+
+

Default: True

+

Example:

+
DFN.params['insertUserRectanglesFirst']['value'] = False
+
+
+
+

Note

+

User defined fractures (ellipses, rectangles, and polygons) are always inserted prior to stochastic fractures.

+
+
+
+
+

forceLargeFractures

+

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.

+
+
+
+
+
+

General Network Output Parameters

+
+

outputAllRadii

+

Description: Create an output file of all fracture radii, both accepted and rejected fractures.

+
+
Filename: radii_AllAccepted.dat
+
Format: xRadius yRadius Distribution # (-2 = userPolygon, -1 = userRectangle, 0 = userEllipse, > 0 is family in order of famProb)
+
+

Type: boolean

+
+
False: Do not create file
+
True: Create file
+
+

Default: False

+

Example

+
DFN.params['outputAllRadii']['value'] = False
+
+
+
+
+
+

outputAcceptedRadiiPerFamily

+

Description: Create one file that contains the radius of every fracture per family prior to isolated fractures removed.

+
+
Filename: radii/radii_AllAccepted_Fam_1.dat
+
Format: xRadius yRadius DistributionNumber (-2 = userPolygon, -1 = userRectangle, 0 = userEllipse, > 0 is family in order of famProb)
+
+

Type: boolean

+
+
False: Do not create file
+
True: Create file
+
+

Default: False

+

Example:

+
DFN.params['outputAcceptedRadiiPerFamily']['value'] = False
+
+
+
+
+
+

outputFinalRadiiPerFamily

+

Description: Create one file that contains the radius of every fracture per family after to isolated fractures removed.

+
+
Filename: radii/radii_Final_Fam_1.dat
+
Format: xRadius yRadius DistributionNumber (-2 = userPolygon, -1 = userRectangle, 0 = userEllipse, > 0 is family in order of famProb)
+
+

Type: boolean

+
+
False: Do not create file
+
True: Create file
+
+

Default: False

+

Example:

+
DFN.params['outputFinalRadiiPerFamily']['value'] = False
+
+
+
+
+
+
+

Fracture Family Generation Parameters: Ellipse

+

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

+
+
+

Ellipse: General Parameters

+
+

nFamEll

+

Description: Number of ellipse families

+

Type: Non-Negative Integer

+

Default: 0

+

Example:

+
DFN.params['nFamEll']['value'] = 3 # There will be 3 ellipse families
+
+
+
+
+
+

eLayer

+

Description: Assign each ellipse family to a layer in domain.

+

Type: list of nFamEll integers

+

Example:

+
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.

+
+
+
+
+

eRegion

+

Description: Assign each ellipse family to a region in domain.

+

Type: list of nFamEll integers

+

Example:

+
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.

+
+
+
+
+

e_p32Targets

+

Description: Target fracture intensity per family. Fracture intensity P_{32} [\text{m}^{-1}] 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.

+

Type: list of nFamEll floats

+

Example:

+
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
+
+
+
+

Note

+

Only used when stopCondition = 1

+
+
+

Warning

+

The fracture surface area is defined using both sides of a fracture.

+
+
+
+
+

enumPoints

+

Description: Number of vertices defining the boundary of each elliptical fracture

+

Type: list of nFamEll integers

+

Example:

+
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
+
3. Suggested value: 8
+
+
+
+
+
+

easpect

+

Description: Aspect ratio of fractures

+

Type: list of nFamEll floats

+

Example:

+
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

+
+
+
+
+
+

Ellipse: Fracture Orientation

+

The fracture orientations are sampled from the three-dimensional von Mises-Fisher distribution,

+
+

f({\bf x}; {\boldsymbol \mu}, \kappa ) = \frac{ \kappa \exp( \kappa {\boldsymbol \mu}^{T} {\bf x} )}{ 4 \pi \sinh(\kappa)}~.

+

where {\boldsymbol \mu} is the mean orientation vector and T denotes transpose. +The distribution is sampled using the algorithm provided Simulation of the von Mises Fisher distribution Communications in statistics-simulation and computation 23.1 (1994): 157-164. by Andrew Wood..

+

dfnGen accepts spherical coordinates (\theta/\phi), 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.

+
+
0 : Spherical Coordinate (Theta/Phi)
+
1 : Trend / Plunge
+
2 : Dip / Strike.
+
+
+
+
+

ekappa

+

Description: The concentration parameter of the von Mises-Fisher distribution, which determines the degree of clustering around the mean orientation.

+

Type: list of nFamEll floats

+

Example:

+
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 \kappa 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.

+
+
+
+
+

Ellipse: Spherical Coordinates

+

The mean normal vector of the fracture family \vec{n} is related to the spherical coordinates \theta and \phi by

+
+

\vec{n}_x & = \sin(\theta) \cos(\phi)\\

+
+

\vec{n}_y & = \sin(\theta) \sin(\phi)\\

+
+

\vec{n}_z & = \cos(\theta)

+
+
etheta
+

Description: Angle the normal vector of the fracture makes with the z-axis

+

Type: list of nFamEll floats

+

Example:

+
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 2 \pi.

+
+
+
+
+
ephi
+

Description: Angle that the projection of the normal vector of a fracture onto the x-y plane makes with the x-axis.

+

Type: list of nFamEll float

+

Example:

+
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 2 \pi.

+
+
+
+
+
+

Ellipse: Trend & Plunge

+

The mean normal vector of the fracture family \vec{n} is related to trend and plunge by

+
+

\vec{n}_x & = \cos(\text{trend}) \cos(\text{plunge})\\

+
+

\vec{n}_y & = \sin(\text{trend}) \cos(\text{plunge})\\

+
+

\vec{n}_z & = \sin(\text{trend})

+
+
etrend
+

Description: Trend of fracture families

+

Type: list of nFamEll float

+

Example

+
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

+
+
+
+
+
eplunge
+

Description: Plunge of fracture families

+

Type: list of nFamEll float

+

Example

+
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

+
+
+
+
+
+

Ellipse: Dip & Strike

+

The mean normal vector of the fracture family \vec{n} is related to dip and strike by

+
+

\vec{n}_x & = \sin(\text{dip}) \sin(\text{strike})\\

+
+

\vec{n}_y & = -\sin(\text{dip}) \cos(\text{strike})\\

+
+

\vec{n}_z & = \cos(\text{dip})

+

+
+
estrike
+

Description: Strike of fracture families

+

Type: list of nFamEll float

+

Example

+
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

+
+
+
+
+
edip
+

Description: Dip of fracture families

+

Type: list of nFamEll float

+

Example

+
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

+
+
+
+
+
+

Ellipse: In Plane Rotation

+
+
ebetaDistribution
+

Description: Prescribe a rotation around each fracture’s normal vector, with the fracture centered on x-y plane at the origin

+

Type: list of nFamEll boolean values

+
+
0: Uniform distribution on [0, 2 \pi )
+
1: Constant rotation specified by ebeta
+
+
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
+
+
+
+
+
+
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
+
+
+
+
+
+
+

Ellipse: Fracture Radius Distributions

+

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.

+
+
+

edistr

+

Description: Assigns fracture radius distribution for each family

+

Type: list of nFamEll Integers (1,2,3,4)

+

Example:

+
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.

+
+
+
+

Ellipse: Lognormal Distribution

+

Fracture radii are sampled from a Lognormal distribution with the following probability density function

+
+

\frac{1}{x \sigma \sqrt{2\pi}} \exp \left (- \frac{(\ln x -\mu)^2}{2 \sigma^2} \right)

+

with mean \mu and variance \sigma^2.

+
+

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 (\mu) and variance (\sigma^2) one uses

+
+

eLogMean = \ln  \left ( \frac{\mu^2}{\sqrt{\mu^2 + \sigma^2}} \right)

+

and

+
+

esd =  \sqrt{\ln \left ( 1 + \frac{ \sigma^2}{\mu^2} \right )}

+

For more details see https://en.wikipedia.org/wiki/Log-normal_distribution.

+
+
+
eLogMean
+
+

Warning

+

This value is not the mean of the Log Normal distribution. Use the equations above to convert between the values if needed.

+
+

Description: Mean value of the underlying normal distribution

+

Type: list of float. Length is the number of elements in edistr set to 1.

+

Example:

+
DFN.params['eLogMean']['value'] = [1.609] # This value along with that in esd produce a lognormal with mean 5 m and variance of 0.1
+
+
+
+
+
+
esd
+
+

Warning

+

This value is not the standard deviation of the Log Normal distribution. Use the equations above to convert between the values if needed.

+
+

Description: Standard deviation value of the underlying normal distribution

+

Type: list of Positive floats. Length is the number of elements in edistr set to 1.

+

Example:

+
DFN.params['esd']['value'] = [0.040] # This value along with that in eLogMean produce a lognormal with mean 5 m and variance of 0.1
+
+
+
+
+
+
eLogMin
+

Description: Minimum radius created by the LogNormal distribution for each family

+

Type: list of Positive floats. Length is the number of elements in edistr set to 1.

+

Example:

+
DFN.params['eLogMin']['value'] = [1,0.4]
+# Lognormal family 1 has a minimum radius of 1 m
+# Lognormal family 2 has a maximum radius of 0.4 m
+
+
+
+

Note

+

eLogMin must be less than eLogMax within each family.

+
+
+
+
eLogMax
+

Description: Maximum radius created by the LogNormal distribution for each family

+

Type: list of Positive floats. Length is the number of elements in edistr set to 1.

+

Example:

+
DFN.params['eLogMax']['value'] = [10,12]
+# Lognormal family 1 has a maximum radius of 10 m
+# Lognormal family 2 has a maximum radius of 12 m
+
+
+
+

Note

+

eLogMax must be greater than eLogMin within each family.

+
+
+
+
+
+

Ellipse: Truncated Powerlaw Distribution

+

Fracture radii are sampled from a truncated power-law distribution with lower bound r_0, upper bound r_u, and exponent \alpha defined by the following probability density function

+
+

\frac{\alpha}{r_0} \frac{(r/r_0)^{-1-\alpha}}{1 - (r_u/r_0)^{-\alpha}}.

+

+
+
ealpha
+

Description: Exponent of the truncated powerlaw distribution

+

Type: list of Positive floats. Length is the number of elements in edistr set to 2.

+

Example:

+
DFN.params['ealpha']['value'] = [1.6, 2.2]
+# TPL family 1 has an alpha of 1.6
+# TPL family 2 has an alpha of 2.2
+
+
+
+

Note

+

A value of 0 creates constant sized fractures of size emin

+
+
+
+
+
emin
+

Description: Lower cutoff of the truncated powerlaw distribution

+

Type: list of Positive floats. Length is the number of elements in edistr set to 2.

+

Example:

+
DFN.params['emin']['value'] = [1.2, 5]
+# TPL family 1 has an lower cutoff of 1.2 m
+# TPL family 2 has an lower cutoff of 5 m
+
+
+
+

Note

+

emin must be less than emax within each family.

+
+
+
+
+
emax
+

Description: Upper cutoff of the truncated powerlaw distribution

+

Type: list of Positive floats. Length is the number of elements in edistr set to 2.

+

Example:

+
DFN.params['emax']['value'] = [10, 50]
+# TPL family 1 has an upper cutoff of 10 m
+# TPL family 2 has an upper cutoff of 50 m
+
+
+
+

Note

+

emax must be greater than emin within each family.

+
+
+
+
+
+

Ellipse: Exponential Distribution

+

Fracture radii are sampled from a exponential distribution with the following probability density function

+
+

\lambda e^{-\lambda x}

+

Where \lambda is referred to as the rate parameter.

+
+
+
eExpMean
+

Description: Mean value of each exponential distribution

+

Type: list of Positive floats. Length is the number of elements in edistr set to 3.

+

Example:

+
DFN.params['eExpMean']['value'] = [10, 25]
+# Exponential family 1 has a mean value of 10 m
+# Exponential family 2 has a mean value of 25 m
+
+
+
+

Note

+

eExpMean equal to 1/\lambda where \lambda is the rate parameter of the distribution.

+
+
+
+
+
eExpMin
+

Description: Lower 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['eExpMin']['value'] = [1, 7]
+# Exponential family 1 has a lower cutoff value of 1 m
+# Exponential family 2 has a lower cutoff value of 7 m
+
+
+
+

Note

+

eExpMin must be less than eExpMax within each family.

+
+
+
+
+
eExpMax
+

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.

+
+
+
+
+
+

Ellipse: Constant

+

Constant sized fracture families are defined using a single parameter econst. These families are also referred to as uniform or mono-disperse.

+
+
+
econst
+

Description: Constant radius for each family

+

Type: list of Positive floats. Length is the number of elements in edistr set to 4.

+

Example:

+
DFN.params['econst']['value'] = [1, 7]
+# Constant family 1 has a x-radius of 1 m
+# Constant family 2 has a x-radius of 7 m
+
+
+
+
+
+
+
+
+

Fracture Family Generation Parameters: Rectangle

+

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

+
+
+

Rectangle: General Parameters

+
+

nFamRect

+

Description: Number of rectangle families

+

Type: Non-Negative Integer

+

Default: 0

+

Example:

+
DFN.params['nFamRect']['value'] = 3 # There will be 3 rectangle families
+
+
+
+
+
+

rLayer

+

Description: Assign each rectangle family to a layer in domain.

+

Type: list of nFamRect integers

+

Example:

+
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.

+
+
+
+
+

rRegion

+

Description: Assign each rectangle family to a region in domain.

+

Type: list of nFamRect integers

+

Example:

+
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.

+
+
+
+
+

r_p32Targets

+

Description: Target fracture intensity per family. Fracture intensity P_{32} [\text{m}^{-1}] 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.

+

Type: list of nFamRect floats

+

Example:

+
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
+
+
+
+

Note

+

Only used when stopCondition = 1

+
+
+

Warning

+

The fracture surface area is defined using both sides of a fracture.

+
+
+
+
+

raspect

+

Description: Aspect ratio of fractures

+

Type: list of nFamRect floats

+

Example:

+
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
+
+
+
+

Note

+

A value of 1 makes squares

+
+
+
+
+
+

Rectangle: Fracture Orientation

+

The fracture orientations are sampled from the three-dimensional von Mises-Fisher distribution,

+
+

f({\bf x}; {\boldsymbol \mu}, \kappa ) = \frac{ \kappa \exp( \kappa {\boldsymbol \mu}^{T} {\bf x} )}{ 4 \pi \sinh(\kappa)}~.

+

where {\boldsymbol \mu} is the mean orientation vector and T denotes transpose. +The distribution is sampled using the algorithm provided Simulation of the von Mises Fisher distribution Communications in statistics-simulation and computation 23.1 (1994): 157-164. by Andrew Wood..

+

dfnGen accepts spherical coordinates (\theta/\phi), 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.

+
+
0 : Spherical Coordinate (Theta/Phi)
+
1 : Trend / Plunge
+
2 : Dip / Strike.
+
+
+
+
+

rkappa

+

Description: The concentration parameter of the von Mises-Fisher distribution, which determines the degree of clustering around the mean orientation.

+

Type: list of nFamRect floats

+

Example:

+
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 \kappa 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.

+
+
+
+
+

Rectangle: Spherical Coordinates

+

The mean normal vector of the fracture family \vec{n} is related to the spherical coordinates \theta and \phi by

+
+

\vec{n}_x & = \sin(\theta) \cos(\phi)\\

+
+

\vec{n}_y & = \sin(\theta) \sin(\phi)\\

+
+

\vec{n}_z & = \cos(\theta)

+
+
rtheta
+

Description: Angle the normal vector of the fracture makes with the z-axis

+

Type: list of nFamRect floats

+

Example:

+
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 2 \pi.

+
+
+
+
+
rphi
+

Description: Angle that the projection of the normal vector of a fracture onto the x-y plane makes with the x-axis.

+

Type: list of nFamRect float

+

Example:

+
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 2 \pi.

+
+
+
+
+
+

Rectangle: Trend & Plunge

+

The mean normal vector of the fracture family \vec{n} is related to trend and plunge by

+
+

\vec{n}_x & = \cos(\text{trend}) \cos(\text{plunge})\\

+
+

\vec{n}_y & = \sin(\text{trend}) \cos(\text{plunge})\\

+
+

\vec{n}_z & = \sin(\text{trend})

+
+
rtrend
+

Description: Trend of fracture families

+

Type: list of nFamRect float

+

Example

+
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

+
+
+
+
+
rplunge
+

Description: Plunge of fracture families

+

Type: list of nFamRect float

+

Example

+
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

+
+
+
+
+
+

Rectangle: Dip & Strike

+

The mean normal vector of the fracture family \vec{n} is related to dip and strike by

+
+

\vec{n}_x & = \sin(\text{dip}) \sin(\text{strike})\\

+
+

\vec{n}_y & = -\sin(\text{dip}) \cos(\text{strike})\\

+
+

\vec{n}_z & = \cos(\text{dip})

+

+
+
rstrike
+

Description: Strike of fracture families

+

Type: list of nFamRect float

+

Example

+
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

+
+
+
+
+
rdip
+

Description: Dip of fracture families

+

Type: list of nFamRect float

+

Example

+
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

+
+
+
+
+
+

Rectangle: In Plane Rotation

+
+
rbetaDistribution
+

Description: Prescribe a rotation around each fracture’s normal vector, with the fracture centered on x-y plane at the origin

+

Type: list of nFamRect boolean values

+
+
0: Uniform distribution on [0, 2 \pi )
+
1: Constant rotation specified by rbeta
+
+
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
+
+
+
+
+
+
rbeta
+

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
+
+
+
+
+
+
+

Rectangle: Fracture Radius Distributions

+

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.

+
+
+

rdistr

+

Description: Assigns fracture radius distribution for each family

+

Type: list of nFamRect Integers (1,2,3,4)

+

Example:

+
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.

+
+
+
+

Rectangle: Lognormal Distribution

+

Fracture radii are sampled from a Lognormal distribution with the following probability density function

+
+

\frac{1}{x \sigma \sqrt{2\pi}} \exp \left (- \frac{(\ln x -\mu)^2}{2 \sigma^2} \right)

+

with mean \mu and variance \sigma^2.

+
+

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 (\mu) and variance (\sigma^2) one uses

+
+

eLogMean = \ln  \left ( \frac{\mu^2}{\sqrt{\mu^2 + \sigma^2}} \right)

+

and

+
+

esd =  \sqrt{\ln \left ( 1 + \frac{ \sigma^2}{\mu^2} \right )}

+

For more details see https://en.wikipedia.org/wiki/Log-normal_distribution.

+
+
+
rLogMean
+
+

Warning

+

This value is not the mean of the Log Normal distribution. Use the equations above to convert between the values if needed.

+
+

Description: Mean value of the underlying normal distribution

+

Type: list of float. Length is the number of elements in rdistr set to 1.

+

Example:

+
DFN.params['rLogMean']['value'] = [1.609] # This value along with that in esd produce a lognormal with mean 5 m and variance of 0.1
+
+
+
+
+
+
rsd
+
+

Warning

+

This value is not the standard deviation of the Log Normal distribution. Use the equations above to convert between the values if needed.

+
+

Description: Standard deviation value of the underlying normal distribution

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 1.

+

Example:

+
DFN.params['rsd']['value'] = [0.040] # This value along with that in rLogMean produce a lognormal with mean 5 m and variance of 0.1
+
+
+
+
+
+
rLogMin
+

Description: Minimum radius created by the LogNormal distribution for each family

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 1.

+

Example:

+
DFN.params['rLogMin']['value'] = [1,0.4]
+# Lognormal family 1 has a minimum radius of 1 m
+# Lognormal family 2 has a maximum radius of 0.4 m
+
+
+
+

Note

+

rLogMin must be less than rLogMax within each family.

+
+
+
+
rLogMax
+

Description: Maximum radius created by the LogNormal distribution for each family

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 1.

+

Example:

+
DFN.params['rLogMax']['value'] = [10,12]
+# Lognormal family 1 has a maximum radius of 10 m
+# Lognormal family 2 has a maximum radius of 12 m
+
+
+
+

Note

+

rLogMax must be greater than rLogMin within each family.

+
+
+
+
+
+

Rectangle: Truncated Powerlaw Distribution

+

Fracture radii are sampled from a truncated power-law distribution with lower bound r_0, upper bound r_u, and exponent \alpha defined by the following probability density function

+
+

\frac{\alpha}{r_0} \frac{(r/r_0)^{-1-\alpha}}{1 - (r_u/r_0)^{-\alpha}}.

+

+
+
ralpha
+

Description: Exponent of the truncated powerlaw distribution

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 2.

+

Example:

+
DFN.params['ralpha']['value'] = [1.6, 2.2]
+# TPL family 1 has an alpha of 1.6
+# TPL family 2 has an alpha of 2.2
+
+
+
+

Note

+

A value of 0 creates constant sized fractures of size rmin

+
+
+
+
+
rmin
+

Description: Lower cutoff of the truncated powerlaw distribution

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 2.

+

Example:

+
DFN.params['rmin']['value'] = [1.2, 5]
+# TPL family 1 has an lower cutoff of 1.2 m
+# TPL family 2 has an lower cutoff of 5 m
+
+
+
+

Note

+

rmin must be less than rmax within each family.

+
+
+
+
+
rmax
+

Description: Upper cutoff of the truncated powerlaw distribution

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 2.

+

Example:

+
DFN.params['rmax']['value'] = [10, 50]
+# TPL family 1 has an upper cutoff of 10 m
+# TPL family 2 has an upper cutoff of 50 m
+
+
+
+

Note

+

rmax must be greater than rmin within each family.

+
+
+
+
+
+

Rectangle: Exponential Distribution

+

Fracture radii are sampled from a exponential distribution with the following probability density function

+
+

\lambda e^{-\lambda x}

+

Where \lambda is referred to as the rate parameter.

+
+
+
rExpMean
+

Description: Mean value of each exponential distribution

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 3.

+

Example:

+
DFN.params['rExpMean']['value'] = [10, 25]
+# Exponential family 1 has a mean value of 10 m
+# Exponential family 2 has a mean value of 25 m
+
+
+
+

Note

+

rExpMean equal to 1/\lambda where \lambda is the rate parameter of the distribution.

+
+
+
+
+
rExpMin
+

Description: Lower 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['rExpMin']['value'] = [1, 7]
+# Exponential family 1 has a lower cutoff value of 1 m
+# Exponential family 2 has a lower cutoff value of 7 m
+
+
+
+

Note

+

rExpMin must be less than rExpMax within each family.

+
+
+
+
+
rExpMax
+

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.

+
+
+
+
+
+

Rectangle: Constant

+

Constant sized fracture families are defined using a single parameter rconst. These families are also referred to as uniform or mono-disperse.

+
+
+
rconst
+

Description: Constant radius for each family

+

Type: list of Positive floats. Length is the number of elements in rdistr set to 4.

+

Example:

+
DFN.params['rconst']['value'] = [1, 7]
+# Constant family 1 has a x-radius of 1 m
+# Constant family 2 has a x-radius of 7 m
+
+
+
+
+
+
+
+
+

User Defined Fracture Generation Parameters

+

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.

+
+
+

User Defined Ellipses

+

Ellipses can be included using two different formats: general or coordinates.

+
+

userEllipsesOnOff

+

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.

+

Type: boolean (0/1)

+
+
0: Do not include general user defined ellipses
+
1: Do include general user defined ellipses
+
+
+
+
+

UserEll_Input_File_Path

+

Description: Filepath for general user defined ellipses.

+

Type: string

+

Example:

+
DFN.params['UserEll_Input_Filepath']['value'] = '/dfnWorks/examples/4_user_ell_uniform/define_4_user_ellipses.dat'
+
+
+
+
+
+

General user defined ellipses parameters

+

Below are the required parameters for the general user defined ellipses

+
+
nUserEll
+

Description: Number of User Defined Ellipses

+

Type: Int

+

Example:

+
nUserEll: 2 // 2 ellipses are expected in the file
+
+
+
+
+
+
Number_of_Vertices
+

Description: Number of vertices defining the boundary of each ellipse. One per line per fracture.

+

Type: Int

+

Example:

+
Number_of_Vertices:
+12 // fracture 1 has 12 vertices
+8 // fracture 2 has 8 vertices
+
+
+
+
+
+
Radii
+

Description: Radius of each ellipse

+

Type: Double, one value per line per fracture

+

Example:

+
Radii:
+0.5 // fracture 1 has a radius of 0.5 m
+1 // fracture 1 has a radius of 1 m
+
+
+
+
+
+
Aspect_Ratio
+

Description: Aspect Ratio for each ellipse

+

Type: Double, one value per line per fracture

+

Example:

+
Aspect_Ratio:
+1 // fracture 1 has a radius of 1
+2 // fracture 2 has a radius of 2
+
+
+
+
+
+
AngleOption (User Defined Fracture)
+

Description: Selection of Radius / Degrees

+

Type: boolean

+
+
0: All angles in radians
+
1: All angles in degrees
+
+

Example:

+
AngleOption:
+0 // fracture 1 has angles in radians
+1 // fracture 2 has angles in degrees
+
+
+
+
+
+
Beta
+

Description: Rotation around center for each ellipse (one per line)

+

Type: Double, one value per line per fracture

+

Example:

+
AngleOption:
+0 // no rotation for fracture 1
+45 // rotate fracture 2 by 45 degrees
+
+
+
+
+
+
Translation
+

Description: Translation of each ellipse according to its center {x,y,z} (one per line)

+

Type: Set of double values {x,y,z}

+

Example:

+
Translation:
+{-0.2,0,0} // Fracture 1 has a center at -0.2, 0, 0
+{0,1,0} // Fracture 2 has a center at 0, 1, 0
+
+
+
+
+
+
userOrientationOption
+

Description: Selection of fracture orientation definition. The same orientation option must be used for all fractures.

+
+
+
0 : Normal Vector -> Normal {x,y,z}
+
1 : Trend / Plunge
+
2 : Dip / Strike
+
+
+

Type: Value of 0,1,2

+

Example:

+
userOrientationOption: 0 // Fracture orientation will be defined using normal vectors
+
+
+
+

Note

+
+
If option 0 is selected, the keyword normal is required.
+
If option 1 is selected, the keyword Trend_Plunge is required.
+
If option 2 is selected, the keyword Dip_Strike is required.
+
+
+
+

Warning

+

You cannot mix orientation options

+
+
+
+
+
Normal
+

Description: Normal vector of each ellipse according {x,y,z} (one per line)

+

Type: Set of double values {x,y,z}

+

Example:

+
Normal:
+{0,0,1} // Fracture 1 has a normal vector of [0,0,1]
+{1,1,0} // Fracture 1 has a normal vector of [1,1,0]
+
+
+
+

Note

+

Vectors do not need to be normalized

+
+
+
+
+
Trend_Plunge
+

Description: Trend and plunge for each fracture

+

Type: Set of double values {trend,plunge}

+

Example:

+
Trend_Plunge:
+{45, 90} // Fracture 1 has a trend of 45 and plunge of 90
+{0, 37} // Fracture 2 has a trend of 0 and plunge of 37
+
+
+
+

Warning

+

AngleOption (User Defined Fracture) must be set to degrees

+
+
+
+
+
Dip_Strike
+

Description: Dip and Strike for each fracture

+

Type: Set of double values {dip,strike}

+

Example:

+
Trend_Plunge:
+{45, 90} // Fracture 1 has a dip of 45 and strike of 90
+{0, 37} // Fracture 2 has a dip of 0 and strike of 37
+
+
+
+

Warning

+

AngleOption (User Defined Fracture) must be set to degrees

+
+
+
+
+
General Ellipse Input Example
+
/************************* 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
+
+
+
+
+
+
+

userEllByCoord

+

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.

+
+
+
+
+

EllByCoord_Input_File_Path

+

Description: File path name for user defined ellipses by coordinate

+

Type: string

+

Example:

+
DFN.params['EllByCoord_Input_File_Path']['value'] = '/dfnWorks/example/4_user_ell/ellCoords.dat'
+
+
+
+
+
+

User defined ellipses by coordinate parameters

+

Below are the required parameters for the user defined ellipses by coordinate

+
+
+
nEllipses
+

Description: Number of User Defined Ellipses

+

Type: Integer

+

Example:

+
nEllipses: 2
+
+
+
+
+
+
nNodes
+

Description: Number of nodes for all ellipses

+

Type: Integer

+
nNodes: 5
+
+
+
+
+
+
Coordinates
+

Description: Coordinates / Vertices for each ellipse.

+

Type: Set of nNodes triples {x_0, y_0, z_0} {x_1, y_1, z_1} \ldots {x_n, y_n, z_n}

+
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}
+
+
+
+

Warning

+

Coordinates must be listed in clockwise, or counterclockwise order. Coordinates must be co-planar

+
+
+
+
+
Ellipse By Coordinate Example
+
/************************************************************************/
+/*                 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}
+
+
+
+
+
+
+

User Defined Rectangles

+

Deterministic Rectangles can be included using two different formats: general or coordinates.

+
+

userRectanglesOnOff

+

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.

+

Type: boolean (0/1)

+
+
0: Do not include general user defined rectangles
+
1: Do include general user defined rectangles
+
+
+
+
+

UserRect_Input_File_Path

+

Description: Filepath for general user defined rectangles

+

Type: string

+

Example:

+
DFN.params['UserRect_Input_File_Path']['value'] = '/dfnWorks/examples/4_user_rects/define_4_user_rects.dat'
+
+
+
+
+
+

General user defined rectangles parameters

+

Below are the required parameters for the general user defined rectangles

+
+
+
nUserRect
+

Description: Number of user defined rectangles

+

Type: Int

+

Example:

+
nUserRect: 2 // There will be 2 rectangles expected.
+
+
+
+

Additional parameters have the same definitions as for user defined ellipses:

+ +
+
+
+
General Rectangle Input Example
+
/************************* 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}
+
+
+
+
+
+

userRecByCoord

+

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.

+
+
+
+
+

RectByCoord_Input_File_Path

+

Description: File path name for user defined rectangles by coordinate

+

Type: string

+

Example:

+
DFN.params['RectByCoord_Input_File_Path']['value'] = '/dfnWorks/example/4_user_rect/rectCoords.dat'
+
+
+
+
+
+

User defined rectangles by coordinate parameters

+

Below are the required parameters for the user defined rectangles by coordinate

+
+
+
nRectangles
+

Description: Number of user defined rectangles

+

Type: Integer

+

Example:

+
nRectangles: 2
+
+
+
+

Fracture coordinates are defined using the same method as for ellipses. See Coordinates.

+
+
+
Rectangle By Coordinate Example
+
/************************************************************************/
+/*                 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}
+
+
+
+
+
+
+

Polygons

+

An example DFN for this input is found in dfnWorks-main/examples/user_polygons.

+
+
+

userPolygonByCoord

+

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.
+
+
+
+
+
+

PolygonByCoord_Input_File_Path:

+

Description: File path for user defined polygons

+

Type: string

+

Example:

+
DFN.params['UserEll_Input_File_Path']['value'] = '/dfnWorks/examples/user_polygons/polygons.dat'
+
+
+
+
+
+

Polygon file format

+

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
+
+
+

Example generated by dfnGen:

+
nPolygons: 13
+5 {0.614809755508, -5, 10} {0.215302589745, -5, 5.14052566974} {0.545132019741, -3.76296408041, 5.01135305297} {1.46205561432, -1.11169723114, 7.28911258873} {1.7422671348, -0.903335314403, 10}
+5 {-5, 5, -7.34275683413} {-5, -3.19676496723, -8.52840001844} {-2.54841054822, -3.3215018603, -8.70265907788} {2.28455866847, 0.968743065242, -8.39004354883} {2.5300049043, 5, -7.82257143457}
+7 {0.169537908304, -1.10365780918, 7.89449659536} {3.39229757501, -1.47097810869, 9.7368040684} {5, -0.396173810104, 9.9199696155} {5, 5, 6.76351044547} {0.192549006049, 5, 4.33581920939} {-0.157462422292, 4.76600532497, 4.29594241204} {-1.30082072643, 1.47978607543, 5.64081751301}
+6 {-0.793278268371, 5, 1.81778296407} {-0.561983329033, 3.45819042638, 3.44636375172} {1.4541413343, -3.51825209439, 3.66719739398} {2.0912558548, -5, 2.1739128129} {3.74246641127, -5, -10} {0.809628560699, 5, -10}
+8 {-3.56729801834, -4.10664963232, -7.17895163585} {-2.75834169562, -5, -7.23742727251} {2.80010131269, -5, -7.26666449977} {3.40228077595, -4.45665913893, -7.23685468994} {3.54844410108, -1.5749811435, -7.06272440502} {1.61045433532, 0.565188057474, -6.92263630123} {-1.27643915621, 0.710166484812, -6.89865208954} {-3.42113429321, -1.22497133918, -7.00482133497}
+6 {1.61970921408, 0.322281059007, 8.27249031626} {1.91468659533, -0.549550927249, 10} {2.19700291895, -5, 10} {1.3154355331, -5, 3.64584005023} {1.20544838483, -3.98437001886, 3.31745142962} {1.22589362057, -0.804792681255, 4.91861492809}
+8 {-4.70593999999, -3.29639431251, -7.25224389054} {-3.10094407568, -4.6562865987, -7.03245087974} {-0.992518232701, -4.50612429324, -7.10741842947} {0.384249987443, -2.93387033715, -7.43323157864} {0.222868762162, -0.860529584164, -7.81903344627} {-1.38212753352, 0.499362646346, -8.03882643837} {-3.4905528176, 0.349200223043, -7.9638588789} {-4.8673209588, -1.22305329999, -7.63804581282}
+6 {5, 5, -3.39564264412} {2.56312416852, 5, -4.08462835745} {2.41898893677, 4.65124788861, -4.11909954775} {3.10520773081, 2.8645938936, -3.89290711889} {4.80872562941, 2.11160312936, -3.39770554375} {5, 2.19173173493, -3.34506895229}
+6 {-5, 2.42895858094, 6.20485792137} {-5, -5, 4.85177752427} {-1.81351205493, -5, 4.99702019362} {0.0853427250495, -2.66597219167, 5.50868187563} {-0.289753327606, 0.588931258258, 6.08441961571} {-2.88990150472, 2.64805531643, 6.34094328974}
+8 {-0.922507014307, -1.71519798881, 2.85778630995} {-0.841071899053, 0.310044679731, 1.25820816606} {-1.26426496474, 2.84566856205, 1.49988903618} {-1.94418542615, 4.40633923745, 3.44125563949} {-2.48254521057, 4.07783735953, 5.94508200102} {-2.5639802392, 2.05259419185, 7.54466008318} {-2.14078724661, -0.483028953749, 7.30297907019} {-1.4608669326, -2.04369954706, 5.36161304612}
+8 {2.18143095055, -0.610312831185, -4.21771261931} {2.80909183961, 0.814183043594, -4.47247873318} {2.26469088948, 2.29432736239, -4.44327042469} {0.867130728085, 2.96307141873, -4.14719749191} {-0.564917036364, 2.42867426252, -3.75769541671} {-1.19257780773, 1.00417817281, -3.50292931657} {-0.648176891617, -0.475965761662, -3.53213764385} {0.74938298608, -1.14470985076, -3.82821050463}
+6 {-1.27165416308, 5, 10} {-1.97370232204, 1.13046740212, 10} {-2.01807341608, 0.962283071202, 9.35725325459} {-1.83562647916, 2.23286105726, 7.12746635144} {-1.40616097581, 4.67958113114, 6.45759785589} {-1.34398137987, 5, 6.64526675756}
+
+
+
+
+
nPolygons
+

Description: Number of polgyons/fractures

+

Type: integer

+

Example:

+
nPolygons: 3 // Read in three polygons/fractures
+
+
+
+
+
+
+
+

Source Code Documentation (Doxygen)

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/dfntrans.html b/docs/dfntrans.html new file mode 100644 index 00000000..214a7fc1 --- /dev/null +++ b/docs/dfntrans.html @@ -0,0 +1,160 @@ + + + + + + + dfnTrans — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/examples.html b/docs/examples.html new file mode 100644 index 00000000..b49db5b1 --- /dev/null +++ b/docs/examples.html @@ -0,0 +1,202 @@ + + + + + + + Examples — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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.

+
+alternate text +
+

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.

+
+alternate text +
+

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

+
+alternate text +
+

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.

+
+alternate text +
+
+
+

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.

+
+alternate text +
+

(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.

+
+
+
+alternate text +
+

(left) Original DFN (right) DFN corresponding to the 2-core of the DFN to the left.

+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/gallery.html b/docs/gallery.html new file mode 100644 index 00000000..2f62466e --- /dev/null +++ b/docs/gallery.html @@ -0,0 +1,161 @@ + + + + + + + dfnWorks Gallery — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ + + + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/genindex.html b/docs/genindex.html new file mode 100644 index 00000000..6d114127 --- /dev/null +++ b/docs/genindex.html @@ -0,0 +1,619 @@ + + + + + + Index — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Index

+ +
+ A + | C + | D + | E + | F + | G + | I + | K + | L + | M + | O + | P + | R + | S + | T + | U + | W + | Z + +
+

A

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + +
+ +

F

+ + + +
    +
  • + false_connections.py + +
  • +
+ +

G

+ + + +
+ +

I

+ + + +
+ +

K

+ + +
+ +

L

+ + + +
+ +

M

+ + +
+ +

O

+ + +
+ +

P

+ + + +
    +
  • + pydfnworks.dfnGen.meshing.add_attribute_to_mesh + +
  • +
  • + pydfnworks.dfnGen.meshing.mesh_dfn + +
  • +
  • + pydfnworks.dfnGen.meshing.mesh_dfn_helper + +
  • +
  • + pydfnworks.dfnGen.meshing.udfm.false_connections + +
  • +
  • + pydfnworks.dfnGen.meshing.udfm.map2continuum + +
  • +
  • + pydfnworks.dfnGen.meshing.udfm.upscale + +
  • +
  • + pydfnworks.dfnGen.well_package.wells + +
  • +
  • + pydfnworks.dfnGraph.dfn2graph + +
  • +
  • + pydfnworks.dfnGraph.graph_flow + +
  • +
  • + pydfnworks.dfnGraph.graph_transport + +
  • +
  • + pydfnworks.dfnGraph.pruning + +
  • +
  • + pydfnworks.dfnTrans.transport + +
  • +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + +
+ +

U

+ + + +
    +
  • + upscale.py + +
  • +
+ +

W

+ + +
+ +

Z

+ + +
+ + + +
+
+
+ +
+ +
+

© Copyright 2020, LANL, LA-UR-17-22216.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/index_docs.html b/docs/index_docs.html new file mode 100644 index 00000000..f44b2793 --- /dev/null +++ b/docs/index_docs.html @@ -0,0 +1,365 @@ + + + + + + + Welcome to dfnWorks 2.7 documentation! — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Welcome to dfnWorks 2.7 documentation!

+

Contents:

+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/intro.html b/docs/intro.html new file mode 100644 index 00000000..77962f81 --- /dev/null +++ b/docs/intro.html @@ -0,0 +1,373 @@ + + + + + + + Welcome To dfnWorks — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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.

+

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://hub.docker.com/r/ees16/dfnworks

+

dfnWorks can be downloaded from https://github.com/lanl/dfnWorks/

+

v1.0 can be downloaded from https://github.com/dfnWorks/dfnWorks-Version1.0

+
+
+

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:

+
  @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.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: 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 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

  • +
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/objects.inv b/docs/objects.inv new file mode 100644 index 00000000..4d57cc0b Binary files /dev/null and b/docs/objects.inv differ diff --git a/docs/output.html b/docs/output.html new file mode 100644 index 00000000..d144c8c8 --- /dev/null +++ b/docs/output.html @@ -0,0 +1,241 @@ + + + + + + + Run Files — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Run Files

+

This section describes the contents and purpose of each file used in dfnWorks and their locations.

+
+

dfnGen - output

+

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:

+

Detailed information about fracture network. Output by DFNGen.

+

families.dat:

+

Information about fracture families. Produced by DFNGen.

+

input_generator.dat:

+

Input file for DFN generator.

+

input_generator_clean.dat:

+

Abbreviated input file for DFN generator.

+

normal_vectors.dat:

+

Normal vector of each fracture in the network.

+

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:

+

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 defined rectangle file.

+

radii.dat:

+

Concatentate file of fracture radii. Contains fractures that are removed due to isolation.

+

radii_Final.dat:

+

Concatentated file of final radii in the DFN.

+

rejections.dat:

+

Summary of rejection reasons.

+

rejectsPerAttempt.dat:

+

Number of rejections per attempted fracture.

+

translations.dat:

+

Fracture centriods.

+

triple_points.dat:

+

x,y,z location of triple intersection points.

+

warningFileDFNGen.txt:

+

Warning file output by DFNGen.

+

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:

+

LaGriT run file to identify boundary nodes. Dumps zone files.

+

boundary_output.txt:

+

Output file from bound_zones.lgi.

+

finalmesh.txt:

+

Brief summary of final mesh.

+

full_mesh.inp:

+

Full DFN mesh in AVS format.

+

full_mesh.lg:

+

Full DFN mesh in LaGriT binary format.

+

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:

+

intersections:

+

Directory containing intersection avs files output by the generator and used by LaGrit.

+

lagrit_logs:

+

Directory of output files from individual meshing.

+

logx3dgen:

+

LaGriT output.

+

outx3dgen:

+

LaGriT output.

+

parameters:

+

Directory of parameter*.mgli files used for fracture meshing.

+

polys:

+

Subdirectory contiaining AVS file for polygon boundaries.

+

tri_fracture.stor:

+

FEHM stor file. Information about cell volume and area.

+

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:

+

Mesh information output by PFLOTRAN.

+

dfn_explicit-000.vtk:

+

VTK file of initial conditions of PFLOTRAN. Mesh is not included in this file.

+

dfn_explicit-001.vtk:

+

VTK file of steady-state solution of PFLOTRAN. Mesh is not included in this file.

+

dfn_explicit-mas.dat:

+

pflotran information file.

+

dfn_explicit.in:

+

pflotran input file.

+

_dfn_explicit.out:

+

pflotran output file.

+

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 DFN in uge format. Volumes and areas have been corrected.

+

materialid.dat:

+

Material ID (Fracture Number) for every node in the mesh.

+

parsed_vtk:

+

Directory of pflotran results.

+

perm.dat:

+

Fracture permeabilities in FEHM format. Each fracture is listed as a zone, starting index at 7.

+

pboundary_back_n.ex:

+

Boundary file for back of the domain used by PFLOTRAN.

+

pboundary_bottom.ex:

+

Boundary file for bottom of the domain used by PFLOTRAN.

+

pboundary_front_s.ex:

+

Boundary file for front of the domain used by PFLOTRAN.

+

pboundary_left_w.ex:

+

Boundary file for left side of the domain used by PFLOTRAN.

+

pboundary_right_e.ex:

+

Boundary file for right of the domain used by PFLOTRAN.

+

pboundary_top.ex:

+

Boundary file for top of the domain used by PFLOTRAN.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/publications.html b/docs/publications.html new file mode 100644 index 00000000..02f8bdb6 --- /dev/null +++ b/docs/publications.html @@ -0,0 +1,188 @@ + + + + + + + dfnWorks Publications — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

dfnWorks Publications

+

The following are publications that use dfnWorks:

+
    +
  1. 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).

  2. +
  3. 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).

  4. +
  5. 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).

  6. +
  7. 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).

  8. +
  9. 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).

  10. +
  11. 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).

  12. +
  13. 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).

  14. +
  15. 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).

  16. +
  17. 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).

  18. +
  19. 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).

  20. +
  21. 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).

  22. +
  23. 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).

  24. +
  25. 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).

  26. +
  27. 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.

  28. +
  29. 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.

  30. +
  31. 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.

  32. +
  33. 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).

  34. +
  35. 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).

  36. +
  37. 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).

  38. +
  39. 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).

  40. +
  41. 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).

  42. +
  43. 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).

  44. +
  45. 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).

  46. +
  47. 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).

  48. +
  49. 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.

  50. +
  51. 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).

  52. +
  53. 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).

  54. +
  55. 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).

  56. +
  57. 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).

  58. +
  59. 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).

  60. +
  61. 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).

  62. +
  63. 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).

  64. +
  65. 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).

  66. +
  67. 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).

  68. +
  69. 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).

  70. +
  71. 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).

  72. +
  73. 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).

  74. +
  75. 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).

  76. +
  77. 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).

  78. +
  79. 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).

  80. +
  81. 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).

  82. +
  83. 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)..

  84. +
  85. 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).

  86. +
  87. 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).

  88. +
  89. J.D. Hyman. Flow Channeling in Fracture Networks: Characterizing the Effect of Density on Preferential Flow Path Formation. Water Resources Research (2020): e2020WR027986..

  90. +
  91. 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)..

  92. +
  93. 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..

  94. +
  95. 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).

  96. +
  97. 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).

  98. +
  99. Hyman, J. D., & Dentz, M. Transport upscaling under flow heterogeneity and matrix-diffusion in three-dimensional discrete fracture networks. Advances in Water Resources (2021).

  100. +
  101. 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).

  102. +
  103. 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.

  104. +
  105. 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).

  106. +
  107. Liangchao Zou and Vladimir Cvetkovic. Evaluation of Flow‐Log Data From Crystalline Rocks With Steady‐State Pumping and Ambient Flow. Geophysical Research Letters (2021).

  108. +
  109. 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).

  110. +
  111. 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).

  112. +
  113. C. R. Romano, R. T. Williams; Evolution of Fault-Zone Hydromechanical Properties in Response to Different Cementation Processes. Lithosphere (2022).

  114. +
  115. 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).

  116. +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/py-modindex.html b/docs/py-modindex.html new file mode 100644 index 00000000..bdb35004 --- /dev/null +++ b/docs/py-modindex.html @@ -0,0 +1,325 @@ + + + + + + Python Module Index — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Python Module Index

+ +
+ f | + g | + m | + p | + u +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ f
+ false_connections +
    + false_connections.py + Checks for false connections between fractures in upscaled mesh
 
+ g
+ graph_transport +
    + graph_transport.py + simulate transport on a pipe network representaiton of a DFN
 
+ m
+ map2continuum +
    + map2continuum.py + Produce octree-refined continuum mesh replacing dfn
+ mesh_dfn +
    + mesh_dfn.py + meshing driver for DFN
+ mesh_dfn_helper +
    + mesh_dfn_helper.py + helper functions for meshing DFN using LaGriT
 
+ p
+ pydfnworks +
    + pydfnworks.dfnFlow.fehm +
    + pydfnworks.dfnFlow.flow +
    + pydfnworks.dfnFlow.mass_balance +
    + pydfnworks.dfnFlow.pflotran +
    + pydfnworks.dfnGen.generation.generator +
    + pydfnworks.dfnGen.generation.input_checking +
    + pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions +
    + pydfnworks.dfnGen.generation.output_report.gen_output +
    + pydfnworks.dfnGen.generation.stress +
    + pydfnworks.dfnGen.meshing.add_attribute_to_mesh +
    + pydfnworks.dfnGen.meshing.mesh_dfn +
    + pydfnworks.dfnGen.meshing.mesh_dfn_helper +
    + pydfnworks.dfnGen.meshing.udfm.false_connections +
    + pydfnworks.dfnGen.meshing.udfm.map2continuum +
    + pydfnworks.dfnGen.meshing.udfm.upscale +
    + pydfnworks.dfnGen.well_package.wells +
    + pydfnworks.dfnGraph.dfn2graph +
    + pydfnworks.dfnGraph.graph_flow +
    + pydfnworks.dfnGraph.graph_transport +
    + pydfnworks.dfnGraph.pruning +
    + pydfnworks.dfnTrans.transport +
 
+ u
+ upscale +
    + upscale.py + Generates PFLOTRAN or FEHM input from octree refined continuum mesh
+ + +
+
+
+ +
+ +
+

© Copyright 2020, LANL, LA-UR-17-22216.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/pydfnFlow.html b/docs/pydfnFlow.html new file mode 100644 index 00000000..523487bf --- /dev/null +++ b/docs/pydfnFlow.html @@ -0,0 +1,392 @@ + + + + + + + pydfnworks: dfnFlow — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

pydfnworks: dfnFlow

+

DFN Class functions used in flow simulations (PFLOTRAN and FEHM)

+
+

Running Flow : General

+
+ +

Create symlinks to files required to run dfnFlow that are in another directory.

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • path (string) – Absolute path to primary directory.

  • +
+
+
Return type:
+

None

+
+
+

Notes

+
    +
  1. Typically, the path is DFN.path, which is set by the command line argument -path

  2. +
  3. Currently only supported for PFLOTRAN

  4. +
+
+ +
+
+dfn_flow(self, dump_vtk=True)[source]
+

Run the dfnFlow portion of the workflow

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • dump_vtk (bool) – True - Write out vtk files for flow solutions +False - Does not write out vtk files for flow solutions

  • +
+
+
+

Notes

+

Information on individual functions is found therein

+
+ +
+
+set_flow_solver(self, flow_solver)[source]
+

Sets flow solver to be used

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • flow_solver (string) – Name of flow solver. Currently supported flow sovlers are FEHM and PFLOTRAN

  • +
+
+
+

Notes

+

Default is PFLOTRAN

+
+ +
+
+

Running Flow: PFLOTRAN

+

functions for using pflotran in dfnworks

+
+
+lagrit2pflotran(self)[source]
+

Takes output from LaGriT and processes it for use in PFLOTRAN. +Calls the function write_perms_and_correct_volumes_areas() and zone2ex

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+

Notes

+

None

+
+ +
+
+parse_pflotran_vtk_python(self, grid_vtk_file='')[source]
+

Adds CELL_DATA to POINT_DATA in the VTK output from PFLOTRAN. +:param self: DFN Class +:type self: object +:param grid_vtk_file: Name of vtk file with mesh. Typically local_dfnFlow_file.vtk +:type grid_vtk_file: string

+
+
Return type:
+

None

+
+
+

Notes

+

If DFN class does not have a vtk file, inp2vtk_python is called

+
+ +
+
+pflotran(self, transient=False, restart=False, restart_file='')[source]
+

Run PFLOTRAN. Copy PFLOTRAN run file into working directory and run with ncpus

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • transient (bool) – Boolean if PFLOTRAN is running in transient mode

  • +
  • restart (bool) – Boolean if PFLOTRAN is restarting from checkpoint

  • +
  • restart_file (string) – Filename of restart file

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Runs PFLOTRAN Executable, see http://www.pflotran.org/ for details on PFLOTRAN input cards

+
+ +
+
+pflotran_cleanup(self, index_start=0, index_finish=1, filename='')[source]
+

Concatenate PFLOTRAN output files and then delete them

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • index (int) – If PFLOTRAN has multiple dumps use this to pick which dump is put into cellinfo.dat and darcyvel.dat

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Can be run in a loop over all pflotran dumps

+
+ +
+
+write_perms_and_correct_volumes_areas(self)[source]
+

Write permeability values to perm_file, write aperture values to aper_file, and correct volume areas in uge_file

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+

Notes

+

Calls executable correct_uge

+
+ +
+
+zone2ex(self, zone_file='', face='', boundary_cell_area=0.1)[source]
+

Convert zone files from LaGriT into ex format for LaGriT

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • zone_file (string) – Name of zone file

  • +
  • Face (Face of the plane corresponding to the zone file) –

  • +
  • zone_file – Name of zone file to work on. Can be ‘all’ processes all directions, top, bottom, left, right, front, back

  • +
  • boundary_cell_area (double) – should be a large value relative to the mesh size to force pressure boundary conditions.

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

the boundary_cell_area should be a function of h, the mesh resolution

+
+ +
+
+

Running Flow: FEHM

+
+
+correct_stor_file(self)[source]
+

Corrects volumes in stor file to account for apertures

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+

Notes

+

Currently does not work with cell based aperture

+
+ +
+
+fehm(self)[source]
+

Run FEHM

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+

Notes

+

See https://fehm.lanl.gov/ for details about FEHM

+
+ +
+
+

Processing Flow

+
+
+effective_perm(self, inflow_pressure, outflow_pressure, boundary_file, direction)[source]
+

Computes the effective permeability of a DFN in the primary direction of flow using a steady-state PFLOTRAN solution.

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • inflow_pressure (float) – Pressure at the inflow boundary face. Units are Pascal

  • +
  • outflow_pressure (float) – Pressure at the outflow boundary face. Units are Pascal

  • +
  • boundary_file (string) – Name of inflow boundary file, e.g., pboundary_left.ex

  • +
  • direction (string) – Primary direction of flow, x, y, or z

  • +
+
+
Return type:
+

None

+
+
+

Notes

+
    +
  1. Information is written to screen and to the file self.local_jobname_effective_perm.txt

  2. +
  3. Currently, only PFLOTRAN solutions are supported

  4. +
  5. Assumes density of water at 20c

  6. +
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/pydfnGen.html b/docs/pydfnGen.html new file mode 100644 index 00000000..8e101917 --- /dev/null +++ b/docs/pydfnGen.html @@ -0,0 +1,863 @@ + + + + + + + pydfnworks: dfnGen — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

pydfnworks: dfnGen

+

DFN Class functions used in network generation and meshing

+
+

dfnGen

+
+

Adding Fracture Families

+
+
+add_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)[source]
+

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}

  • +
+
+
Return type:
+

populated fracture family dictionary for specified family

+
+
+

Notes

+

See https://dfnworks.lanl.gov/dfngen.html#domain-parameters for more +information about parameters

+
+ +

Example:

+
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

+

Created on Mon Aug 29 09:17:14 2022

+

@author: 369984

+
+
+add_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)[source]
+

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

  • +
+
+
Return type:
+

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

+
+ +

Example:

+
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

+

Created on Mon Aug 29 09:17:14 2022

+

@author: 369984

+
+
+add_user_fract_from_file(self, filename, shape, nPolygons, by_coord=False, aperture=None, transmissivity=None, permeability=None)[source]
+

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)

+
+

Paramters

+
+
+
filenamestring

path to source file

+
+
shape: string

The shape of the fracture options are ‘rect’, ‘ell’, and ‘poly’ - Required

+
+
by_coordboolean

True / False of file format for coordinate or general input

+
+
nPolygonsint

The number of polygons specified in the file

+
+
permeabilitylist or array

Permeabilities of the fractures

+
+
transmissivitylist or array

Fracture Tramsmissivities

+
+
aperturelist or array

Hydraulic apertures of the fracture

+
+
+
+
+
rtype:
+

None

+
+
+

Notes

+

Does not write the file, only sets up paths

+

~/src/dfnworks-aidan/pydfnworks/pydfnworks/

+
+
+ +

Example:

+
DFN.add_user_fract_from_file(shape="poly",
+               filename = f'{src_path}/polygons.dat',
+               permeability = 1e-12)
+
+
+
+ +
+

Processing Generator Input

+
+
+check_input(self, from_file=False)[source]
+

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) –

+
+
Return type:
+

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.

+
+ +
+
+

Running the Generator

+
+
+create_network(self)[source]
+

Execute dfnGen

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

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.

+
+ +
+
+dfn_gen(self, output=True)[source]
+
+
Wrapper script the runs the dfnGen workflow:
    +
  1. make_working_directory: Create a directory with name of job

  2. +
  3. check_input: Check input parameters and create a clean version of the input file

  4. +
  5. create_network: Create network. DFNGEN v2.0 is called and creates the network

  6. +
  7. output_report: Generate a PDF summary of the DFN generation

  8. +
  9. mesh_network: calls module dfnGen_meshing and runs LaGriT to mesh the DFN

  10. +
+
+
+
+
Parameters:
+
    +
  • self (object) – DFN Class 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

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Details of each portion of the routine are in those sections

+
+ +
+
+grab_polygon_data(self)[source]
+

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) –

+
+
Return type:
+

None

+
+
+

Notes

+

None

+
+ +
+
+make_working_directory(self, delete=False)[source]
+

Make working directory for dfnWorks Simulation

+
+
Parameters:
+

self (object) – DFN Class object

+
+
Return type:
+

None

+
+
+

Notes

+

If directory already exists, user is prompted if they want to overwrite and proceed. If not, program exits.

+
+ +
+
+

Analysis of Generated DFN

+
+
filename:
+

gen_output.py

+
+
synopsis:
+

Main driver for dfnGen output report

+
+
version:
+

1.0

+
+
maintainer:
+

Jeffrey Hyman

+
+
moduleauthor:
+

Jeffrey Hyman <jhyman@lanl.gov>

+
+
+
+
+output_report(self, verbose=True, output_dir='dfnGen_output_report')[source]
+

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

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Final output report is named “jobname”_output_report.pdf +User defined fractures (ellipses, rectangles, and polygons) are not supported at this time.

+
+ +
+
+

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 b, permeability, k, or transmissivity 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

+
+

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

+
+

\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 \sigma and the stochastic term is an independent identically distributed random variable sampled from a normal distribution with mean 0 and variance 1, \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 \mu and variance \sigma,

+
+

\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

+
+

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

+
+
+stress_based_apertures(self, sigma_mat, friction_angle=25.0, dilation_angle=5, critical_shear_displacement=0.003, shear_modulus=10000000000.0, min_b=1e-10, shear_stiffness=400000000000.0)[source]
+

Takes stress tensor as input (defined in dfn run file) and calculates new apertures based on Bandis equations. New aperture and permeability values are written to files.

+
+
Parameters:
+
    +
  • sigma_mat (array) – 3 x 3 stress tensor (units in Pa)

  • +
  • friction_angle (float) – Friction angle (Degrees)

  • +
  • dilation_angle (float) – Dilation angle (Degrees)

  • +
  • critical_shear_displacement (float) – Critical shear displacement

  • +
  • shear_modulus (float) – Shear modulus (Pa)

  • +
  • min_b (float) – Minimum aperture (m)

  • +
  • shear_stiffness (float) – Shear stiffness (Pa/m)

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

For details of implementation see

+

“Sweeney, Matthew Ryan, and J. D. Hyman. “Stress effects on flow and transport in three‐dimensional fracture networks.” Journal of Geophysical Research: Solid Earth 125.8 (2020): e2020JB019754.”

+

and

+

Baghbanan, Alireza, and Lanru Jing. “Stress effects on permeability in a fractured rock mass with correlated fracture length and aperture.” International journal of rock mechanics and mining sciences 45.8 (2008): 1320-1334.

+

and

+

Zhao, Zhihong, et al. “Impact of stress on solute transport in a fracture network: A comparison study.” Journal of Rock Mechanics and Geotechnical Engineering 5.2 (2013): 110-123.

+
+ +
+
+
+

Meshing - LaGriT

+
+

Primary DFN meshing driver

+
+
+mesh_network(self, prune=False, uniform_mesh=False, production_mode=True, coarse_factor=8, slope=0.1, min_dist=1, max_dist=40, concurrent_samples=10, grid_size=10, well_flag=False)[source]
+

Mesh fracture network using LaGriT

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • prune (bool) – If prune is False, mesh entire network. If prune is True, mesh only fractures in self.prune_file

  • +
  • uniform_mesh (bool) – If true, mesh is uniform resolution. If False, mesh is spatially variable

  • +
  • production_mode (bool) – If True, all working files while meshing are cleaned up. If False, then working files will not be deleted

  • +
  • 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

  • +
  • coarse_factor (float) – Maximum resolution of the mesh. Given as a factor of h

  • +
  • slope (float) – slope of variable coarsening resolution.

  • +
  • min_dist (float) – Range of constant min-distance around an intersection (in units of h).

  • +
  • max_dist (float) – Range over which the min-distance between nodes increases (in units of h)

  • +
  • concurrent_samples (int) – number of new candidates sampled around an accepted node at a time.

  • +
  • grid_size (float) – side length of the occupancy grid is given by H/occupancy_factor

  • +
  • well_flag (bool) – If well flag is true, higher resolution around the points in

  • +
+
+
Return type:
+

None

+
+
+

Notes

+
    +
  1. For uniform resolution mesh, set slope = 0

  2. +
  3. All fractures in self.prune_file must intersect at least 1 other fracture

  4. +
+
+ +
+
+

Meshing helper methods

+
+ +

Makes symlinks for files in path required for meshing

+
+
Parameters:
+
    +
  • self (DFN object) –

  • +
  • path (string) – Path to where meshing files are located

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

None

+
+ +
+
+inp2gmv(self, inp_file='')[source]
+

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

  • +
+
+
Return type:
+

None

+
+
+

Notes

+
+ +
+
+inp2vtk_python(self)[source]
+

Using Python VTK library, convert inp file to VTK file.

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+

Notes

+

For a mesh base.inp, this dumps a VTK file named base.vtk

+
+ +
+
+run_lagrit_script(lagrit_file, output_file=None, quiet=False)[source]
+

Runs LaGriT

+
+
Parameters:
+

----------

+
lagrit_filestring

Name of LaGriT script to run

+
+
output_filestring

Name of file to dump LaGriT output

+
+
quietbool

If false, information will be printed to screen.

+
+
+

+
+
Returns:
+

failure – If the run was successful, then 0 is returned.

+
+
Return type:
+

int

+
+
+
+ +
+
+add_variable_to_mesh(self, variable, variable_file, mesh_file_in, mesh_file_out=None, node_based=False)[source]
+

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 – Name of LaGriT output file

+
+
Return type:
+

string

+
+
+
+ +
+
+
+

UDFM

+
+

Creating an upscaled mesh of the DFN

+
+
+map_to_continuum(self, l, orl, path='./', dir_name='octree')[source]
+

This function generates an octree-refined continuum mesh using the +reduced_mesh.inp as input. To generate the reduced_mesh.inp, one must +turn visualization mode on in the DFN input card.

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • l (float) – Size (m) of level-0 mesh element in the continuum mesh

  • +
  • orl (int) – Number of total refinement levels in the octree

  • +
  • path (string) – path to primary DFN directory

  • +
  • dir_name (string) – name of directory where the octree mesh is created

  • +
+
+
Return type:
+

None

+
+
+

Notes

+
+
octree_dfn.inpMesh file

Octree-refined continuum mesh

+
+
fracX.inpMesh files

Octree-refined continuum meshes, which contain intersection areas

+
+
+
+ +
+
+upscale(self, mat_perm, mat_por, path='../')[source]
+

Generate permeabilities and porosities based on output of map2continuum.

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • mat_perm (float) – Matrix permeability (in m^2)

  • +
  • mat_por (float) – Matrix porosity

  • +
+
+
Returns:
+

    +
  • perm_fehm.dat (text file) – Contains permeability data for FEHM input

  • +
  • rock_fehm.dat (text file) – Contains rock properties data for FEHM input

  • +
  • mesh_permeability.h5 (h5 file) – Contains permeabilites at each node for PFLOTRAN input

  • +
  • mesh_porosity.h5 (h5 file) – Contains porosities at each node for PFLOTRAN input

  • +
+

+
+
+

Notes

+

None

+
+ +
+
+check_false_connections(self, path='../')[source]
+
+
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

+
+ +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/pydfnGraph.html b/docs/pydfnGraph.html new file mode 100644 index 00000000..19be62da --- /dev/null +++ b/docs/pydfnGraph.html @@ -0,0 +1,403 @@ + + + + + + + pydfnworks: dfnGraph — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

pydfnworks: dfnGraph

+

DFN Class functions used in graph analysis and pipe-network simulations

+
+

General Graph Functions

+
+
+add_fracture_source(self, G, source)[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

+
+
Return type:
+

NetworkX Graph

+
+
+

Notes

+

bipartite graph not supported

+
+ +
+
+add_fracture_target(self, G, target)[source]
+
+
Parameters:
+
    +
  • G (NetworkX Graph) – NetworkX Graph based on a DFN

  • +
  • target (list) – list of integers corresponding to fracture numbers

  • +
+
+
Returns:
+

G

+
+
Return type:
+

NetworkX Graph

+
+
+

Notes

+

bipartite graph not supported

+
+ +
+
+create_graph(self, graph_type, inflow, outflow)[source]
+

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 – Graph based on DFN

+
+
Return type:
+

NetworkX Graph

+
+
+

Notes

+
+ +
+
+dump_fractures(self, G, filename)[source]
+

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

  • +
+
+
+

Notes

+
+ +
+
+dump_json_graph(self, G, name)[source]
+

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)

  • +
+
+
+

Notes

+
+ +
+
+load_json_graph(self, name)[source]
+

Read in graph from json format

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • name (string) – Name of input file (no .json)

  • +
+
+
Returns:
+

G – NetworkX Graph based on the DFN

+
+
Return type:
+

networkX graph

+
+
+
+ +
+
+plot_graph(self, G, source='s', target='t', output_name='dfn_graph')[source]
+

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)

  • +
+
+
+

Notes

+

Image is written to output_name.png

+
+ +
+
+greedy_edge_disjoint(self, G, source='s', target='t', weight='None', k='')[source]
+

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 – Subgraph of G made up of the k shortest of all edge-disjoint paths from source to target

+
+
Return type:
+

NetworkX Graph

+
+
+

Notes

+
    +
  1. Edge weights must be numerical and non-negative.

  2. +
  3. 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

  4. +
+
+ +
+
+k_shortest_paths_backbone(self, G, k, source='s', target='t', weight=None)[source]
+

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 – Subgraph of G made up of the k shortest paths

+
+
Return type:
+

NetworkX Graph

+
+
+

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

+
+ +
+
+

Graph-Based Flow and Transport

+
+
+run_graph_flow(self, inflow, outflow, pressure_in, pressure_out, fluid_viscosity=0.00089, phi=1, G=None)[source]
+

Solve for pressure driven steady state flow on a graph representation of the DFN.

+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • inflow (string) – name of file containing list of DFN fractures on inflow boundary

  • +
  • outflow (string) – name of file containing list of DFN fractures on outflow boundary

  • +
  • pressure_in (double) – Value of pressure at inlet [Pa]

  • +
  • pressure_out (double) – Value of pressure at outlet [Pa]

  • +
  • fluid_viscosity (double) – optional, default is for water. [Pa*s]

  • +
  • phi (double) – Fracture porosity, default is 1 [-]

  • +
  • G (Input Graph) –

  • +
+
+
Returns:
+

Gtilde – Gtilde is a directed acyclic graph with vertex pressures, fluxes, velocities, volumetric flow rates, and travel times

+
+
Return type:
+

NetworkX graph

+
+
+
+ +
+
+run_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')[source]
+

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.

  • +
  • direction (primary) – string indicating primary direction of flow

  • +
+
+
Returns:
+

particles – list of particles objects

+
+
Return type:
+

list

+
+
+

Notes

+

Information on individual functions is found therein

+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/pydfnTrans.html b/docs/pydfnTrans.html new file mode 100644 index 00000000..4f5b9490 --- /dev/null +++ b/docs/pydfnTrans.html @@ -0,0 +1,218 @@ + + + + + + + pydfnworks: dfnTrans — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

pydfnworks: dfnTrans

+

DFN Class functions used in particle transport simulations (DFNTrans)

+
+

Running Transport Simulations

+
+
+check_dfn_trans_run_files(self)[source]
+

Ensures that all files required for dfnTrans run are in the current directory

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+

Notes

+

None

+
+ +
+
+copy_dfn_trans_files(self)[source]
+

Creates symlink to dfnTrans Execuateble and copies input files for dfnTrans into working directory

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+
+ +
+ +

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.

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Typically, the path is DFN.path, which is set by the command line argument -path

+
+ +
+
+dfn_trans(self)[source]
+

Primary driver for dfnTrans.

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+
+ +
+
+run_dfn_trans(self)[source]
+

Execute dfnTrans

+
+
Parameters:
+

self (object) – DFN Class

+
+
Return type:
+

None

+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/pydfnWorks-well.html b/docs/pydfnWorks-well.html new file mode 100644 index 00000000..1ed59d93 --- /dev/null +++ b/docs/pydfnWorks-well.html @@ -0,0 +1,265 @@ + + + + + + + pydfnworks: Well Package — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

pydfnworks: Well Package

+

DFN Class functions used for well package

+
+

dfnWorks - Well Package

+
+
+cleanup_wells(self, wells)[source]
+

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

    +
    +
    +

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Wells can be a list of well dictionaries

+
+ +
+
+combine_well_boundary_zones(self, wells)[source]
+

Processes zone files for particle tracking. All zone files are combined into allboundaries.zone

+
+
Parameters:
+

None

+
+
Return type:
+

None

+
+
+

Notes

+

None

+
+ +
+
+find_well_intersection_points(self, wells)[source]
+

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

    +
    +
    +

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Wells can be a list of well dictionaries. +Calls the subroutine run_find_well_intersection_points to remove redundant code.

+
+ +
+
+tag_well_in_mesh(self, wells)[source]
+

Identifies nodes in a DFN for nodes the intersect a well with radius r [m]

+
    +
  1. Well coordinates in well[“filename”] are converted to a polyline that are written into “well_{well[‘name’]}_line.inp”

  2. +
  3. Well is expanded to a volume with radius well[“r”] and written into the avs file well_{well[“name”]}_volume.inp

  4. +
  5. Nodes in the DFN that intersect with the well are written into the zone file well_{well[“name”]}.zone

  6. +
  7. If using PFLOTRAN, then an ex file is created from the well zone file

  8. +
+
+
Parameters:
+
    +
  • self (object) – DFN Class

  • +
  • well

    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

    +

    x1 y1 z1

    +

    +

    xn yn zn

    +
    +
    +

  • +
+
+
Return type:
+

None

+
+
+

Notes

+

Wells can be a list of well dictionaries

+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/pydfnworks.html b/docs/pydfnworks.html new file mode 100644 index 00000000..aad83910 --- /dev/null +++ b/docs/pydfnworks.html @@ -0,0 +1,325 @@ + + + + + + + pydfnworks: the dfnWorks python package — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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 (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.

+
$ 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:

+
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:

+
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:

+
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:

+
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:

+
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:

+
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:

+
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:

+
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:

+
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

+

pydfnGen - Network generation, meshing, and analysis

+

pydfnFlow - Flow simulations using PFLOTRAN and FEHM

+

pydfnTrans - Particle Tracking

+

pydfnGraph - Graph-based analysis and pipe-network simulations

+

Well-Package - Well simulations

+
+

Note

+

There are additional required arguments for network generation described in dfnGen

+
+
+
+

Detailed Doxygen Documentation

+

Doxygen

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/search.html b/docs/search.html new file mode 100644 index 00000000..048e2ad2 --- /dev/null +++ b/docs/search.html @@ -0,0 +1,138 @@ + + + + + + Search — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + + + +
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2020, LANL, LA-UR-17-22216.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/searchindex.js b/docs/searchindex.js new file mode 100644 index 00000000..fe638eb1 --- /dev/null +++ b/docs/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["applications", "dfnflow", "dfngen", "dfntrans", "examples", "gallery", "index_docs", "intro", "output", "publications", "pydfnFlow", "pydfnGen", "pydfnGraph", "pydfnTrans", "pydfnWorks-well", "pydfnworks", "setup"], "filenames": ["applications.rst", "dfnflow.rst", "dfngen.rst", "dfntrans.rst", "examples.rst", "gallery.rst", "index_docs.rst", "intro.rst", "output.rst", "publications.rst", "pydfnFlow.rst", "pydfnGen.rst", "pydfnGraph.rst", "pydfnTrans.rst", "pydfnWorks-well.rst", "pydfnworks.rst", "setup.rst"], "titles": ["Example Applications", "dfnFlow", "dfnGen - C++ Generation Code", "dfnTrans", "Examples", "dfnWorks Gallery", "Welcome to dfnWorks 2.7 documentation!", "Welcome To dfnWorks", "Run Files", "dfnWorks Publications", "pydfnworks: dfnFlow", "pydfnworks: dfnGen", "pydfnworks: dfnGraph", "pydfnworks: dfnTrans", "pydfnworks: Well Package", "pydfnworks: the dfnWorks python package", "Setting and Running up dfnWorks"], "terms": {"dfnwork": [0, 2, 4, 8, 10, 11], "provid": [0, 1, 2, 3, 4, 7, 11], "framework": [0, 7, 9], "necessari": [0, 11], "perform": [0, 2, 3, 7], "multiphas": [0, 16], "simul": [0, 1, 2, 3, 5, 6, 7, 9, 10, 11, 12, 15, 16], "flow": [0, 1, 2, 3, 4, 6, 7, 9, 11, 15, 16], "reactiv": [0, 3, 7, 9, 16], "transport": [0, 2, 3, 4, 6, 7, 9, 11, 15, 16], "reservoir": [0, 9], "scale": [0, 7, 9, 16], "A": [0, 2, 7, 9, 11, 12], "particular": [0, 7, 12, 15], "highlight": 0, "here": [0, 2, 4, 15], "i": [0, 1, 2, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], "sequest": 0, "co": [0, 2, 7], "2": [0, 1, 2, 4, 11, 12, 15, 16], "from": [0, 2, 3, 6, 7, 8, 9, 10, 12, 14], "anthropogen": 0, "sourc": [0, 6, 7, 10, 11, 12, 13, 14, 16], "dispos": 0, "geolog": 0, "format": [0, 1, 8, 9, 10, 11, 12, 14, 16], "deep": 0, "salin": 0, "aquif": [0, 9], "abandon": 0, "oil": 0, "field": [0, 3, 5, 6, 7, 9], "one": [0, 1, 2, 3, 4, 11, 16], "princip": 0, "method": [0, 2, 3, 6, 9, 15], "under": [0, 1, 7, 9], "consider": 0, "reduc": [0, 2, 9], "footprint": 0, "atmospher": 0, "due": [0, 2, 4, 8, 9], "fossil": 0, "fuel": 0, "bachu": 0, "2002": 0, "pacala": 0, "socolow": 0, "2004": 0, "For": [0, 2, 11, 16], "safe": 0, "sustain": 0, "long": 0, "term": [0, 7, 11], "storag": 0, "prevent": 0, "leak": 0, "through": [0, 2, 3, 4, 7], "exist": [0, 11], "fault": [0, 9], "fractur": [0, 1, 3, 5, 6, 7, 8, 9, 12, 15, 16], "rock": [0, 9, 11], "along": [0, 2, 4, 5], "ones": 0, "creat": [0, 1, 2, 4, 6, 7, 8, 10, 12, 13, 14, 15, 16], "dure": [0, 2, 9], "inject": [0, 9], "process": [0, 3, 6, 8, 9, 14, 16], "understand": [0, 2, 9], "complex": 0, "physic": [0, 3, 9, 12], "chemic": 0, "interact": [0, 3, 7, 9], "between": [0, 2, 8, 9, 11, 16], "water": [0, 9, 10, 11, 12], "brine": 0, "vital": 0, "capabl": [0, 2, 7], "studi": [0, 7, 9, 11], "dfn": [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16], "can": [0, 1, 2, 4, 7, 10, 11, 14, 15, 16], "us": [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16], "potenti": 0, "migrat": 0, "cap": 0, "risk": [0, 11], "associ": 0, "propos": 0, "subsurfac": [0, 1, 7, 9, 16], "deplet": 0, "moreov": 0, "pflotran": [0, 1, 4, 6, 7, 11, 14, 15], "coupl": 0, "cell": [0, 8, 10, 11, 15], "base": [0, 2, 6, 7, 8, 9, 10, 15, 16], "transmiss": [0, 9, 11], "allow": [0, 3, 15, 16], "dynam": 0, "chang": [0, 4, 11, 15], "permeabl": [0, 1, 8, 9, 10, 11, 15], "miner": 0, "precipit": 0, "dissolut": 0, "tempor": 0, "evolut": [0, 9], "supercrit": [0, 9], "co2": [0, 9], "displac": [0, 11], "meter": [0, 2, 4, 11], "cube": 0, "contain": [0, 2, 4, 6, 7, 8, 11, 12, 14, 15], "24": 0, "The": [0, 2, 3, 4, 6, 7, 8, 9, 11, 16], "initi": [0, 1, 2, 8, 12], "fulli": 0, "satur": 0, "top": [0, 2, 8, 10], "left": [0, 2, 4, 8, 10], "time": [0, 1, 2, 7, 9, 11, 12], "0": [0, 1, 2, 4, 6, 10, 11, 12, 15], "hour": 0, "slowli": 0, "system": [0, 9], "bottom": [0, 2, 8, 10], "domain": [0, 4, 6, 7, 8, 11], "total": [0, 2, 11, 12], "10": [0, 1, 2, 7, 11], "h": [0, 6, 7, 8, 9, 10, 11, 12, 15], "There": [0, 2, 11, 15], "an": [0, 1, 2, 3, 4, 6, 7, 9, 12, 14, 16], "flush": 0, "first": [0, 2, 4, 9, 11, 12, 15], "rate": [0, 2, 12], "decreas": 0, "hydraul": [0, 5, 6, 7, 9, 15], "frack": 0, "ha": [0, 2, 4, 7, 8, 10], "access": [0, 11, 15], "hydrocarbon": 0, "trap": 0, "low": [0, 4], "media": [0, 7, 9, 16], "tight": 0, "involv": [0, 1], "high": [0, 4, 7, 16], "pressur": [0, 1, 4, 5, 10, 12], "also": [0, 2, 11, 16], "new": [0, 2, 4, 7, 9, 11], "increas": [0, 2, 7, 9, 11, 16], "howev": [0, 2, 3, 7, 16], "fundament": 0, "why": 0, "work": [0, 2, 7, 9, 10, 11, 13, 14, 15, 16], "its": [0, 2, 7, 11], "ramif": 0, "ar": [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], "well": [0, 1, 2, 6, 7, 9, 11, 15, 16], "understood": 0, "karra": [0, 1, 3, 7, 9], "et": [0, 9, 11, 12], "al": [0, 9, 11, 12], "2015": [0, 3, 7, 9], "gener": [0, 3, 4, 6, 7, 8, 9, 15], "typic": [0, 10, 13], "product": [0, 7, 9], "site": [0, 9], "thi": [0, 1, 2, 4, 6, 8, 10, 11, 15, 16], "model": [0, 7, 9, 11, 12, 15], "thei": [0, 2, 11], "found": [0, 2, 4, 7, 10, 12, 15, 16], "good": [0, 2, 7], "agreement": 0, "data": [0, 7, 9, 11, 15], "determin": [0, 2, 3, 7, 9, 11], "what": [0, 11, 12], "mechan": [0, 9, 11], "control": [0, 3, 12, 15, 16], "declin": 0, "curv": [0, 1, 9], "swedish": 0, "manag": 0, "compani": 0, "skb": 0, "undertaken": 0, "detail": [0, 1, 2, 3, 6, 7, 8, 10, 11, 12, 16], "investig": 0, "granit": 0, "forsmark": 0, "sweden": 0, "host": [0, 9, 16], "spent": 0, "2011": 0, "hartlei": 0, "joyc": 0, "2013": [0, 11], "area": [0, 2, 8, 10, 11], "about": [0, 2, 3, 6, 8, 10, 11, 14, 15], "120": 0, "km": 0, "north": [0, 2], "stockholm": 0, "northern": 0, "uppland": 0, "construct": [0, 2], "crystallin": [0, 9], "bedrock": 0, "depth": 0, "approxim": [0, 4, 7], "500": 0, "m": [0, 1, 2, 3, 9, 11, 12, 14, 16], "statist": [0, 2, 9], "multipl": [0, 2, 10, 15], "set": [0, 2, 4, 6, 7, 10, 11, 13], "wa": [0, 7, 11], "develop": [0, 7, 16], "paramet": [0, 1, 4, 6, 7, 8, 10, 12, 13, 14], "we": [0, 3, 4, 7, 11, 16], "adopt": [0, 3, 7], "subset": 0, "consist": [0, 4, 15, 16], "three": [0, 2, 3, 7, 9, 11, 12], "background": [0, 6, 7], "non": [0, 2, 3, 8, 9, 12], "determinist": [0, 2], "circular": [0, 4], "whose": [0, 4, 5], "orient": [0, 6, 7, 11], "follow": [0, 2, 4, 5, 7, 9, 11, 14, 16], "fisher": [0, 2, 11], "distribut": [0, 5, 6, 7, 9, 11, 12, 16], "radii": [0, 5, 8, 11, 15], "sampl": [0, 1, 2, 7, 9, 11], "truncat": [0, 5, 6, 9, 11], "power": [0, 2, 6, 9, 11], "law": [0, 2, 6, 9, 11], "estim": 0, "radiu": [0, 6, 11, 14], "apertur": [0, 5, 7, 8, 9, 10, 11, 15], "relat": [0, 2, 11], "size": [0, 2, 4, 9, 10, 11, 12], "cubic": [0, 4], "adler": 0, "2012": 0, "formul": 0, "uniform": [0, 2, 4, 11, 12], "each": [0, 2, 3, 4, 8, 11, 12], "vari": 0, "among": 0, "network": [0, 1, 3, 4, 6, 7, 8, 9, 11, 12, 14, 15, 16], "side": [0, 2, 4, 8, 11], "length": [0, 2, 6, 8, 11, 12], "kilomet": [0, 7, 9], "dirichlet": [0, 1, 4], "boundari": [0, 1, 2, 4, 8, 10, 11, 12], "condit": [0, 1, 4, 7, 8, 10, 12], "impos": [0, 2], "1": [0, 1, 2, 6, 10, 11, 12, 15, 16], "mpa": 0, "gradient": [0, 4, 8], "align": [0, 4], "vertic": [0, 2, 4, 11], "axi": [0, 2, 4], "noflow": 0, "enforc": 0, "later": [0, 7], "particl": [0, 3, 4, 5, 9, 12, 13, 14, 15], "trajectori": [0, 3, 4, 12], "p": [0, 9], "thovert": 0, "j": [0, 2, 7, 9, 11], "f": [0, 2, 9, 11], "mourzenko": 0, "v": [0, 9, 16], "porou": [0, 9, 16], "oxford": 0, "univers": [0, 7], "press": 0, "unit": [0, 2, 10, 11, 12], "kingdom": 0, "": [0, 1, 2, 3, 4, 5, 7, 9, 12], "respons": [0, 9], "climat": 0, "road": 0, "map": 0, "select": [0, 2], "transform": 0, "space": [0, 2], "phase": 0, "conver": 0, "43": 0, "87": 0, "102": 0, "l": [0, 2, 3, 7, 9, 11], "approach": [0, 2, 3, 7, 9, 12], "algorithm": [0, 2, 3, 4, 7, 9, 12, 16], "groundwat": [0, 9], "support": [0, 2, 10, 11, 12], "safeti": 0, "assess": 0, "Fors": 0, "mark": [0, 7], "hydrol": 0, "200": 0, "216": 0, "makedonska": [0, 1, 2, 3, 7, 9], "n": [0, 2, 3, 7, 9, 11], "viswanathan": [0, 7, 9], "painter": [0, 2, 3, 7, 9], "hyman": [0, 2, 7, 9, 11, 12], "effect": [0, 2, 3, 9, 10, 11], "advect": [0, 3, 9], "matrix": [0, 3, 8, 9, 11, 12], "diffus": [0, 3, 9, 12], "natur": [0, 4, 9], "ga": [0, 9], "resour": [0, 9], "re": [0, 2, 9, 16], "review": [0, 9, 12], "r": [0, 2, 7, 9, 11, 14, 16], "stabil": 0, "wedg": 0, "solv": [0, 3, 12, 16], "problem": [0, 3, 9], "next": [0, 11, 16], "50": [0, 2], "year": [0, 7], "current": [0, 4, 10, 11, 12, 13, 16], "technologi": 0, "scienc": [0, 7, 9, 11], "305": 0, "968": 0, "972": 0, "final": [0, 2, 7, 8, 11, 15, 16], "main": [0, 2, 9, 11, 16], "report": [0, 2, 7, 9, 11], "sr": 0, "project": [0, 2], "technic": 0, "tr": 0, "11": 0, "01": [0, 1], "solver": [1, 7, 10, 16], "fehm": [1, 6, 7, 8, 11, 15], "recommend": [1, 2, 4, 7, 15, 16], "larg": [1, 10, 16], "number": [1, 2, 8, 11, 12, 15], "o": [1, 9, 15, 16], "1000": 1, "function": [1, 2, 4, 6, 7, 8, 10, 11, 13, 14, 15], "call": [1, 7, 10, 11, 14, 15], "part": 1, "pydfnwork": [1, 2, 6, 7], "mesh": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14, 15, 16], "file": [1, 4, 6, 10, 12, 13, 14, 15, 16], "need": [1, 2, 16], "run": [1, 2, 4, 6, 7, 12], "unstructur": [1, 3], "ug": [1, 8, 15], "ex": [1, 8, 10, 14], "pleas": [1, 7, 11], "see": [1, 2, 7, 10, 11, 12, 15, 16], "user": [1, 4, 6, 7, 8, 15], "manual": [1, 6, 15], "http": [1, 2, 4, 7, 10, 11, 16], "www": [1, 4, 10, 16], "org": [1, 2, 4, 10, 16], "explicit": 1, "usag": [1, 9], "further": 1, "exampl": [1, 6, 7, 11, 14, 15, 16], "input": [1, 4, 6, 7, 8, 10, 12, 13, 15], "repositori": [1, 6, 7, 9], "start": [1, 2, 3, 8, 12, 16], "point": [1, 2, 8, 11, 14], "build": [1, 6], "your": [1, 4, 7, 11, 15, 16], "deck": 1, "below": [1, 2, 11, 15], "refer": [1, 2, 11], "descript": [1, 2, 3, 7, 15], "jan": 1, "13": [1, 2], "2014": [1, 2, 9], "nataliia": [1, 7, 9], "satish": [1, 7, 9], "lanl": [1, 6, 10, 11, 16], "simulation_typ": 1, "process_model": 1, "subsurface_flow": 1, "mode": [1, 2, 9, 10, 11], "richard": 1, "end": [1, 4, 5, 8, 12], "discret": [1, 2, 3, 7, 9, 11, 12, 15], "grid": [1, 11], "type": [1, 2, 10, 11, 12, 13, 14, 15], "unstructured_explicit": 1, "full_mesh_vol_area": [1, 8], "graviti": 1, "d0": 1, "fluid": [1, 9], "properti": [1, 2, 6, 8, 9, 15], "fluid_properti": 1, "diffusion_coeffici": 1, "d": [1, 2, 7, 9, 11], "9": [1, 2], "dataset": 1, "filenam": [1, 2, 10, 11, 12, 14], "dfn_properti": [1, 8], "h5": [1, 8, 11], "materi": [1, 4, 7, 8, 9, 11], "material_properti": 1, "soil1": 1, "id": [1, 4, 8, 11, 15], "poros": [1, 11, 12], "25d0": 1, "tortuos": 1, "5d0": 1, "characteristic_curv": 1, "default": [1, 2, 10, 11, 12, 15], "characterist": [1, 9], "saturation_funct": 1, "van_genuchten": 1, "alpha": [1, 2, 11], "4": [1, 2, 6, 9, 11, 15], "liquid_residual_satur": 1, "1d0": 1, "max_capillary_pressur": 1, "d8": 1, "permeability_funct": 1, "mualem_vg_liq": 1, "output": [1, 6, 7, 10, 11, 12, 15, 16], "option": [1, 2, 7, 11, 12, 15, 16], "05": [1, 2], "5": [1, 2, 3, 4, 6, 11], "tecplot": 1, "block": [1, 12], "print_primal_grid": 1, "vtk": [1, 4, 8, 10, 11, 16], "mass_flowr": 1, "mass_bal": 1, "variabl": [1, 5, 9, 11, 16], "liquid_pressur": 1, "initial_timestep_s": 1, "8": [1, 2, 4, 11, 15], "final_tim": 1, "maximum_timestep_s": 1, "steady_st": 1, "reference_pressur": 1, "1500000": 1, "region": [1, 6, 7, 11], "all": [1, 2, 4, 7, 10, 11, 12, 13, 14, 16], "coordin": [1, 11, 14], "d20": 1, "inflow": [1, 4, 10, 12], "pboundary_left_w": [1, 8], "outflow": [1, 4, 10, 12], "pboundary_right_": [1, 8], "flow_condit": 1, "01325d6": 1, "d6": 1, "coupler": 1, "initial_condit": 1, "boundary_condit": 1, "stratigraphi": 1, "strata": 1, "end_subsurfac": 1, "featur": [2, 7, 9, 11], "reject": [2, 7, 8, 9], "fram": [2, 7], "stochast": [2, 4, 7, 9, 11], "implement": [2, 3, 11, 16], "methodologi": [2, 3, 7], "w": [2, 3, 7, 9], "gabl": [2, 3, 7, 9], "conform": [2, 7, 9, 16], "delaunai": [2, 7, 9, 16], "triangul": [2, 7, 9, 16], "dimension": [2, 3, 7, 9, 11, 12], "strategi": [2, 9], "siam": [2, 9, 12], "sci": [2, 9], "comput": [2, 3, 4, 7, 9, 10, 16], "36": 2, "a1871": 2, "a1894": 2, "si": [2, 12], "mandatori": [2, 15], "label": 2, "must": [2, 7, 11, 12, 15, 16], "present": [2, 4, 11, 16], "python": [2, 6, 7, 11], "driver": [2, 6, 13, 15], "script": [2, 4, 11, 15, 16], "which": [2, 3, 4, 7, 8, 10, 11, 12, 13, 16], "center": [2, 11], "origin": [2, 4], "entri": 2, "span": 2, "x": [2, 4, 8, 10, 11], "east": 2, "west": 2, "second": [2, 4, 11, 15], "y": [2, 4, 8, 9, 10], "south": 2, "third": [2, 11], "z": [2, 8, 9, 10], "list": [2, 7, 8, 11, 12, 14, 15], "float": [2, 10, 11, 12, 14], "none": [2, 10, 11, 12, 13, 14, 15, 16], "valu": [2, 8, 10, 11, 12, 15], "20": 2, "minimum": [2, 4, 11], "maximum": [2, 4, 11], "direct": [2, 4, 7, 10, 12], "temporari": 2, "insert": [2, 4, 11], "outsid": 2, "after": [2, 11, 15], "complet": [2, 11], "back": [2, 8, 10], "expans": 2, "help": [2, 16], "mitig": 2, "edg": [2, 4, 12, 16], "densiti": [2, 9, 10], "param": [2, 8, 10, 11, 15], "ad": [2, 6, 7], "subtract": 2, "less": 2, "than": [2, 7], "rule": 2, "thumb": 2, "least": [2, 11, 16], "largest": 2, "stratograph": 2, "If": [2, 7, 10, 11, 14, 15, 16], "height": 2, "assign": [2, 7, 11, 15], "when": [2, 11], "pydfngen": [2, 15], "neg": [2, 8, 12], "integ": [2, 8, 12, 15], "two": [2, 4, 15], "lower": 2, "upper": 2, "limit": [2, 7, 8, 12], "etc": 2, "everi": [2, 8], "entir": [2, 11], "element": [2, 3, 8, 11], "zmin": 2, "zmax": 2, "30": 2, "40": [2, 11], "overlap": 2, "cuboid": 2, "creation": 2, "bound": 2, "box": 2, "theses": 2, "six": 2, "minx": 2, "maxx": 2, "mini": 2, "maxi": 2, "minz": 2, "maxz": 2, "15": 2, "Will": 2, "min": [2, 11], "max": 2, "do": [2, 3, 7, 16], "same": [2, 4, 11, 15], "face": [2, 10], "boolean": [2, 10, 11, 15], "fals": [2, 10, 11, 12, 15], "true": [2, 10, 11, 15], "ignor": [2, 12], "keep": 2, "cluster": [2, 9], "retain": [2, 7, 15], "onli": [2, 3, 10, 11, 12], "connect": [2, 8, 9, 11, 12], "correspond": [2, 4, 8, 10, 11, 12], "3": [2, 6, 11, 16], "within": [2, 4, 5, 7, 15], "criteria": 2, "stop": [2, 11], "onc": 2, "accept": [2, 11], "p32": [2, 11], "have": [2, 4, 8, 10, 14, 15, 16], "been": [2, 7, 8, 15], "meet": 2, "belong": [2, 11], "request": [2, 12], "posit": [2, 11], "100": 2, "probabl": [2, 11], "occurr": 2, "add": [2, 7, 10, 11], "up": [2, 4, 6, 7, 11, 12], "save": [2, 11], "order": [2, 4], "automat": [2, 11, 15, 16], "prior": [2, 4, 7, 15], "ani": [2, 7, 16], "possibl": [2, 7], "constraint": 2, "met": [2, 7], "definit": 2, "theta": [2, 11], "phi": [2, 11, 12], "degre": [2, 11], "angl": [2, 8, 11], "specifi": [2, 11, 15], "string": [2, 10, 11, 12, 13, 14, 15], "either": [2, 7, 11, 15], "radian": [2, 11], "reason": [2, 8], "unless": 2, "turn": [2, 11], "greater": 2, "cdot": 2, "sqrt": 2, "where": [2, 9, 11, 14, 16], "smaller": 2, "10th": 2, "larger": 2, "1000th": 2, "zero": [2, 8], "clear": 2, "requir": [2, 4, 6, 10, 11, 13, 15], "discuss": [2, 15], "off": [2, 12], "result": [2, 8, 11], "coars": 2, "visual": [2, 4, 7, 9, 11, 16], "you": [2, 4, 7, 11, 16], "cannot": 2, "full": [2, 8, 12], "intent": 2, "upscal": [2, 6, 9], "octre": [2, 9, 11], "udfm": [2, 6, 9], "modul": [2, 6, 7, 11, 16], "print": [2, 6, 7], "inform": [2, 3, 6, 8, 9, 10, 12, 14, 15], "screen": [2, 10, 11], "debug": [2, 15, 16], "more": [2, 7, 11, 12], "effici": [2, 9], "hit": 2, "target": [2, 11, 12], "befor": [2, 11, 16], "being": 2, "equal": 2, "queue": 2, "percentag": 2, "smallest": 2, "begin": 2, "properli": 2, "resolv": [2, 3], "represent": [2, 3, 4, 5, 7, 9, 12, 16], "exhaust": 2, "random": [2, 7, 9, 11], "like": [2, 15], "doubl": [2, 10, 12], "examin": 2, "check": [2, 7, 11], "prescrib": 2, "output_report": [2, 11], "No": 2, "longer": [2, 11], "should": [2, 7, 10, 11], "done": [2, 16], "mesh_network": [2, 4, 11, 15], "visual_mod": [2, 11], "want": [2, 11], "quick": 2, "standard": 2, "full_mesh": [2, 4, 8, 15], "inp": [2, 4, 8, 11, 14, 15], "reduced_mesh": [2, 11], "clock": 2, "uniqu": [2, 3], "produc": [2, 7, 8, 11], "reproduc": [2, 4, 7], "42069": 2, "dfn_output": [2, 8], "txt": [2, 4, 8, 10, 11], "isol": [2, 4, 8], "remov": [2, 4, 8, 11, 12, 14, 15], "contribut": 2, "fail": [2, 11], "still": 2, "whether": [2, 7, 11], "tripl": [2, 8], "intersect": [2, 8, 9, 11, 12, 14, 16], "even": [2, 7, 11], "dfntran": [2, 6, 7, 15], "doe": [2, 3, 9, 10, 11], "cutoff": 2, "e": [2, 9, 10, 12], "g": [2, 9, 10, 12], "down": 2, "those": [2, 11], "how": [2, 16], "alwai": 2, "sequenti": [2, 9], "respect": 2, "forc": [2, 10], "sort": [2, 9], "minim": 2, "both": [2, 15], "radii_allaccept": 2, "dat": [2, 4, 8, 10, 11, 14, 15], "xradiu": 2, "yradiu": 2, "userpolygon": 2, "userrectangl": 2, "userellips": 2, "per": [2, 8], "radii_allaccepted_fam_1": 2, "distributionnumb": 2, "radii_final_fam_1": 2, "section": [2, 4, 8, 11, 15, 16], "describ": [2, 8, 15, 16], "disc": [2, 7], "shape": [2, 11, 15], "match": [2, 9], "index": [2, 8, 10, 11], "shown": [2, 5], "written": [2, 7, 10, 11, 12, 14], "dictionari": [2, 11, 14], "meant": 2, "addit": [2, 6, 7], "whole": [2, 11], "intens": [2, 11], "p_": 2, "32": 2, "text": [2, 11], "surfac": [2, 4], "divid": 2, "volum": [2, 3, 7, 8, 10, 14, 16], "until": 2, "obtain": [2, 3, 4, 6, 12], "desir": [2, 11, 16], "02": 2, "ellipt": [2, 4], "12": [2, 11, 15], "16": 2, "lead": 2, "challeng": [2, 3, 9], "via": [2, 9], "suggest": 2, "aspect": [2, 11], "ratio": [2, 11], "circl": 2, "twice": 2, "make": [2, 7, 11, 14], "As": 2, "degrad": 2, "accuraci": 2, "von": [2, 11], "mise": [2, 11], "bf": 2, "boldsymbol": 2, "mu": [2, 11], "kappa": [2, 11], "frac": 2, "exp": [2, 6, 11], "t": [2, 9, 11, 12], "pi": 2, "sinh": 2, "mean": [2, 11], "vector": [2, 5, 8, 11], "denot": 2, "transpos": 2, "commun": 2, "23": 2, "1994": 2, "157": 2, "164": 2, "andrew": 2, "wood": 2, "indic": [2, 12], "concentr": [2, 11], "around": [2, 11], "17": [2, 7, 11], "45": [2, 11], "78": 2, "sphere": 2, "small": 2, "deviat": 2, "numer": [2, 3, 9, 12], "becom": 2, "unstabl": 2, "vec": 2, "_x": 2, "sin": 2, "_y": 2, "_z": 2, "onto": 2, "56": 2, "270": 2, "four": [2, 4, 11], "differ": [2, 9, 11, 15], "log": [2, 9, 11], "sigma": [2, 11], "ln": 2, "right": [2, 4, 7, 8, 10], "varianc": [2, 11], "underli": 2, "en": 2, "wikipedia": 2, "wiki": 2, "normal_distribut": 2, "equat": [2, 3, 11, 16], "abov": [2, 7], "convert": [2, 10, 11, 14], "609": 2, "040": 2, "r_0": 2, "r_u": 2, "expon": [2, 4], "6": [2, 4, 6, 15, 16], "tpl": [2, 6, 11], "lambda": 2, "25": [2, 11], "7": [2, 8], "527": 2, "89": 2, "singl": [2, 4, 8, 11, 15], "These": [2, 8, 14, 15, 16], "mono": 2, "dispers": [2, 3, 9], "rectangular": [2, 4], "squar": 2, "ref": 2, "includ": [2, 4, 7, 8, 11, 15], "wai": [2, 7, 15, 16], "One": [2, 11], "put": [2, 10], "convex": 2, "vertex": [2, 4, 12], "To": [2, 6, 9, 11, 15, 16], "incoper": 2, "add_user_fract": [2, 11, 15], "add_user_fract_from_fil": [2, 11], "predefin": 2, "go": [2, 9, 16], "activ": 2, "read": [2, 12], "path": [2, 6, 7, 9, 10, 11, 12, 13], "valid": [2, 15], "filepath": 2, "userell_input_filepath": 2, "4_user_ell_uniform": 2, "define_4_user_ellips": 2, "int": [2, 10, 11, 12], "expect": [2, 8], "line": [2, 4, 6, 8, 10, 11, 13], "accord": 2, "keyword": [2, 11], "mix": [2, 9], "90": 2, "37": 2, "name": [2, 7, 10, 11, 12, 14, 15], "4_user_el": 2, "ellcoord": 2, "node": [2, 8, 11, 12, 14, 15], "x_0": 2, "y_0": 2, "z_0": 2, "x_1": 2, "y_1": 2, "z_1": 2, "ldot": 2, "x_n": 2, "y_n": 2, "z_n": 2, "clockwis": 2, "counterclockwis": 2, "planar": 2, "BY": [2, 7], "note": [2, 3, 4, 8, 10, 11, 12, 13, 14, 16], "white": 2, "matter": 2, "x1": [2, 8, 11, 14], "y1": [2, 8, 11, 14], "z1": [2, 8, 11, 14], "x2": [2, 11], "y2": [2, 11], "z2": [2, 11], "x3": 2, "y3": 2, "z3": 2, "x4": 2, "y4": 2, "z4": 2, "xn": [2, 11, 14], "yn": [2, 11, 14], "zn": [2, 11, 14], "4_user_rect": 2, "define_4_user_rect": 2, "rectcoord": 2, "user_polygon": 2, "refpolygonbycoord_input_file_path": 2, "Then": [2, 3, 11], "remain": 2, "614809755508": 2, "215302589745": 2, "14052566974": 2, "545132019741": 2, "76296408041": 2, "01135305297": 2, "46205561432": 2, "11169723114": 2, "28911258873": 2, "7422671348": 2, "903335314403": 2, "34275683413": 2, "19676496723": 2, "52840001844": 2, "54841054822": 2, "3215018603": 2, "70265907788": 2, "28455866847": 2, "968743065242": 2, "39004354883": 2, "5300049043": 2, "82257143457": 2, "169537908304": 2, "10365780918": 2, "89449659536": 2, "39229757501": 2, "47097810869": 2, "7368040684": 2, "396173810104": 2, "9199696155": 2, "76351044547": 2, "192549006049": 2, "33581920939": 2, "157462422292": 2, "76600532497": 2, "29594241204": 2, "30082072643": 2, "47978607543": 2, "64081751301": 2, "793278268371": 2, "81778296407": 2, "561983329033": 2, "45819042638": 2, "44636375172": 2, "4541413343": 2, "51825209439": 2, "66719739398": 2, "0912558548": 2, "1739128129": 2, "74246641127": 2, "809628560699": 2, "56729801834": 2, "10664963232": 2, "17895163585": 2, "75834169562": 2, "23742727251": 2, "80010131269": 2, "26666449977": 2, "40228077595": 2, "45665913893": 2, "23685468994": 2, "54844410108": 2, "5749811435": 2, "06272440502": 2, "61045433532": 2, "565188057474": 2, "92263630123": 2, "27643915621": 2, "710166484812": 2, "89865208954": 2, "42113429321": 2, "22497133918": 2, "00482133497": 2, "61970921408": 2, "322281059007": 2, "27249031626": 2, "91468659533": 2, "549550927249": 2, "19700291895": 2, "3154355331": 2, "64584005023": 2, "20544838483": 2, "98437001886": 2, "31745142962": 2, "22589362057": 2, "804792681255": 2, "91861492809": 2, "70593999999": 2, "29639431251": 2, "25224389054": 2, "10094407568": 2, "6562865987": 2, "03245087974": 2, "992518232701": 2, "50612429324": 2, "10741842947": 2, "384249987443": 2, "93387033715": 2, "43323157864": 2, "222868762162": 2, "860529584164": 2, "81903344627": 2, "38212753352": 2, "499362646346": 2, "03882643837": 2, "4905528176": 2, "349200223043": 2, "9638588789": 2, "8673209588": 2, "22305329999": 2, "63804581282": 2, "39564264412": 2, "56312416852": 2, "08462835745": 2, "41898893677": 2, "65124788861": 2, "11909954775": 2, "10520773081": 2, "8645938936": 2, "89290711889": 2, "80872562941": 2, "11160312936": 2, "39770554375": 2, "19173173493": 2, "34506895229": 2, "42895858094": 2, "20485792137": 2, "85177752427": 2, "81351205493": 2, "99702019362": 2, "0853427250495": 2, "66597219167": 2, "50868187563": 2, "289753327606": 2, "588931258258": 2, "08441961571": 2, "88990150472": 2, "64805531643": 2, "34094328974": 2, "922507014307": 2, "71519798881": 2, "85778630995": 2, "841071899053": 2, "310044679731": 2, "25820816606": 2, "26426496474": 2, "84566856205": 2, "49988903618": 2, "94418542615": 2, "40633923745": 2, "44125563949": 2, "48254521057": 2, "07783735953": 2, "94508200102": 2, "5639802392": 2, "05259419185": 2, "54466008318": 2, "14078724661": 2, "483028953749": 2, "30297907019": 2, "4608669326": 2, "04369954706": 2, "36161304612": 2, "18143095055": 2, "610312831185": 2, "21771261931": 2, "80909183961": 2, "814183043594": 2, "47247873318": 2, "26469088948": 2, "29432736239": 2, "44327042469": 2, "867130728085": 2, "96307141873": 2, "14719749191": 2, "564917036364": 2, "42867426252": 2, "75769541671": 2, "19257780773": 2, "00417817281": 2, "50292931657": 2, "648176891617": 2, "475965761662": 2, "53213764385": 2, "74938298608": 2, "14470985076": 2, "82821050463": 2, "27165416308": 2, "97370232204": 2, "13046740212": 2, "01807341608": 2, "962283071202": 2, "35725325459": 2, "83562647916": 2, "23286105726": 2, "12746635144": 2, "40616097581": 2, "67958113114": 2, "45759785589": 2, "34398137987": 2, "64526675756": 2, "polgyon": 2, "solut": [3, 4, 7, 8, 9, 10, 11], "dfnflow": [3, 6, 7, 15], "dfngen": [3, 6, 7, 15], "lagrangian": [3, 7], "repres": [3, 9, 11], "conserv": 3, "collect": 3, "indivis": 3, "passiv": 3, "tracer": 3, "track": [3, 4, 9, 12, 14, 15], "wealth": 3, "local": [3, 15, 16], "b": [3, 9, 11], "suffer": 3, "inher": 3, "c": [3, 6, 7, 9, 16], "intrins": [3, 7], "parallel": [3, 7, 9, 16], "fashion": [3, 7], "anoth": [3, 10, 13], "pose": 3, "aris": [3, 7], "qualiti": [3, 7], "phenomena": 3, "interest": 3, "mass": [3, 11], "so": [3, 4, 7, 16], "galerkin": 3, "finit": [3, 7, 12, 16], "code": [3, 4, 6, 7, 14, 16], "reconstruct": 3, "veloc": [3, 4, 5, 12], "darci": 3, "flux": [3, 12], "pathlin": [3, 4, 5, 7], "It": [3, 16], "import": [3, 15, 16], "itself": [3, 7], "longitudin": 3, "sorption": 3, "other": [3, 7, 11, 15], "retent": 3, "easili": 3, "incorpor": 3, "post": 3, "bui": [3, 7, 9], "q": [3, 9], "geoscienc": [3, 7, 9], "19": [3, 7], "1123": 3, "1137": 3, "doxygen": [3, 6], "few": 4, "folder": [4, 16], "focu": 4, "document": [4, 7, 16], "confirm": 4, "correctli": 4, "carri": 4, "out": [4, 7, 8, 10, 11, 12], "imag": [4, 12, 16], "render": 4, "paraview": 4, "free": [4, 7], "simplest": 4, "proce": [4, 11], "directori": [4, 6, 7, 8, 10, 11, 13, 14, 15], "subdirectori": [4, 8], "command": [4, 6, 10, 11, 13, 16], "Be": 4, "sure": [4, 11], "test_output_fil": 4, "locat": [4, 8, 11, 12], "test": [4, 6], "case": [4, 9], "defin": [4, 6, 7, 8, 11], "color": [4, 5, 12], "overlaid": 4, "load": 4, "job": [4, 11], "red": [4, 12], "appli": [4, 9], "blue": [4, 12], "parsed_vtk": [4, 8], "dfn_explicit": [4, 8, 15], "001": [4, 8], "uniformli": 4, "inlet": [4, 12], "exit": [4, 11], "horizont": 4, "might": 4, "exactli": 4, "magnitud": 4, "part_": 4, "4_user_rectangl": 4, "traj": 4, "extract": [4, 6], "tube": 4, "filter": 4, "clariti": 4, "In": [4, 9], "ellips": [4, 6, 11], "resolut": [4, 9, 10, 11, 14, 16], "argument": [4, 6, 10, 13], "slope": [4, 11], "run_explicit": 4, "py": [4, 11, 15, 16], "famili": [4, 6, 7, 8, 16], "1m": 4, "50m": 4, "10m": 4, "gen_exponential_dist": 4, "modifi": [4, 7], "card": [4, 10, 11], "5m": 4, "15m": 4, "core": 4, "dead": [4, 5], "identifi": [4, 8, 9, 12, 14], "networkx": [4, 12, 16], "github": [4, 6, 7], "io": [4, 16], "sub": [4, 14, 16], "207": 4, "79": 4, "topologi": [4, 9], "compos": 5, "powerlaw": 5, "contour": 5, "graph": [5, 6, 7, 9, 15], "intern": [5, 9, 11], "purpl": 5, "content": [6, 8], "cite": 6, "version": [6, 11, 16], "v2": [6, 11], "contact": 6, "contributor": 6, "extern": 6, "copyright": 6, "applic": [6, 7, 9], "carbon": 6, "dioxid": 6, "sequestr": [6, 7], "shale": [6, 9], "energi": [6, 7, 9], "nuclear": [6, 7, 9], "wast": [6, 7, 9], "docker": [6, 7], "nativ": 6, "clone": 6, "dnfwork": 6, "fix": [6, 7], "lagrit": [6, 7, 10], "petsc": 6, "instal": [6, 15], "4_user_defined_rect": 6, "4_user_defined_ell_uniform": 6, "exponenti": 6, "prune": [6, 11, 15], "packag": [6, 7, 16], "class": [6, 10, 11, 12, 13, 14, 16], "jobnam": [6, 11], "ncpu": [6, 10], "dfnflow_fil": 6, "dfngen_fil": 6, "dfntrans_fil": 6, "prune_fil": [6, 11], "flow_solv": [6, 10], "cell_based_apertur": 6, "analysi": [6, 9, 12, 15], "modif": [6, 7], "stress": [6, 7, 9], "primari": [6, 9, 10, 12, 13], "helper": 6, "create_dfn_flow_link": [6, 10], "dfn_flow": [6, 10, 15], "set_flow_solv": [6, 10], "lagrit2pflotran": [6, 10], "parse_pflotran_vtk_python": [6, 10], "pflotran_cleanup": [6, 10], "write_perms_and_correct_volumes_area": [6, 10], "zone2ex": [6, 10], "correct_stor_fil": [6, 10], "effective_perm": [6, 10], "check_dfn_trans_run_fil": [6, 13], "copy_dfn_trans_fil": [6, 13], "create_dfn_trans_link": [6, 13], "dfn_tran": [6, 13, 15], "run_dfn_tran": [6, 13], "dfngraph": 6, "add_fracture_sourc": [6, 12], "add_fracture_target": [6, 12], "create_graph": [6, 12], "dump_fractur": [6, 12], "dump_json_graph": [6, 12], "load_json_graph": [6, 12], "plot_graph": [6, 12], "greedy_edge_disjoint": [6, 12], "k_shortest_paths_backbon": [6, 12], "run_graph_flow": [6, 12], "run_graph_transport": [6, 12], "cleanup_wel": [6, 14], "combine_well_boundary_zon": [6, 14], "find_well_intersection_point": [6, 14], "tag_well_in_mesh": [6, 14], "domains": [6, 15], "domainsizeincreas": 6, "numoflay": 6, "layer": [6, 11], "numofregion": 6, "ignoreboundaryfac": 6, "boundaryfac": 6, "stopcondit": [6, 11], "npoli": 6, "famprob": 6, "orientationopt": [6, 11], "angleopt": 6, "disablefram": 6, "printrejectreason": 6, "rejectsperfractur": 6, "radiilistincreas": 6, "visualizationmod": 6, "seed": 6, "keeponlylargestclust": 6, "keepisolatedfractur": 6, "tripleintersect": 6, "removefractureslessthan": 6, "insertuserrectanglesfirst": 6, "forcelargefractur": 6, "outputallradii": 6, "outputacceptedradiiperfamili": 6, "outputfinalradiiperfamili": 6, "rectangl": [6, 8, 11], "polygon": [6, 8, 11], "public": [6, 7], "galleri": 6, "suit": 7, "lo": [7, 16], "alamo": [7, 16], "nation": [7, 9, 16], "laboratori": [7, 16], "rang": [7, 11], "millimet": 7, "combin": [7, 14], "toolbox": [7, 16], "suitabl": [7, 16], "util": 7, "massiv": [7, 16], "workflow": [7, 10, 11, 15], "highli": [7, 16], "variou": [7, 15], "tool": [7, 16], "aim": 7, "seamless": 7, "scientif": [7, 9], "download": [7, 16], "hub": [7, 16], "com": [7, 16], "ees16": [7, 16], "v1": 7, "version1": 7, "84": 7, "bibtex": 7, "articl": 7, "hyman2015dfnwork": 7, "titl": 7, "author": [7, 11], "jeffrei": [7, 9, 11], "carl": [7, 9], "scott": [7, 9], "hari": [7, 9], "journal": [7, 9, 11], "page": [7, 9], "publish": 7, "elsevi": 7, "object": [7, 10, 11, 12, 13, 14, 15], "interfac": 7, "updat": 7, "compat": 7, "bug": 7, "warn": [7, 8, 11], "error": [7, 11], "trend": [7, 11], "plung": [7, 11], "dip": [7, 11], "strike": [7, 11], "techniqu": [7, 15], "poisson": [7, 9], "expand": [7, 14], "pdf": [7, 11], "abil": 7, "python3": 7, "pipe": [7, 12, 15], "dfn2graph": 7, "streamlin": 7, "rout": 7, "walk": 7, "much": 7, "faster": [7, 16], "mathematica": 7, "successfulli": 7, "350": 7, "000": [7, 8], "compris": 7, "short": 7, "tutori": 7, "prepar": 7, "some": [7, 11], "email": 7, "gov": [7, 10, 11], "question": 7, "let": [7, 11], "u": 7, "know": 7, "ll": 7, "matt": 7, "sweenei": [7, 9, 11], "aric": [7, 9], "hagberg": [7, 9], "shriram": [7, 9], "srinivasan": [7, 9], "aidan": [7, 11], "stansberri": 7, "pnnl": 7, "ornl": 7, "quan": 7, "now": 7, "llnl": 7, "jeremi": 7, "harrod": 7, "spectra": 7, "logic": 7, "thoma": 7, "sherman": [7, 9], "notr": 7, "dame": 7, "johann": 7, "krotz": [7, 9], "oregon": 7, "state": [7, 8, 9, 10, 12], "yu": 7, "chen": [7, 9], "la": 7, "ur": 7, "22216": 7, "softwar": [7, 16], "cc": 7, "027": 7, "2018": [7, 9, 12], "triad": 7, "secur": [7, 9], "llc": 7, "reserv": 7, "program": [7, 11], "govern": 7, "contract": 7, "89233218cna000001": 7, "oper": 7, "depart": 7, "administr": 7, "grant": 7, "act": 7, "behalf": 7, "nonexclus": 7, "paid": 7, "irrevoc": 7, "worldwid": 7, "licens": 7, "deriv": 7, "copi": [7, 10, 13], "publicli": 7, "displai": 7, "permit": 7, "neither": 7, "THE": 7, "nor": 7, "warranti": 7, "express": 7, "OR": 7, "impli": 7, "assum": [7, 10, 11], "liabil": 7, "FOR": 7, "OF": 7, "clearli": 7, "confus": 7, "avail": [7, 16], "addition": 7, "redistribut": 7, "gnu": 7, "foundat": 7, "accordingli": 7, "hope": 7, "without": [7, 11], "merchant": 7, "fit": 7, "purpos": [7, 8], "binari": [7, 8], "form": 7, "notic": 7, "disclaim": 7, "mai": [7, 11], "endors": 7, "promot": 7, "specif": 7, "permiss": 7, "AND": 7, "AS": 7, "BUT": 7, "NOT": 7, "TO": 7, "IN": 7, "NO": 7, "event": 7, "shall": 7, "BE": 7, "liabl": 7, "indirect": 7, "incident": 7, "special": 7, "exemplari": 7, "consequenti": 7, "damag": 7, "procur": 7, "substitut": 7, "servic": 7, "loss": 7, "profit": 7, "busi": 7, "interrupt": 7, "caus": 7, "ON": 7, "theori": [7, 9], "strict": 7, "tort": 7, "neglig": 7, "otherwis": 7, "IF": 7, "advis": 7, "SUCH": 7, "row": 8, "adjac": 8, "input_gener": 8, "input_generator_clean": 8, "abbrevi": 8, "normal_vector": [8, 11, 15], "normal": [8, 11], "visualmod": 8, "dud": 8, "dimens": 8, "poly_info": 8, "rotat": [8, 11], "rotateln": 8, "x0": [8, 14], "y0": [8, 14], "z0": [8, 14], "user_rect": 8, "concatent": 8, "radii_fin": 8, "summari": [8, 11], "rejectsperattempt": 8, "attempt": 8, "translat": [8, 11, 15], "centriod": 8, "triple_point": 8, "warningfiledfngen": 8, "intersection_list": 8, "fracture1": 8, "fracture2": 8, "bound_zon": 8, "lgi": 8, "dump": [8, 10, 11, 12], "zone": [8, 9, 10, 14], "boundary_output": 8, "finalmesh": 8, "brief": 8, "av": [8, 14], "lg": 8, "correct": [8, 10], "convert_ug": 8, "full_mesh_viz": 8, "lagrit_log": 8, "individu": [8, 10, 11, 12, 15], "logx3dgen": 8, "outx3dgen": 8, "mgli": 8, "poli": [8, 11], "contiain": 8, "tri_fractur": 8, "stor": [8, 10], "user_funct": 8, "coarsen": [8, 11], "rescal": 8, "cellinfo": [8, 10], "steadi": [8, 9, 10, 12], "ma": 8, "_dfn_explicit": 8, "attribut": [8, 14], "materialid": [8, 15], "perm": 8, "pboundary_back_n": 8, "pboundary_bottom": 8, "pboundary_front_": 8, "front": [8, 10], "pboundary_top": 8, "middleton": 9, "carei": 9, "currier": 9, "kang": 9, "jimenez": 9, "mart\u0131nez": 9, "porter": 9, "aqueou": 9, "opportun": 9, "influenc": 9, "resourc": [9, 11], "research": [9, 11], "rougier": 9, "zhou": 9, "discoveri": 9, "while": [9, 11, 14, 15, 16], "chapter": 9, "71": 9, "88": 9, "ac": 9, "2016": [9, 11], "mallei": 9, "jim\u00e9nez": 9, "mart\u00ednez": 9, "jw": 9, "ml": 9, "frash": 9, "multi": [9, 16], "phil": 9, "tran": 9, "soc": 9, "aldrich": 9, "woodr": 9, "hamann": 9, "ieee": 9, "transact": 9, "graphic": 9, "2017": [9, 12], "evalu": 9, "advanc": 9, "correl": [9, 11], "implic": [9, 11], "spars": [9, 11, 12], "djidjev": 9, "learn": 9, "predict": [9, 12], "propag": 9, "symposium": 9, "workshop": 9, "ipdpsw": 9, "mohd": 9, "yusof": 9, "passag": [9, 12], "reduct": [9, 12], "phy": 9, "rev": 9, "96": 9, "013304": 9, "jul": 9, "hadgu": 9, "k": [9, 11, 12], "klise": 9, "wang": 9, "compar": 9, "equival": 9, "continuum": [9, 11, 16], "far": 9, "hypothet": 9, "hydrologi": 9, "romano": 9, "valocchi": 9, "battaglia": 9, "bigi": 9, "majella": 9, "mountain": 9, "itali": 9, "procedia": 9, "125": [9, 11], "556": 9, "560": 9, "valera": 9, "guo": 9, "kelli": 9, "matz": 9, "cantu": 9, "percu": 9, "machin": [9, 16], "mudunuru": 9, "geophys": [9, 11], "invers": 9, "character": 9, "mine": [9, 11], "asa": 9, "william": 9, "esteban": 9, "lei": 9, "discontinu": 9, "percol": 9, "jimen\u00e9z": 9, "nonlinear": 9, "interplai": 9, "structur": 9, "heterogen": 9, "mont": 9, "carlo": 9, "osthu": 9, "moor": 9, "hunter": 9, "quantifi": 9, "topolog": 9, "uncertainti": [9, 11], "robust": 9, "fidel": 9, "preserv": 9, "dave": 9, "gowri": 9, "backbon": [9, 12], "net": 9, "bipartit": [9, 12], "multiscal": [9, 12], "lukasczyk": 9, "garth": 9, "leitt": 9, "ahren": 9, "queri": 9, "search": 9, "explor": 9, "ensembl": 9, "engin": [9, 11], "bolster": 9, "impact": [9, 11], "behavior": 9, "2019": 9, "dentz": 9, "link": 9, "geophi": 9, "sol": 9, "ea": 9, "pathwai": 9, "kwickli": 9, "birdsel": 9, "confer": 9, "proceed": 9, "topopah": 9, "spring": 9, "pahut": 9, "mesa": 9, "2nd": 9, "pawar": 9, "merg": 9, "With": [9, 16], "3d": 9, "novel": 9, "frampton": 9, "zou": 9, "disconnect": 9, "textur": 9, "stauffer": 9, "caprock": 9, "rajaram": 9, "insight": 9, "breakthrough": 9, "letter": 9, "emerg": 9, "stabl": 9, "refin": [9, 11], "mediacomput": 9, "panda": 9, "probabilist": 9, "subnetwork": 9, "quantif": 9, "2020": [9, 11], "carneval": 9, "tartarello": 9, "geologi": 9, "cawi": 9, "identif": 9, "geosci": 9, "guthri": 9, "role": 9, "pham": 9, "parashar": 9, "sund": 9, "pohlmann": 9, "solid": [9, 11], "earth": [9, 11], "channel": 9, "preferenti": 9, "e2020wr027986": 9, "calibr": 9, "nevada": 9, "usa": 9, "hydrogeol": 9, "transient": [9, 10], "liangchao": 9, "vladimir": 9, "cvetkov": 9, "infer": 9, "pump": 9, "han": 9, "anomal": 9, "2021": 9, "sole": 9, "mari": 9, "vassallo": 9, "rapid": 9, "forecast": 9, "histori": 9, "unconvent": 9, "bridg": 9, "ambient": 9, "ushijima": 9, "mwesigwa": 9, "safro": 9, "multilevel": 9, "partit": 9, "mathemat": 9, "yingtao": 9, "hu": 9, "wenji": 9, "xu": 9, "liangtong": 9, "zhan": 9, "yunmin": 9, "hydromechan": 9, "cement": 9, "lithospher": 9, "2022": [9, 11], "restrepo": 9, "disk": 9, "self": [10, 11, 12, 13, 14], "symlink": [10, 11, 13], "absolut": [10, 13, 16], "return": [10, 11, 12, 13, 14], "dump_vtk": 10, "portion": [10, 11], "bool": [10, 11, 12], "write": [10, 11, 12], "therein": [10, 12], "sovler": 10, "take": [10, 11], "grid_vtk_fil": 10, "cell_data": 10, "point_data": 10, "local_dfnflow_fil": [10, 15], "inp2vtk_python": [10, 11], "restart": 10, "restart_fil": 10, "checkpoint": 10, "execut": [10, 11, 13, 16], "index_start": 10, "index_finish": 10, "concaten": 10, "delet": [10, 11, 15], "them": [10, 16], "pick": 10, "darcyvel": 10, "loop": 10, "over": [10, 11, 12], "perm_fil": 10, "aper_fil": 10, "uge_fil": [10, 15], "correct_ug": 10, "zone_fil": 10, "boundary_cell_area": 10, "plane": [10, 12], "rel": 10, "account": [10, 11], "inflow_pressur": 10, "outflow_pressur": 10, "boundary_fil": 10, "pascal": 10, "pboundary_left": 10, "local_jobname_effective_perm": 10, "20c": 10, "add_fracture_famili": 11, "family_numb": 11, "number_of_point": 11, "beta_distribut": 11, "beta": 11, "log_mean": 11, "log_std": 11, "exp_mean": 11, "constant": 11, "min_radiu": 11, "max_radiu": 11, "hy_vari": 11, "hy_funct": 11, "hy_param": 11, "rect": [11, 15], "ell": 11, "dein": 11, "log_norm": 11, "fracutr": 11, "probabili": 11, "th": 11, "eboundari": 11, "distribtuion": 11, "2pi": 11, "specfi": 11, "ebeta": 11, "fo": 11, "relationship": 11, "semi": 11, "syntax": 11, "popul": 11, "html": 11, "mon": 11, "aug": 11, "29": 11, "09": 11, "14": 11, "369984": 11, "aspect_ratio": [11, 15], "angle_opt": 11, "orientation_opt": 11, "trend_plung": 11, "dip_strik": 11, "number_of_vertic": 11, "user_defined_": 11, "choic": 11, "orien": 11, "tramsmiss": 11, "attach": 11, "awar": 11, "0e": [11, 15], "npolygon": 11, "by_coord": 11, "arrai": 11, "rtype": 11, "src": [11, 16], "src_path": [11, 15], "1e": 11, "print_user_fracture_inform": 11, "frac_numb": 11, "fracture_numb": 11, "print_domain_paramet": [11, 15], "print_al": 11, "print_family_inform": 11, "check_input": [11, 15], "from_fil": 11, "clean": 11, "own": 11, "separ": 11, "newlin": 11, "kei": 11, "colon": 11, "ie": 11, "place": [11, 16], "comment": 11, "On": [11, 16], "noth": 11, "rais": 11, "continu": 11, "past": 11, "maintain": 11, "futur": 11, "create_network": [11, 15], "succeed": 11, "dfn_gen": 11, "wrapper": 11, "make_working_directori": [11, 15], "dfngen_mesh": 11, "made": [11, 12], "overrid": 11, "routin": [11, 14], "grab_polygon_data": 11, "flag": 11, "store_polygon_data": 11, "store": [11, 15], "np": 11, "alreadi": [11, 16], "prompt": 11, "overwrit": 11, "gen_output": 11, "synopsi": 11, "moduleauthor": 11, "jhyman": 11, "verbos": 11, "output_dir": 11, "dfngen_output_report": 11, "plot": 11, "family_": 11, "toggl": [11, 15], "amount": 11, "progress": 11, "_output_report": 11, "perfectli": 11, "log_": 11, "mathcal": 11, "strength": 11, "independ": 11, "ident": 11, "cor": 11, "semicorrel": 11, "speci": 11, "fi": 11, "fourth": 11, "assumpt": 11, "variat": 11, "stress_based_apertur": 11, "sigma_mat": 11, "friction_angl": 11, "dilation_angl": 11, "critical_shear_displac": 11, "003": 11, "shear_modulu": 11, "10000000000": 11, "min_b": 11, "shear_stiff": 11, "400000000000": 11, "tensor": 11, "calcul": 11, "bandi": 11, "pa": [11, 12], "friction": 11, "dilat": 11, "critic": 11, "shear": 11, "modulu": 11, "stiff": 11, "matthew": 11, "ryan": 11, "e2020jb019754": 11, "baghbanan": 11, "alireza": 11, "lanru": 11, "jing": 11, "2008": 11, "1320": 11, "1334": 11, "zhao": 11, "zhihong": 11, "comparison": 11, "geotechn": 11, "110": 11, "123": 11, "uniform_mesh": 11, "production_mod": 11, "coarse_factor": 11, "min_dist": 11, "max_dist": 11, "concurrent_sampl": 11, "grid_siz": 11, "well_flag": 11, "spatial": 11, "given": [11, 12], "factor": 11, "distanc": 11, "candid": 11, "occup": 11, "occupancy_factor": 11, "higher": [11, 14], "create_mesh_link": 11, "inp2gmv": 11, "inp_fil": [11, 15], "gmv": 11, "viewer": 11, "str": [11, 12], "attribur": 11, "librari": 11, "run_lagrit_script": 11, "lagrit_fil": 11, "output_fil": 11, "quiet": 11, "failur": 11, "success": 11, "add_variable_to_mesh": 11, "variable_fil": 11, "mesh_file_in": 11, "mesh_file_out": 11, "node_bas": 11, "column": [11, 15], "map_to_continuum": 11, "orl": 11, "dir_nam": 11, "level": 11, "octree_dfn": 11, "fracx": 11, "mat_perm": 11, "mat_por": 11, "map2continuum": 11, "perm_fehm": 11, "rock_fehm": 11, "mesh_perm": 11, "permeabilit": 11, "mesh_poros": 11, "check_false_connect": 11, "fmc_filnam": 11, "pickl": 11, "num_false_connect": 11, "num_cell_fals": 11, "voronoi": [11, 16], "false_connect": 11, "tupl": 11, "thu": 11, "source_list": 12, "remove_old_sourc": 12, "old": 12, "graph_typ": 12, "header": [12, 15], "bipartiti": 12, "assocait": 12, "ascii": [12, 15], "json": 12, "output_nam": 12, "dfn_graph": 12, "png": 12, "black": 12, "weight": 12, "greedi": 12, "find": 12, "disjoint": 12, "subgraph": 12, "mm": 12, "shortest": 12, "pressure_in": 12, "pressure_out": 12, "fluid_viscos": 12, "00089": 12, "driven": 12, "outlet": 12, "gtild": 12, "acycl": 12, "volumetr": 12, "travel": 12, "nparticl": 12, "partime_fil": 12, "frac_id_fil": 12, "hdf5": 12, "initial_posit": 12, "dump_traj": 12, "tdrw_flag": 12, "matrix_poros": 12, "matrix_diffus": 12, "fracture_spac": 12, "control_plan": 12, "cp_filenam": 12, "graph_flow": 12, "dump_flag": 12, "tdrw": 12, "ensur": 13, "execuatebl": 13, "move": 14, "well_data": 14, "well_coord": 14, "allboundari": 14, "run_find_well_intersection_point": 14, "subroutin": 14, "redund": 14, "polylin": 14, "well_": 14, "_line": 14, "_volum": 14, "becaus": 15, "favorit": 15, "basic": 15, "taken": 15, "4_user_rects_exampl": 15, "getcwd": 15, "ptdfn_control": 15, "050": 15, "65": 15, "everyth": 15, "mat_fil": 15, "stor_fil": 15, "vtk_file": 15, "num_nod": 15, "mesh_typ": 15, "processor": 15, "depreci": 15, "amanzi": 15, "pars": 15, "local_dfngen_fil": 15, "local_dfntrans_fil": 15, "gen_4_user_rectangl": 15, "parent": 15, "demonstr": 15, "pruning_exampl": 15, "2_core": 15, "aper_nod": 15, "perm_nod": 15, "in_fracture_var": 15, "piec": 15, "pydfnflow": 15, "pydfntran": 15, "pydfngraph": 15, "easiest": 16, "get": 16, "our": 16, "visit": 16, "pull": 16, "dockerhub": 16, "latest": 16, "ti": 16, "exchang": 16, "mount": 16, "local_fold": 16, "expos": 16, "maco": 16, "instruct": 16, "setup": 16, "instead": 16, "git": 16, "pathnam": 16, "throughout": 16, "fix_path": 16, "cd": 16, "bin": 16, "dfnworks_path": 16, "petsc_dir": 16, "petsc_arch": 16, "environment": 16, "pflotran_ex": 16, "python_ex": 16, "lagrit_ex": 16, "vi": 16, "environ": 16, "home": 16, "usernam": 16, "altern": 16, "dfnworksrc": 16, "arch": 16, "darwin": 16, "anaconda3": 16, "fehm_ex": 16, "xfehm_v3": 16, "compli": 16, "bdist_wheel": 16, "pip": 16, "dist": 16, "py3": 16, "whl": 16, "releas": 16, "enabl": 16, "quantiti": 16, "mac": 16, "unix": 16, "ubuntu": 16, "anaconda": 16, "numpi": 16, "h5py": 16, "scipi": 16, "matplotlib": 16, "multiprocess": 16, "argpars": 16, "shutil": 16, "sy": 16, "subprocess": 16, "glob": 16, "fpdf": 16, "coincid": 16, "triangl": 16, "partial": 16, "differenti": 16, "multicompon": 16, "design": 16, "leadership": 16, "supercomput": 16, "workstat": 16, "laptop": 16, "open": 16, "cross": 16, "platform": 16, "bottleneck": 16, "step": 16, "greatli": 16, "speed": 16, "visualis": 16, "xmf": 16, "veri": 16}, "objects": {"false_connections": [[11, 0, 0, "-", "py"]], "graph_transport": [[12, 0, 0, "-", "py"]], "map2continuum": [[11, 0, 0, "-", "py"]], "mesh_dfn": [[11, 0, 0, "-", "py"]], "mesh_dfn_helper": [[11, 0, 0, "-", "py"]], "pydfnworks.dfnFlow": [[10, 0, 0, "-", "fehm"], [10, 0, 0, "-", "flow"], [10, 0, 0, "-", "mass_balance"], [10, 0, 0, "-", "pflotran"]], "pydfnworks.dfnFlow.fehm": [[10, 1, 1, "", "correct_stor_file"], [10, 1, 1, "", "fehm"]], "pydfnworks.dfnFlow.flow": [[10, 1, 1, "", "create_dfn_flow_links"], [10, 1, 1, "", "dfn_flow"], [10, 1, 1, "", "set_flow_solver"]], "pydfnworks.dfnFlow.mass_balance": [[10, 1, 1, "", "effective_perm"]], "pydfnworks.dfnFlow.pflotran": [[10, 1, 1, "", "lagrit2pflotran"], [10, 1, 1, "", "parse_pflotran_vtk_python"], [10, 1, 1, "", "pflotran"], [10, 1, 1, "", "pflotran_cleanup"], [10, 1, 1, "", "write_perms_and_correct_volumes_areas"], [10, 1, 1, "", "zone2ex"]], "pydfnworks.dfnGen.generation": [[11, 0, 0, "-", "generator"], [11, 0, 0, "-", "input_checking"], [11, 0, 0, "-", "stress"]], "pydfnworks.dfnGen.generation.generator": [[11, 1, 1, "", "create_network"], [11, 1, 1, "", "dfn_gen"], [11, 1, 1, "", "grab_polygon_data"], [11, 1, 1, "", "make_working_directory"]], "pydfnworks.dfnGen.generation.input_checking": [[11, 1, 1, "", "print_domain_parameters"], [11, 1, 1, "", "print_family_information"], [11, 0, 0, "-", "user_defined_fracture_functions"]], "pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions": [[11, 1, 1, "", "print_user_fracture_information"]], "pydfnworks.dfnGen.generation.output_report": [[11, 0, 0, "-", "gen_output"]], "pydfnworks.dfnGen.generation.output_report.gen_output": [[11, 1, 1, "", "output_report"]], "pydfnworks.dfnGen.generation.stress": [[11, 1, 1, "", "stress_based_apertures"]], "pydfnworks.dfnGen.meshing": [[11, 0, 0, "-", "add_attribute_to_mesh"], [11, 0, 0, "-", "mesh_dfn"], [11, 0, 0, "-", "mesh_dfn_helper"]], "pydfnworks.dfnGen.meshing.add_attribute_to_mesh": [[11, 1, 1, "", "add_variable_to_mesh"]], "pydfnworks.dfnGen.meshing.mesh_dfn": [[11, 1, 1, "", "mesh_network"]], "pydfnworks.dfnGen.meshing.mesh_dfn_helper": [[11, 1, 1, "", "create_mesh_links"], [11, 1, 1, "", "inp2gmv"], [11, 1, 1, "", "inp2vtk_python"], [11, 1, 1, "", "run_lagrit_script"]], "pydfnworks.dfnGen.meshing.udfm": [[11, 0, 0, "-", "false_connections"], [11, 0, 0, "-", "map2continuum"], [11, 0, 0, "-", "upscale"]], "pydfnworks.dfnGen.meshing.udfm.false_connections": [[11, 1, 1, "", "check_false_connections"]], "pydfnworks.dfnGen.meshing.udfm.map2continuum": [[11, 1, 1, "", "map_to_continuum"]], "pydfnworks.dfnGen.meshing.udfm.upscale": [[11, 1, 1, "", "upscale"]], "pydfnworks.dfnGen.well_package": [[14, 0, 0, "-", "wells"]], "pydfnworks.dfnGen.well_package.wells": [[14, 1, 1, "", "cleanup_wells"], [14, 1, 1, "", "combine_well_boundary_zones"], [14, 1, 1, "", "find_well_intersection_points"], [14, 1, 1, "", "tag_well_in_mesh"]], "pydfnworks.dfnGraph": [[12, 0, 0, "-", "dfn2graph"], [12, 0, 0, "-", "graph_flow"], [12, 0, 0, "-", "graph_transport"], [12, 0, 0, "-", "pruning"]], "pydfnworks.dfnGraph.dfn2graph": [[12, 1, 1, "", "add_fracture_source"], [12, 1, 1, "", "add_fracture_target"], [12, 1, 1, "", "create_graph"], [12, 1, 1, "", "dump_fractures"], [12, 1, 1, "", "dump_json_graph"], [12, 1, 1, "", "load_json_graph"], [12, 1, 1, "", "plot_graph"]], "pydfnworks.dfnGraph.graph_flow": [[12, 1, 1, "", "run_graph_flow"]], "pydfnworks.dfnGraph.graph_transport": [[12, 1, 1, "", "run_graph_transport"]], "pydfnworks.dfnGraph.pruning": [[12, 1, 1, "", "greedy_edge_disjoint"], [12, 1, 1, "", "k_shortest_paths_backbone"]], "pydfnworks.dfnTrans": [[13, 0, 0, "-", "transport"]], "pydfnworks.dfnTrans.transport": [[13, 1, 1, "", "check_dfn_trans_run_files"], [13, 1, 1, "", "copy_dfn_trans_files"], [13, 1, 1, "", "create_dfn_trans_links"], [13, 1, 1, "", "dfn_trans"], [13, 1, 1, "", "run_dfn_trans"]], "upscale": [[11, 0, 0, "-", "py"]]}, "objtypes": {"0": "py:module", "1": "py:function"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"]}, "titleterms": {"exampl": [0, 2, 4], "applic": 0, "carbon": 0, "dioxid": 0, "sequestr": 0, "shale": 0, "energi": 0, "extract": 0, "nuclear": 0, "wast": 0, "repositori": [0, 16], "dfnflow": [1, 10], "dfngen": [2, 8, 11], "c": 2, "gener": [2, 10, 11, 12], "code": 2, "domain": 2, "paramet": [2, 11], "domains": 2, "domainsizeincreas": 2, "numoflay": 2, "layer": 2, "numofregion": 2, "region": 2, "ignoreboundaryfac": 2, "boundaryfac": 2, "network": 2, "stopcondit": 2, "npoli": 2, "famprob": 2, "orientationopt": 2, "angleopt": 2, "h": 2, "disablefram": 2, "printrejectreason": 2, "rejectsperfractur": 2, "radiilistincreas": 2, "visualizationmod": 2, "seed": 2, "keeponlylargestclust": 2, "keepisolatedfractur": 2, "tripleintersect": 2, "removefractureslessthan": 2, "insertuserrectanglesfirst": 2, "forcelargefractur": 2, "output": [2, 8], "outputallradii": 2, "outputacceptedradiiperfamili": 2, "outputfinalradiiperfamili": 2, "fractur": [2, 4, 11], "famili": [2, 11], "ellips": 2, "nfamel": 2, "elay": 2, "eregion": 2, "e_p32target": 2, "enumpoint": 2, "easpect": 2, "orient": 2, "ekappa": 2, "spheric": 2, "coordin": 2, "etheta": 2, "ephi": 2, "trend": 2, "plung": 2, "etrend": 2, "eplung": 2, "dip": 2, "strike": 2, "estrik": 2, "edip": 2, "In": 2, "plane": 2, "rotat": 2, "ebetadistribut": 2, "ebeta": 2, "radiu": 2, "distribut": [2, 4], "edistr": 2, "lognorm": 2, "elogmean": 2, "esd": 2, "elogmin": 2, "elogmax": 2, "truncat": [2, 4], "powerlaw": 2, "ealpha": 2, "emin": 2, "emax": 2, "exponenti": [2, 4], "eexpmean": 2, "eexpmin": 2, "eexpmax": 2, "constant": 2, "econst": 2, "rectangl": 2, "nfamrect": 2, "rlayer": 2, "rregion": 2, "r_p32target": 2, "raspect": 2, "rkappa": 2, "rtheta": 2, "rphi": 2, "rtrend": 2, "rplung": 2, "rstrike": 2, "rdip": 2, "rbetadistribut": 2, "rbeta": 2, "rdistr": 2, "rlogmean": 2, "rsd": 2, "rlogmin": 2, "rlogmax": 2, "ralpha": 2, "rmin": 2, "rmax": 2, "rexpmean": 2, "rexpmin": 2, "rexpmax": 2, "rconst": 2, "user": [2, 11], "defin": 2, "userellipsesonoff": 2, "userell_input_file_path": 2, "nuserel": 2, "number_of_vertic": 2, "radii": 2, "aspect_ratio": 2, "beta": 2, "translat": 2, "userorientationopt": 2, "normal": 2, "trend_plung": 2, "dip_strik": 2, "input": [2, 11], "userellbycoord": 2, "ellbycoord_input_file_path": 2, "nellips": 2, "nnode": 2, "By": 2, "userrectanglesonoff": 2, "userrect_input_file_path": 2, "nuserrect": 2, "userrecbycoord": 2, "rectbycoord_input_file_path": 2, "nrectangl": 2, "polygon": 2, "userpolygonbycoord": 2, "polygonbycoord_input_file_path": 2, "file": [2, 8, 11], "format": 2, "npolygon": 2, "sourc": 2, "document": [2, 3, 6, 15], "doxygen": [2, 15], "dfntran": [3, 13], "4_user_defined_rect": 4, "4_user_defined_ell_uniform": 4, "exp": 4, "length": 4, "tpl": 4, "power": 4, "law": 4, "graph": [4, 12], "base": [4, 11, 12], "prune": 4, "dfnwork": [5, 6, 7, 9, 14, 15, 16], "galleri": 5, "welcom": [6, 7], "2": [6, 7], "7": [6, 7], "To": 7, "obtain": 7, "cite": 7, "version": 7, "v2": 7, "6": 7, "5": 7, "4": 7, "3": 7, "1": 7, "0": 7, "about": 7, "thi": 7, "manual": 7, "contact": 7, "contributor": 7, "lanl": 7, "extern": 7, "copyright": 7, "inform": [7, 11], "run": [8, 10, 11, 13, 15, 16], "lagrit": [8, 11, 16], "pflotran": [8, 10, 16], "public": 9, "pydfnwork": [10, 11, 12, 13, 14, 15, 16], "flow": [10, 12], "fehm": [10, 16], "process": [10, 11], "ad": 11, "from": [11, 15, 16], "paramt": 11, "print": 11, "analysi": 11, "dfn": 11, "addit": [11, 15], "modif": 11, "hydraul": 11, "properti": 11, "background": 11, "stress": 11, "field": 11, "mesh": 11, "primari": 11, "driver": 11, "helper": 11, "method": 11, "udfm": 11, "creat": 11, "an": 11, "upscal": 11, "dfngraph": 12, "function": 12, "transport": [12, 13], "simul": 13, "well": 14, "packag": [14, 15], "python": [15, 16], "command": 15, "line": 15, "us": 15, "The": 15, "class": 15, "jobnam": 15, "ncpu": 15, "dfnflow_fil": 15, "dfngen_fil": 15, "dfntrans_fil": 15, "path": [15, 16], "prune_fil": 15, "flow_solv": 15, "cell_based_apertur": 15, "argument": 15, "modul": 15, "detail": 15, "set": 16, "up": 16, "docker": 16, "contain": 16, "nativ": 16, "build": 16, "github": 16, "clone": 16, "dnfwork": 16, "fix": 16, "test": 16, "directori": 16, "petsc": 16, "instal": 16, "requir": 16, "oper": 16, "system": 16, "cmake": 16, "paraview": 16}, "envversion": {"sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.viewcode": 1, "sphinx": 60}, "alltitles": {"Example Applications": [[0, "example-applications"]], "Carbon dioxide sequestration": [[0, "carbon-dioxide-sequestration"]], "Shale energy extraction": [[0, "shale-energy-extraction"]], "Nuclear waste repository": [[0, "nuclear-waste-repository"]], "dfnFlow": [[1, "dfnflow"]], "dfnGen - C++ Generation Code": [[2, "dfngen-c-generation-code"]], "Domain Parameters": [[2, "domain-parameters"]], "domainSize": [[2, "domainsize"]], "domainSizeIncrease": [[2, "domainsizeincrease"]], "numOfLayers": [[2, "numoflayers"]], "layers": [[2, "layers"]], "numOfRegions": [[2, "numofregions"]], "regions": [[2, "regions"]], "ignoreBoundaryFaces": [[2, "ignoreboundaryfaces"]], "boundaryFaces": [[2, "boundaryfaces"]], "General Network Generation Parameters": [[2, "general-network-generation-parameters"]], "stopCondition": [[2, "stopcondition"]], "nPoly": [[2, "npoly"]], "famProb": [[2, "famprob"]], "orientationOption": [[2, "orientationoption"]], "angleOption": [[2, "angleoption"]], "h": [[2, "h"]], "disableFram": [[2, "disablefram"]], "printRejectReasons": [[2, "printrejectreasons"]], "rejectsPerFracture": [[2, "rejectsperfracture"]], "radiiListIncrease": [[2, "radiilistincrease"]], "visualizationMode": [[2, "visualizationmode"]], "seed": [[2, "seed"]], "keepOnlyLargestCluster": [[2, "keeponlylargestcluster"]], "keepIsolatedFractures": [[2, "keepisolatedfractures"]], "tripleIntersections": [[2, "tripleintersections"]], "removeFracturesLessThan": [[2, "removefractureslessthan"]], "insertUserRectanglesFirst": [[2, "insertuserrectanglesfirst"]], "forceLargeFractures": [[2, "forcelargefractures"]], "General Network Output Parameters": [[2, "general-network-output-parameters"]], "outputAllRadii": [[2, "outputallradii"]], "outputAcceptedRadiiPerFamily": [[2, "outputacceptedradiiperfamily"]], "outputFinalRadiiPerFamily": [[2, "outputfinalradiiperfamily"]], "Fracture Family Generation Parameters: Ellipse": [[2, "fracture-family-generation-parameters-ellipse"]], "Ellipse: General Parameters": [[2, "ellipse-general-parameters"]], "nFamEll": [[2, "nfamell"]], "eLayer": [[2, "elayer"]], "eRegion": [[2, "eregion"]], "e_p32Targets": [[2, "e-p32targets"]], "enumPoints": [[2, "enumpoints"]], "easpect": [[2, "easpect"]], "Ellipse: Fracture Orientation": [[2, "ellipse-fracture-orientation"]], "ekappa": [[2, "ekappa"]], "Ellipse: Spherical Coordinates": [[2, "ellipse-spherical-coordinates"]], "etheta": [[2, "etheta"]], "ephi": [[2, "ephi"]], "Ellipse: Trend & Plunge": [[2, "ellipse-trend-plunge"]], "etrend": [[2, "etrend"]], "eplunge": [[2, "eplunge"]], "Ellipse: Dip & Strike": [[2, "ellipse-dip-strike"]], "estrike": [[2, "estrike"]], "edip": [[2, "edip"]], "Ellipse: In Plane Rotation": [[2, "ellipse-in-plane-rotation"]], "ebetaDistribution": [[2, "ebetadistribution"]], "ebeta": [[2, "ebeta"]], "Ellipse: Fracture Radius Distributions": [[2, "ellipse-fracture-radius-distributions"]], "edistr": [[2, "edistr"]], "Ellipse: Lognormal Distribution": [[2, "ellipse-lognormal-distribution"]], "eLogMean": [[2, "elogmean"]], "esd": [[2, "esd"]], "eLogMin": [[2, "elogmin"]], "eLogMax": [[2, "elogmax"]], "Ellipse: Truncated Powerlaw Distribution": [[2, "ellipse-truncated-powerlaw-distribution"]], "ealpha": [[2, "ealpha"]], "emin": [[2, "emin"]], "emax": [[2, "emax"]], "Ellipse: Exponential Distribution": [[2, "ellipse-exponential-distribution"]], "eExpMean": [[2, "eexpmean"]], "eExpMin": [[2, "eexpmin"]], "eExpMax": [[2, "eexpmax"]], "Ellipse: Constant": [[2, "ellipse-constant"]], "econst": [[2, "econst"]], "Fracture Family Generation Parameters: Rectangle": [[2, "fracture-family-generation-parameters-rectangle"]], "Rectangle: General Parameters": [[2, "rectangle-general-parameters"]], "nFamRect": [[2, "nfamrect"]], "rLayer": [[2, "rlayer"]], "rRegion": [[2, "rregion"]], "r_p32Targets": [[2, "r-p32targets"]], "raspect": [[2, "raspect"]], "Rectangle: Fracture Orientation": [[2, "rectangle-fracture-orientation"]], "rkappa": [[2, "rkappa"]], "Rectangle: Spherical Coordinates": [[2, "rectangle-spherical-coordinates"]], "rtheta": [[2, "rtheta"]], "rphi": [[2, "rphi"]], "Rectangle: Trend & Plunge": [[2, "rectangle-trend-plunge"]], "rtrend": [[2, "rtrend"]], "rplunge": [[2, "rplunge"]], "Rectangle: Dip & Strike": [[2, "rectangle-dip-strike"]], "rstrike": [[2, "rstrike"]], "rdip": [[2, "rdip"]], "Rectangle: In Plane Rotation": [[2, "rectangle-in-plane-rotation"]], "rbetaDistribution": [[2, "rbetadistribution"]], "rbeta": [[2, "rbeta"]], "Rectangle: Fracture Radius Distributions": [[2, "rectangle-fracture-radius-distributions"]], "rdistr": [[2, "rdistr"]], "Rectangle: Lognormal Distribution": [[2, "rectangle-lognormal-distribution"]], "rLogMean": [[2, "rlogmean"]], "rsd": [[2, "rsd"]], "rLogMin": [[2, "rlogmin"]], "rLogMax": [[2, "rlogmax"]], "Rectangle: Truncated Powerlaw Distribution": [[2, "rectangle-truncated-powerlaw-distribution"]], "ralpha": [[2, "ralpha"]], "rmin": [[2, "rmin"]], "rmax": [[2, "rmax"]], "Rectangle: Exponential Distribution": [[2, "rectangle-exponential-distribution"]], "rExpMean": [[2, "rexpmean"]], "rExpMin": [[2, "rexpmin"]], "rExpMax": [[2, "rexpmax"]], "Rectangle: Constant": [[2, "rectangle-constant"]], "rconst": [[2, "rconst"]], "User Defined Fracture Generation Parameters": [[2, "user-defined-fracture-generation-parameters"]], "User Defined Ellipses": [[2, "user-defined-ellipses"]], "userEllipsesOnOff": [[2, "userellipsesonoff"]], "UserEll_Input_File_Path": [[2, "userell-input-file-path"]], "General user defined ellipses parameters": [[2, "general-user-defined-ellipses-parameters"]], "nUserEll": [[2, "nuserell"]], "Number_of_Vertices": [[2, "number-of-vertices"]], "Radii": [[2, "radii"]], "Aspect_Ratio": [[2, "aspect-ratio"]], "AngleOption (User Defined Fracture)": [[2, "angleoption-user-defined-fracture"]], "Beta": [[2, "beta"]], "Translation": [[2, "translation"]], "userOrientationOption": [[2, "userorientationoption"]], "Normal": [[2, "normal"]], "Trend_Plunge": [[2, "trend-plunge"]], "Dip_Strike": [[2, "dip-strike"]], "General Ellipse Input Example": [[2, "general-ellipse-input-example"]], "userEllByCoord": [[2, "userellbycoord"]], "EllByCoord_Input_File_Path": [[2, "ellbycoord-input-file-path"]], "User defined ellipses by coordinate parameters": [[2, "user-defined-ellipses-by-coordinate-parameters"]], "nEllipses": [[2, "nellipses"]], "nNodes": [[2, "nnodes"]], "Coordinates": [[2, "coordinates"]], "Ellipse By Coordinate Example": [[2, "ellipse-by-coordinate-example"]], "User Defined Rectangles": [[2, "user-defined-rectangles"]], "userRectanglesOnOff": [[2, "userrectanglesonoff"]], "UserRect_Input_File_Path": [[2, "userrect-input-file-path"]], "General user defined rectangles parameters": [[2, "general-user-defined-rectangles-parameters"]], "nUserRect": [[2, "nuserrect"]], "General Rectangle Input Example": [[2, "general-rectangle-input-example"]], "userRecByCoord": [[2, "userrecbycoord"]], "RectByCoord_Input_File_Path": [[2, "rectbycoord-input-file-path"]], "User defined rectangles by coordinate parameters": [[2, "user-defined-rectangles-by-coordinate-parameters"]], "nRectangles": [[2, "nrectangles"]], "Rectangle By Coordinate Example": [[2, "rectangle-by-coordinate-example"]], "Polygons": [[2, "polygons"]], "userPolygonByCoord": [[2, "userpolygonbycoord"]], "PolygonByCoord_Input_File_Path:": [[2, "polygonbycoord-input-file-path"]], "Polygon file format": [[2, "polygon-file-format"]], "nPolygons": [[2, "npolygons"]], "Source Code Documentation (Doxygen)": [[2, "source-code-documentation-doxygen"]], "dfnTrans": [[3, "dfntrans"]], "Documentation": [[3, "documentation"]], "Examples": [[4, "examples"]], "4_user_defined_rects": [[4, "user-defined-rects"]], "4_user_defined_ell_uniform": [[4, "user-defined-ell-uniform"]], "exp: Exponentially Distributed fracture lengths": [[4, "exp-exponentially-distributed-fracture-lengths"]], "TPL: Truncated Power-Law": [[4, "tpl-truncated-power-law"]], "Graph-based pruning": [[4, "graph-based-pruning"]], "dfnWorks Gallery": [[5, "dfnworks-gallery"]], "Welcome to dfnWorks 2.7 documentation!": [[6, "welcome-to-dfnworks-2-7-documentation"]], "Welcome To dfnWorks": [[7, "welcome-to-dfnworks"]], "Obtaining dfnWorks": [[7, "obtaining-dfnworks"]], "Citing dfnWorks": [[7, "citing-dfnworks"]], "Versions": [[7, "versions"]], "v2.7": [[7, "v2-7"]], "v2.6": [[7, "v2-6"]], "v2.5": [[7, "v2-5"]], "v2.4": [[7, "v2-4"]], "v2.3": [[7, "v2-3"]], "v2.2": [[7, "v2-2"]], "v2.1": [[7, "v2-1"]], "v2.0": [[7, "v2-0"]], "About this manual": [[7, "about-this-manual"]], "Contact": [[7, "contact"]], "Contributors": [[7, "contributors"]], "LANL": [[7, "lanl"]], "External": [[7, "external"]], "Copyright Information": [[7, "copyright-information"]], "Run Files": [[8, "run-files"]], "dfnGen - output": [[8, "dfngen-output"]], "LaGrit - Output": [[8, "lagrit-output"]], "PFLOTRAN - output": [[8, "pflotran-output"]], "dfnWorks Publications": [[9, "dfnworks-publications"]], "pydfnworks: dfnFlow": [[10, "pydfnworks-dfnflow"]], "Running Flow : General": [[10, "module-pydfnworks.dfnFlow.flow"]], "Running Flow: PFLOTRAN": [[10, "module-pydfnworks.dfnFlow.pflotran"]], "Running Flow: FEHM": [[10, "module-pydfnworks.dfnFlow.fehm"]], "Processing Flow": [[10, "module-pydfnworks.dfnFlow.mass_balance"]], "pydfnworks: dfnGen": [[11, "pydfnworks-dfngen"]], "dfnGen": [[11, "dfngen"]], "Adding Fracture Families": [[11, "adding-fracture-families"]], "Adding User Fractures": [[11, "adding-user-fractures"]], "Adding User Fractures From a File": [[11, "adding-user-fractures-from-a-file"]], "Paramters": [[11, "paramters"], [11, "id1"]], "Print Parameter Information": [[11, "print-parameter-information"]], "Processing Generator Input": [[11, "processing-generator-input"]], "Running the Generator": [[11, "module-pydfnworks.dfnGen.generation.generator"]], "Analysis of Generated DFN": [[11, "module-pydfnworks.dfnGen.generation.output_report.gen_output"]], "Additional Information on the Modification of Hydraulic Properties of the DFN": [[11, "additional-information-on-the-modification-of-hydraulic-properties-of-the-dfn"]], "Modification of hydraulic properties of the DFN based on background stress field": [[11, "module-pydfnworks.dfnGen.generation.stress"]], "Meshing - LaGriT": [[11, "meshing-lagrit"]], "Primary DFN meshing driver": [[11, "module-pydfnworks.dfnGen.meshing.mesh_dfn"]], "Meshing helper methods": [[11, "module-pydfnworks.dfnGen.meshing.mesh_dfn_helper"]], "UDFM": [[11, "udfm"]], "Creating an upscaled mesh of the DFN": [[11, "module-pydfnworks.dfnGen.meshing.udfm.map2continuum"]], "pydfnworks: dfnGraph": [[12, "pydfnworks-dfngraph"]], "General Graph Functions": [[12, "module-pydfnworks.dfnGraph.dfn2graph"]], "Graph-Based Flow and Transport": [[12, "module-pydfnworks.dfnGraph.graph_flow"]], "pydfnworks: dfnTrans": [[13, "pydfnworks-dfntrans"]], "Running Transport Simulations": [[13, "module-pydfnworks.dfnTrans.transport"]], "pydfnworks: Well Package": [[14, "pydfnworks-well-package"]], "dfnWorks - Well Package": [[14, "module-pydfnworks.dfnGen.well_package.wells"]], "pydfnworks: the dfnWorks python package": [[15, "pydfnworks-the-dfnworks-python-package"]], "Running dfnWorks from the command line using pydfnWorks": [[15, "running-dfnworks-from-the-command-line-using-pydfnworks"]], "The DFNWORKS class": [[15, "the-dfnworks-class"]], "jobname": [[15, "jobname"]], "ncpu": [[15, "ncpu"]], "dfnFlow_file/dfnGen_file/dfnTrans_file": [[15, "dfnflow-file-dfngen-file-dfntrans-file"]], "path": [[15, "path"]], "prune_file": [[15, "prune-file"]], "flow_solver": [[15, "flow-solver"]], "cell_based_aperture": [[15, "cell-based-aperture"]], "additional arguments": [[15, "additional-arguments"]], "pydfnWorks : Modules": [[15, "pydfnworks-modules"]], "Detailed Doxygen Documentation": [[15, "detailed-doxygen-documentation"]], "Setting and Running up dfnWorks": [[16, "setting-and-running-up-dfnworks"]], "Docker": [[16, "docker"]], "Running the dfnWorks container": [[16, "running-the-dfnworks-container"]], "Native build from github repository": [[16, "native-build-from-github-repository"]], "Clone the dnfWorks repository": [[16, "clone-the-dnfworks-repository"]], "Fix paths in test directory": [[16, "fix-paths-in-test-directory"]], "Set the LagriT, PETSC, PFLOTRAN, Python, and FEHM paths": [[16, "set-the-lagrit-petsc-pflotran-python-and-fehm-paths"]], "Installing pydfnworks": [[16, "installing-pydfnworks"]], "Installation Requirements for Native Build": [[16, "installation-requirements-for-native-build"]], "Operating Systems": [[16, "operating-systems"]], "Python": [[16, "python"]], "LaGriT": [[16, "lagrit"]], "PFLOTRAN": [[16, "pflotran"]], "FEHM": [[16, "fehm"]], "CMake": [[16, "cmake"]], "Paraview": [[16, "paraview"]]}, "indexentries": {"correct_stor_file() (in module pydfnworks.dfnflow.fehm)": [[10, "pydfnworks.dfnFlow.fehm.correct_stor_file"]], "create_dfn_flow_links() (in module pydfnworks.dfnflow.flow)": [[10, "pydfnworks.dfnFlow.flow.create_dfn_flow_links"]], "dfn_flow() (in module pydfnworks.dfnflow.flow)": [[10, "pydfnworks.dfnFlow.flow.dfn_flow"]], "effective_perm() (in module pydfnworks.dfnflow.mass_balance)": [[10, "pydfnworks.dfnFlow.mass_balance.effective_perm"]], "fehm() (in module pydfnworks.dfnflow.fehm)": [[10, "pydfnworks.dfnFlow.fehm.fehm"]], "lagrit2pflotran() (in module pydfnworks.dfnflow.pflotran)": [[10, "pydfnworks.dfnFlow.pflotran.lagrit2pflotran"]], "module": [[10, "module-pydfnworks.dfnFlow.fehm"], [10, "module-pydfnworks.dfnFlow.flow"], [10, "module-pydfnworks.dfnFlow.mass_balance"], [10, "module-pydfnworks.dfnFlow.pflotran"], [11, "module-false_connections.py"], [11, "module-map2continuum.py"], [11, "module-mesh_dfn.py"], [11, "module-mesh_dfn_helper.py"], [11, "module-pydfnworks.dfnGen.generation.generator"], [11, "module-pydfnworks.dfnGen.generation.input_checking"], [11, "module-pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions"], [11, "module-pydfnworks.dfnGen.generation.output_report.gen_output"], [11, "module-pydfnworks.dfnGen.generation.stress"], [11, "module-pydfnworks.dfnGen.meshing.add_attribute_to_mesh"], [11, "module-pydfnworks.dfnGen.meshing.mesh_dfn"], [11, "module-pydfnworks.dfnGen.meshing.mesh_dfn_helper"], [11, "module-pydfnworks.dfnGen.meshing.udfm.false_connections"], [11, "module-pydfnworks.dfnGen.meshing.udfm.map2continuum"], [11, "module-pydfnworks.dfnGen.meshing.udfm.upscale"], [11, "module-upscale.py"], [12, "module-graph_transport.py"], [12, "module-pydfnworks.dfnGraph.dfn2graph"], [12, "module-pydfnworks.dfnGraph.graph_flow"], [12, "module-pydfnworks.dfnGraph.graph_transport"], [12, "module-pydfnworks.dfnGraph.pruning"], [13, "module-pydfnworks.dfnTrans.transport"], [14, "module-pydfnworks.dfnGen.well_package.wells"]], "parse_pflotran_vtk_python() (in module pydfnworks.dfnflow.pflotran)": [[10, "pydfnworks.dfnFlow.pflotran.parse_pflotran_vtk_python"]], "pflotran() (in module pydfnworks.dfnflow.pflotran)": [[10, "pydfnworks.dfnFlow.pflotran.pflotran"]], "pflotran_cleanup() (in module pydfnworks.dfnflow.pflotran)": [[10, "pydfnworks.dfnFlow.pflotran.pflotran_cleanup"]], "pydfnworks.dfnflow.fehm": [[10, "module-pydfnworks.dfnFlow.fehm"]], "pydfnworks.dfnflow.flow": [[10, "module-pydfnworks.dfnFlow.flow"]], "pydfnworks.dfnflow.mass_balance": [[10, "module-pydfnworks.dfnFlow.mass_balance"]], "pydfnworks.dfnflow.pflotran": [[10, "module-pydfnworks.dfnFlow.pflotran"]], "set_flow_solver() (in module pydfnworks.dfnflow.flow)": [[10, "pydfnworks.dfnFlow.flow.set_flow_solver"]], "write_perms_and_correct_volumes_areas() (in module pydfnworks.dfnflow.pflotran)": [[10, "pydfnworks.dfnFlow.pflotran.write_perms_and_correct_volumes_areas"]], "zone2ex() (in module pydfnworks.dfnflow.pflotran)": [[10, "pydfnworks.dfnFlow.pflotran.zone2ex"]], "add_variable_to_mesh() (in module pydfnworks.dfngen.meshing.add_attribute_to_mesh)": [[11, "pydfnworks.dfnGen.meshing.add_attribute_to_mesh.add_variable_to_mesh"]], "check_false_connections() (in module pydfnworks.dfngen.meshing.udfm.false_connections)": [[11, "pydfnworks.dfnGen.meshing.udfm.false_connections.check_false_connections"]], "create_mesh_links() (in module pydfnworks.dfngen.meshing.mesh_dfn_helper)": [[11, "pydfnworks.dfnGen.meshing.mesh_dfn_helper.create_mesh_links"]], "create_network() (in module pydfnworks.dfngen.generation.generator)": [[11, "pydfnworks.dfnGen.generation.generator.create_network"]], "dfn_gen() (in module pydfnworks.dfngen.generation.generator)": [[11, "pydfnworks.dfnGen.generation.generator.dfn_gen"]], "false_connections.py": [[11, "module-false_connections.py"]], "grab_polygon_data() (in module pydfnworks.dfngen.generation.generator)": [[11, "pydfnworks.dfnGen.generation.generator.grab_polygon_data"]], "inp2gmv() (in module pydfnworks.dfngen.meshing.mesh_dfn_helper)": [[11, "pydfnworks.dfnGen.meshing.mesh_dfn_helper.inp2gmv"]], "inp2vtk_python() (in module pydfnworks.dfngen.meshing.mesh_dfn_helper)": [[11, "pydfnworks.dfnGen.meshing.mesh_dfn_helper.inp2vtk_python"]], "make_working_directory() (in module pydfnworks.dfngen.generation.generator)": [[11, "pydfnworks.dfnGen.generation.generator.make_working_directory"]], "map2continuum.py": [[11, "module-map2continuum.py"]], "map_to_continuum() (in module pydfnworks.dfngen.meshing.udfm.map2continuum)": [[11, "pydfnworks.dfnGen.meshing.udfm.map2continuum.map_to_continuum"]], "mesh_dfn.py": [[11, "module-mesh_dfn.py"]], "mesh_dfn_helper.py": [[11, "module-mesh_dfn_helper.py"]], "mesh_network() (in module pydfnworks.dfngen.meshing.mesh_dfn)": [[11, "pydfnworks.dfnGen.meshing.mesh_dfn.mesh_network"]], "output_report() (in module pydfnworks.dfngen.generation.output_report.gen_output)": [[11, "pydfnworks.dfnGen.generation.output_report.gen_output.output_report"]], "print_domain_parameters() (in module pydfnworks.dfngen.generation.input_checking)": [[11, "pydfnworks.dfnGen.generation.input_checking.print_domain_parameters"]], "print_family_information() (in module pydfnworks.dfngen.generation.input_checking)": [[11, "pydfnworks.dfnGen.generation.input_checking.print_family_information"]], "print_user_fracture_information() (in module pydfnworks.dfngen.generation.input_checking.user_defined_fracture_functions)": [[11, "pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions.print_user_fracture_information"]], "pydfnworks.dfngen.generation.generator": [[11, "module-pydfnworks.dfnGen.generation.generator"]], "pydfnworks.dfngen.generation.input_checking": [[11, "module-pydfnworks.dfnGen.generation.input_checking"]], "pydfnworks.dfngen.generation.input_checking.user_defined_fracture_functions": [[11, "module-pydfnworks.dfnGen.generation.input_checking.user_defined_fracture_functions"]], "pydfnworks.dfngen.generation.output_report.gen_output": [[11, "module-pydfnworks.dfnGen.generation.output_report.gen_output"]], "pydfnworks.dfngen.generation.stress": [[11, "module-pydfnworks.dfnGen.generation.stress"]], "pydfnworks.dfngen.meshing.add_attribute_to_mesh": [[11, "module-pydfnworks.dfnGen.meshing.add_attribute_to_mesh"]], "pydfnworks.dfngen.meshing.mesh_dfn": [[11, "module-pydfnworks.dfnGen.meshing.mesh_dfn"]], "pydfnworks.dfngen.meshing.mesh_dfn_helper": [[11, "module-pydfnworks.dfnGen.meshing.mesh_dfn_helper"]], "pydfnworks.dfngen.meshing.udfm.false_connections": [[11, "module-pydfnworks.dfnGen.meshing.udfm.false_connections"]], "pydfnworks.dfngen.meshing.udfm.map2continuum": [[11, "module-pydfnworks.dfnGen.meshing.udfm.map2continuum"]], "pydfnworks.dfngen.meshing.udfm.upscale": [[11, "module-pydfnworks.dfnGen.meshing.udfm.upscale"]], "run_lagrit_script() (in module pydfnworks.dfngen.meshing.mesh_dfn_helper)": [[11, "pydfnworks.dfnGen.meshing.mesh_dfn_helper.run_lagrit_script"]], "stress_based_apertures() (in module pydfnworks.dfngen.generation.stress)": [[11, "pydfnworks.dfnGen.generation.stress.stress_based_apertures"]], "upscale() (in module pydfnworks.dfngen.meshing.udfm.upscale)": [[11, "pydfnworks.dfnGen.meshing.udfm.upscale.upscale"]], "upscale.py": [[11, "module-upscale.py"]], "add_fracture_source() (in module pydfnworks.dfngraph.dfn2graph)": [[12, "pydfnworks.dfnGraph.dfn2graph.add_fracture_source"]], "add_fracture_target() (in module pydfnworks.dfngraph.dfn2graph)": [[12, "pydfnworks.dfnGraph.dfn2graph.add_fracture_target"]], "create_graph() (in module pydfnworks.dfngraph.dfn2graph)": [[12, "pydfnworks.dfnGraph.dfn2graph.create_graph"]], "dump_fractures() (in module pydfnworks.dfngraph.dfn2graph)": [[12, "pydfnworks.dfnGraph.dfn2graph.dump_fractures"]], "dump_json_graph() (in module pydfnworks.dfngraph.dfn2graph)": [[12, "pydfnworks.dfnGraph.dfn2graph.dump_json_graph"]], "graph_transport.py": [[12, "module-graph_transport.py"]], "greedy_edge_disjoint() (in module pydfnworks.dfngraph.pruning)": [[12, "pydfnworks.dfnGraph.pruning.greedy_edge_disjoint"]], "k_shortest_paths_backbone() (in module pydfnworks.dfngraph.pruning)": [[12, "pydfnworks.dfnGraph.pruning.k_shortest_paths_backbone"]], "load_json_graph() (in module pydfnworks.dfngraph.dfn2graph)": [[12, "pydfnworks.dfnGraph.dfn2graph.load_json_graph"]], "plot_graph() (in module pydfnworks.dfngraph.dfn2graph)": [[12, "pydfnworks.dfnGraph.dfn2graph.plot_graph"]], "pydfnworks.dfngraph.dfn2graph": [[12, "module-pydfnworks.dfnGraph.dfn2graph"]], "pydfnworks.dfngraph.graph_flow": [[12, "module-pydfnworks.dfnGraph.graph_flow"]], "pydfnworks.dfngraph.graph_transport": [[12, "module-pydfnworks.dfnGraph.graph_transport"]], "pydfnworks.dfngraph.pruning": [[12, "module-pydfnworks.dfnGraph.pruning"]], "run_graph_flow() (in module pydfnworks.dfngraph.graph_flow)": [[12, "pydfnworks.dfnGraph.graph_flow.run_graph_flow"]], "run_graph_transport() (in module pydfnworks.dfngraph.graph_transport)": [[12, "pydfnworks.dfnGraph.graph_transport.run_graph_transport"]], "check_dfn_trans_run_files() (in module pydfnworks.dfntrans.transport)": [[13, "pydfnworks.dfnTrans.transport.check_dfn_trans_run_files"]], "copy_dfn_trans_files() (in module pydfnworks.dfntrans.transport)": [[13, "pydfnworks.dfnTrans.transport.copy_dfn_trans_files"]], "create_dfn_trans_links() (in module pydfnworks.dfntrans.transport)": [[13, "pydfnworks.dfnTrans.transport.create_dfn_trans_links"]], "dfn_trans() (in module pydfnworks.dfntrans.transport)": [[13, "pydfnworks.dfnTrans.transport.dfn_trans"]], "pydfnworks.dfntrans.transport": [[13, "module-pydfnworks.dfnTrans.transport"]], "run_dfn_trans() (in module pydfnworks.dfntrans.transport)": [[13, "pydfnworks.dfnTrans.transport.run_dfn_trans"]], "cleanup_wells() (in module pydfnworks.dfngen.well_package.wells)": [[14, "pydfnworks.dfnGen.well_package.wells.cleanup_wells"]], "combine_well_boundary_zones() (in module pydfnworks.dfngen.well_package.wells)": [[14, "pydfnworks.dfnGen.well_package.wells.combine_well_boundary_zones"]], "find_well_intersection_points() (in module pydfnworks.dfngen.well_package.wells)": [[14, "pydfnworks.dfnGen.well_package.wells.find_well_intersection_points"]], "pydfnworks.dfngen.well_package.wells": [[14, "module-pydfnworks.dfnGen.well_package.wells"]], "tag_well_in_mesh() (in module pydfnworks.dfngen.well_package.wells)": [[14, "pydfnworks.dfnGen.well_package.wells.tag_well_in_mesh"]]}}) \ No newline at end of file diff --git a/docs/setup.html b/docs/setup.html new file mode 100644 index 00000000..7ed3de2e --- /dev/null +++ b/docs/setup.html @@ -0,0 +1,299 @@ + + + + + + + Setting and Running up dfnWorks — dfnWorks 2.7 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

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:

+
$ docker pull ees16/dfnworks:latest
+
+
+
+

Running the dfnWorks container

+

The base command for running the dfnWorks container is:

+
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:

+
docker run -ti -v <LOCAL_FOLDER>:/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

+
$ 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:

+
$ 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

  • +
+
$ vi dfnWorks/pydfnworks/pydfnworks/paths.py
+
+
+

For example:

+
os.environ['dfnWorks_PATH'] = '/home/username/dfnWorks/'
+
+
+

Alternatively, you can create a .dfnworksrc file in your home directory with the following format

+
{
+    "dfnworks_PATH": "<your-home-directory>/src/dfnworks-main/",
+    "PETSC_DIR": "<your-home-directory>/src/petsc",
+    "PETSC_ARCH": "arch-darwin-c-debug",
+    "PFLOTRAN_EXE": "<your-home-directory>/src/pflotran/src/pflotran/pflotran",
+    "PYTHON_EXE": "<your-home-directory>/anaconda3/bin/python",
+    "LAGRIT_EXE": "<your-home-directory>/bin/lagrit",
+    "FEHM_EXE": "<your-home-directory>//src/xfehm_v3.3.1"
+}
+
+
+
+
+

Installing pydfnworks

+

Go up into the pydfnworks sub-directory:

+
$ cd dfnWorks/pydfnworks/
+
+
+

Complie The pydfnWorks Package:

+
$ python setup.py bdist_wheel
+
+
+

Install on Your Local Machine:

+
$ 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.

+
+
+

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.

+
+
+

FEHM

+

FEHM is a subsurface multiphase flow code developed at Los Alamos National +Laboratory.

+
+
+

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.

+
+
+

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/

+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file