Source code for flopy.modpath.mp7particlegroup

"""
mp7particlegroup module.  Contains the ParticleGroup, and
    ParticleGroupNodeTemplate classes.


"""

import os

import numpy as np

from ..utils.util_array import Util2d
from .mp7particledata import NodeParticleData, ParticleData


class _Modpath7ParticleGroup:
    """
    Base particle group class that defines common data to all particle
    input styles (MODPATH 7 simulation file items 26 through 32).
    _Modpath7ParticleGroup should not be called directly.

    Parameters
    ----------
    particlegroupname : str
       Name of particle group
    filename : str
        Name of the external file that will contain the particle data.
        If filename is '' or None the particle information for the
        particle group will be written to the MODPATH7 simulation
        file.
    releasedata : float, int, list, or tuple
        If releasedata is a float or an int or a list/tuple with a single
        float or int, releaseoption is set to 1 and release data is the
        particle release time.

    """

    def __init__(self, particlegroupname, filename, releasedata):
        """
        Class constructor

        """
        self.particlegroupname = particlegroupname
        if filename == "":
            filename = None
        self.filename = filename
        if self.filename is None:
            self.external = False
        else:
            self.external = True

        if releasedata is None:
            raise ValueError(
                "releasedata must be provided to instantiate "
                "a MODPATH 7 particle group"
            )

        # convert releasedata to a list, if required
        if isinstance(releasedata, (float, int)):
            releasedata = [releasedata]
        elif isinstance(releasedata, np.ndarray):
            releasedata = releasedata.tolist()

        # validate that releasedata is a list or tuple
        if not isinstance(releasedata, (list, tuple)):
            msg = "releasedata must be a float, int, list, or tuple"
            raise ValueError(msg)

        # process releasedata
        if len(releasedata) == 1:
            releaseoption = 1
            releasetimecount = 0
            releaseinterval = 0
            releasetimes = np.array(releasedata, dtype=np.float32)
        elif len(releasedata) == 3:
            releaseoption = 2
            releasetimecount = int(releasedata[0])
            releaseinterval = int(releasedata[2])
            releasetimes = np.array(releasedata[1], dtype=np.float32)
        elif len(releasedata) == 2:
            releaseoption = 3
            releasetimecount = int(releasedata[0])
            releaseinterval = 0
            # convert releasetimes list or tuple to a numpy array
            if isinstance(releasedata[1], list) or isinstance(
                releasedata[1], tuple
            ):
                releasedata[1] = np.array(releasedata[1])
            if releasedata[1].shape[0] != releasetimecount:
                raise ValueError(
                    "The number of releasetimes data ({}) "
                    "is not equal to releasetimecount "
                    "({}).".format(releasedata[1].shape[0], releasetimecount)
                )
            releasetimes = np.array(releasedata[1], dtype=np.float32)
        else:
            raise ValueError("releasedata must have 1, 2, or 3 entries")

        # set release data
        self.releaseoption = releaseoption
        self.releasetimecount = releasetimecount
        self.releaseinterval = releaseinterval
        self.releasetimes = releasetimes

    def write(self, fp=None, ws="."):
        """
        Common write of MODPATH 7 simulation file items 26 through 32

        Parameters
        ----------
        fp : fileobject
            Fileobject that is open with write access
        ws : str
            Workspace for particle data

        Returns
        -------

        """

        # validate that a valid file object was passed
        if not hasattr(fp, "write"):
            raise ValueError(
                "Cannot write data for particle group {} without passing a "
                "valid file object ({}) open for writing".format(
                    self.particlegroupname, fp
                )
            )

        # item 26
        fp.write(f"{self.particlegroupname}\n")

        # item 27
        fp.write(f"{self.releaseoption}\n")

        if self.releaseoption == 1:
            # item 28
            fp.write(f"{self.releasetimes[0]}\n")
        elif self.releaseoption == 2:
            # item 29
            fp.write(
                "{} {} {}\n".format(
                    self.releasetimecount,
                    self.releasetimes[0],
                    self.releaseinterval,
                )
            )
        elif self.releaseoption == 3:
            # item 30
            fp.write(f"{self.releasetimecount}\n")
            # item 31
            tp = self.releasetimes
            v = Util2d(
                self, (tp.shape[0],), np.float32, tp, name="temp", locat=0
            )
            fp.write(v.string)

        # item 32
        if self.external:
            line = f"EXTERNAL {self.filename}\n"
        else:
            line = "INTERNAL\n"
        fp.write(line)

        return


