8. Insert Wells
In this step, we will generate two cylindrical 'wells', refine the mesh
`MONAME` around them, and identify a line of nodes that will be the well
source/sink for boundary conditions (ultimately writing these nodes
to `zone` files).
### 8.1 Generating Cylindrical Tetrahedral Wells
First, we define variables for the well's position (`XWELL`,`YWELL`), radius (`RADIUS_WELL`), and number of nodes across the cylindrical radius
(`NRADIUS`):
```
define / XWELL1 / 1234.56
define / YWELL1 / 1987.65
define / XWELL2 / 2243.21
define / YWELL2 / 1212.34
define / RADIUS_WELL / 25.0
define / NRADIUS / 2
```
Now, we create a cylindrical point cloud defining the first well using
`createpts / rtz`:
```
cmo / create / mo_well1 / / / tet
createpts / rtz / NRADIUS 9 NZ / 0. 0. 3100. / RADIUS_WELL 360. 1500. / 1 1 1
```
This creates a point cloud centered around (0,0,0) with a radius of `RADIUS_WELL`, an angular component spanning a full 360 degrees (`φ = {0., 360.}`), and a Z range of `{3100.,1500.}`.
Run `filter`, `rmpoint / compress`, and set `imt` to 1 for the well:
```
filter / 1 0 0
rmpoint / compress
cmo / setatt / mo_well1 / imt / 1 0 0 / 1
```
Next, connect the point cloud into a tetrahedral mesh and translate the
X and Y origin to `{XWELL1,YWELL1}`:
```
connect
resetpts / itp
cmo / printatt / mo_well1 / -xyz- / minmax
trans / 1 0 0 / 0. 0. 0. / XWELL1 YWELL1 0.0
cmo / printatt / mo_well1 / -xyz- / minmax
```
The first 'well' mesh object has been generated. Repeat this process with different parameters to create the second well:
```
cmo / create / mo_well2 / / / tet
createpts / rtz / NRADIUS 9 NZ / 0. 0. 3100. / RADIUS_WELL 360. 2200. / 1 1 1
filter / 1 0 0
rmpoint / compress
cmo / setatt / mo_well1 / imt / 1 0 0 / 1
connect
resetpts / itp
cmo / printatt / mo_well2 / -xyz- / minmax
trans / 1 0 0 / 0. 0. 0. / XWELL2 YWELL2 0.0
cmo / printatt / mo_well2 / -xyz- / minmax
```
Finally, join the two distinct wells into a single mesh object with `addmesh / merge`:
```
addmesh / merge / mo_wells / mo_well1 mo_well2
```
### 8.2 Refining `MONAME` around the wells
As we did for the fault in step 7, we refine the main mesh `MONAME` around the wells:
```
# First pass refinement
cmo / select / MONAME
intersect_elements / MONAME / mo_wells / if_inter
eltset / e_refine / if_inter / gt / 0
refine/ eltset / eltset get e_refine
cmo / setatt / MONAME / if_inter / 1 0 0 / 0
eltset / e_refine / delete
# Second pass refinement
cmo / select / MONAME
intersect_elements / MONAME / mo_wells / if_inter
eltset / e_refine / if_inter / gt / 0
refine/ eltset / eltset get e_refine
cmo / setatt / MONAME / if_inter / 1 0 0 / 0
eltset / e_refine / delete
# Third pass refinement
cmo / select / MONAME
intersect_elements / MONAME / mo_wells / if_inter
eltset / e_refine / if_inter / gt / 0
refine/ eltset / eltset get e_refine
cmo / setatt / MONAME / if_inter / 1 0 0 / 0
eltset / e_refine / delete
```
The refinement process returns a octree grid object, which stores information about parent-children relationships, among other properties. It's important, as the prepare to finalize the mesh for exporting, to strip this information and convert the octree grid object to a standard mesh object.
This conversion is done through the `grid2grid / tree_to_fe` command:
```
grid2grid / tree_to_fe / mohex_octree / mohex
define / MONAME / mohex_octree
```
### 8.3 Writing `zone` files based on well distances
The `zone` files are lists of node numbers in [FEHM](https://fehm.lanl.gov) file format and used to identify materials, well source/sinks, and boundary conditions.
In this subsection, we will generate `zone` files describing all nodes within 32, 16, 8, 4, 2 and 1 meters of the wells.
To begin, we will compute the well point cloud again, as we did above. First, for well 1:
```
cmo / create / mo_pts1
createpts / rtz / 2 2 1000 / 0. 0. 3100. / 0.0 360. 2200. / 1 1 1
trans / 1 0 0 / 0. 0. 0. / XWELL1 YWELL1 0.0
```
Then for well 2:
```
cmo / create / mo_pts2
createpts / rtz / 2 2 1000 / 0. 0. 3100. / 0.0 360. 2200. / 1 1 1
trans / 1 0 0 / 0. 0. 0. / XWELL2 YWELL2 0.0
```
and joining them into a single mesh object, `mo_pts`:
```
addmesh / merge / mo_pts / mo_pts1 / mo_pts2
cmo / select / mo_pts
filter / 1 0 0
rmpoint / compress
```
Next, we will compute a distance field attribute, `dfield_well`, which is a node-based attribute storing the Euclidean distance from `node_i` in one mesh to the closest node in another mesh. In other words, all nodes in `MONAME` store their distance to the closest well (`mo_pts`) node.
```
compute / distance_field / MONAME / mo_pts / dfield_well
```
Clean up unneeded mesh objects:
```
cmo / delete / mo_pts1
cmo / delete / mo_pts2
cmo / delete / mo_pts
cmo / delete / mo_wells
cmo / delete / mo_well1
cmo / delete / mo_well2
```
And finally, for each mesh-to-well distance in {32,16,8,4,2,1} (which is stored in `dfield_well`), (i) create a pset object
containing all nodes within that distance, and (ii) write those nodes to a `zone` file:
```
cmo / select / MONAME
pset / pwell / attribute / dfield_well / 1 0 0 / le / 1.0
pset / pwell / zone / zone_radius_01.0.zone
pset / pwell / attribute / dfield_well / 1 0 0 / le / 2.0
pset / pwell / zone / zone_radius_02.0.zone
pset / pwell / attribute / dfield_well / 1 0 0 / le / 4.0
pset / pwell / zone / zone_radius_04.0.zone
pset / pwell / attribute / dfield_well / 1 0 0 / le / 8.0
pset / pwell / zone / zone_radius_08.0.zone
pset / pwell / attribute / dfield_well / 1 0 0 / le / 16.0
pset / pwell / zone / zone_radius_16.0.zone
pset / pwell / attribute / dfield_well / 1 0 0 / le / 32.0
pset / pwell / zone / zone_radius_32.0.zone
```