[docs]class ParticleGroup(_Modpath7ParticleGroup): """ ParticleGroup class to create MODPATH 7 particle group data for location input style 1. Location input style 1 is the most general type of particle group that requires the user to define the location of all particles and associated data (relative release time, drape, and optionally particle ids). Particledata locations can be specified by layer, row, column (locationstyle=1) or nodes (locationstyle=2) and are created with the ParticleData class. Parameters ---------- particlegroupname : str Name of particle group (default is 'PG1') filename : str Name of the external file that will contain the particle data. If filename is '' or None the particle information for the particle group will be written to the MODPATH7 simulation file (default is None). releasedata : float, int, list, or tuple If releasedata is a float or an int or a list/tuple with a single float or int, releaseoption is set to 1 and release data is the particle release time (default is 0.0). particledata : ParticleData ParticleData instance with particle data. If particledata is None, a ParticleData instance will be created with a node-based particle in the center of the first node in the model (default is None). Examples -------- >>> import flopy >>> p = [(2, 0, 0), (0, 20, 0)] >>> p = flopy.modpath.ParticleData(p) >>> pg = flopy.modpath.ParticleGroup(particledata=p) """ def __init__( self, particlegroupname="PG1", filename=None, releasedata=0.0, particledata=None, ): """ Class constructor """ # instantiate base class _Modpath7ParticleGroup.__init__( self, particlegroupname, filename, releasedata ) self.name = "ParticleGroup" # create default node-based particle data if not passed if particledata is None: particledata = ParticleData(structured=False) # convert particledata to a list if a ParticleData type if not isinstance(particledata, ParticleData): raise TypeError( f"{self.name}: particledata must be a " f"ParticleData instance not a {type(particledata)}" ) # set attributes self.inputstyle = 1 self.particlecount = particledata.particlecount self.particleidoption = particledata.particleidoption self.locationstyle = particledata.locationstyle self.particledata = particledata return
[docs] def write(self, fp=None, ws="."): """ Write MODPATH 7 particle data items 1 through 5 Parameters ---------- fp : fileobject Fileobject that is open with write access ws : str Workspace for particle data Returns ------- """ # call base class write method to write common data _Modpath7ParticleGroup.write(self, fp, ws) # open external file if required if self.external: fpth = os.path.join(ws, self.filename) f = open(fpth, "w") else: f = fp # particle data item 1 f.write(f"{self.inputstyle}\n") # particle data item 2 f.write(f"{self.locationstyle}\n") # particle data item 3 f.write(f"{self.particlecount} {self.particleidoption}\n") # particle data item 4 and 5 # call the write method in ParticleData self.particledata.write(f=f) # close the external file if self.external: f.close() return
class _ParticleGroupTemplate(_Modpath7ParticleGroup): """ Base particle group template that defines all data for particle group items 1 through 6. _ParticleGroupTemplate should not be called directly. """ def __init__(self, particlegroupname, filename, releasedata): """ Base class constructor """ # instantiate base class _Modpath7ParticleGroup.__init__( self, particlegroupname, filename, releasedata ) def write(self, fp=None, ws="."): """ Parameters ---------- fp : fileobject Fileobject that is open with write access ws : str Workspace for particle data Returns ------- """ return
[docs]class ParticleGroupLRCTemplate(_ParticleGroupTemplate): """ Layer, row, column particle template class to create MODPATH 7 particle location input style 2. Particle locations for this template are specified by layer, row, column regions. Parameters ---------- particlegroupname : str Name of particle group filename : str Name of the external file that will contain the particle data. If filename is '' or None the particle information for the particle group will be written to the MODPATH7 simulation file. releasedata : float, int, list, or tuple If releasedata is a float or an int or a list/tuple with a single float or int, releaseoption is set to 1 and release data is the particle release time. particledata : LRCParticleData object with input style 2 face and/or node particle data. If particledata is None a default LRCParticleData object is created (default is None). Returns ------- """ def __init__( self, particlegroupname="PG1", filename=None, releasedata=(0.0,), particledata=None, ): """ Class constructor """ self.name = "ParticleGroupLRCTemplate" # instantiate base class _ParticleGroupTemplate.__init__( self, particlegroupname, filename, releasedata ) # validate particledata if particledata is None: particledata = NodeParticleData() self.inputstyle = 2 self.particledata = particledata
[docs] def write(self, fp=None, ws="."): """ Parameters ---------- fp : fileobject Fileobject that is open with write access ws : str Workspace for particle data Returns ------- """ # validate that a valid file object was passed if not hasattr(fp, "write"): raise ValueError( "{}: cannot write data for template without passing a valid " "file object ({}) open for writing".format(self.name, fp) ) # call base class write method to write common data _Modpath7ParticleGroup.write(self, fp, ws) # open external file if required if self.external: fpth = os.path.join(ws, self.filename) f = open(fpth, "w") else: f = fp # item 1 f.write(f"{self.inputstyle}\n") # items 2, 3, 4 or 5, and 6 self.particledata.write(f) # close the external file if self.external: f.close() return
[docs]class ParticleGroupNodeTemplate(_ParticleGroupTemplate): """ Node particle template class to create MODPATH 7 particle location input style 3. Particle locations for this template are specified by nodes. Parameters ---------- particlegroupname : str Name of particle group filename : str Name of the external file that will contain the particle data. If filename is '' or None the particle information for the particle group will be written to the MODPATH7 simulation file. releasedata : float, int, list, or tuple If releasedata is a float or an int or a list/tuple with a single float or int, releaseoption is set to 1 and release data is the particle release time. particledata : NodeParticleData object with input style 3 face and/or node particle data. If particledata is None a default NodeParticleData object is created (default is None). Returns ------- """ def __init__( self, particlegroupname="PG1", filename=None, releasedata=(0.0,), particledata=None, ): """ Class constructor """ self.name = "ParticleGroupNodeTemplate" # instantiate base class _ParticleGroupTemplate.__init__( self, particlegroupname, filename, releasedata ) # validate particledata if particledata is None: particledata = NodeParticleData() self.inputstyle = 3 self.particledata = particledata
[docs] def write(self, fp=None, ws="."): """ Parameters ---------- fp : fileobject Fileobject that is open with write access ws : str Workspace for particle data Returns ------- """ # validate that a valid file object was passed if not hasattr(fp, "write"): raise ValueError( "{}: cannot write data for template without passing a valid " "file object ({}) open for writing".format(self.name, fp) ) # call base class write method to write common data _Modpath7ParticleGroup.write(self, fp, ws) # open external file if required if self.external: fpth = os.path.join(ws, self.filename) f = open(fpth, "w") else: f = fp # item 1 f.write(f"{self.inputstyle}\n") # items 2, 3, 4 or 5, and 6 self.particledata.write(f) # close the external file if self.external: f.close() return