Ejemplo n.º 1
0
    def processAlgorithm(self, parameters, context, feedback):
        try:
            dir_path = os.path.dirname(os.path.realpath(__file__))
            sys.path.insert(0, dir_path)
            import porepy as pp
            from flow import Flow_Model1
            from tracer import Tracer
            from fcts import read_network, bc_flag
            from math import ceil
            import numpy as np
        except Exception as e:
            feedback.reportError(
                QCoreApplication.translate('Error', '%s' % (e)))
            feedback.reportError(QCoreApplication.translate('Error', ''))
            feedback.reportError(
                QCoreApplication.translate(
                    'Error', 'Please install porepy dependencies'))
            return {}

        #Parameters
        layer = self.parameterAsLayer(parameters, self.Network, context)
        h = self.parameterAsDouble(parameters, self.boundSize,
                                   context) * pp.METER
        steps = self.parameterAsInt(parameters, self.steps, context)
        endv = self.parameterAsInt(parameters, self.end, context) * pp.SECOND
        tol = 1e-8 * pp.METER
        d = self.parameterAsInt(parameters, self.Direction, context)
        lP = parameters[self.lowPressure] * pp.PASCAL
        hP = parameters[self.highPressure] * pp.PASCAL
        mu = parameters[self.mu] * pp.PASCAL * pp.SECOND

        if d == 0:
            direction = "left_to_right"
        elif d == 1:
            direction = "right_to_left"
        elif d == 2:
            direction = "bottom_to_top"
        else:
            direction = "top_to_bottom"

        if lP > hP:
            feedback.reportError(
                QCoreApplication.translate(
                    'Error',
                    'Low pressure value is higher than high pressure value.'))
            return {}

        params = {'INPUT': layer, 'OUTPUT': 'memory:'}
        explode = st.run("native:explodelines",
                         params,
                         context=context,
                         feedback=feedback)

        layer2 = explode['OUTPUT']

        #Create new field to fracture line
        newFields = [
            'ID', 'Pressure', 'Flux', 'Azimuth', 'Tracer', 'StartTime',
            'EndTime'
        ]

        if layer.fields().indexFromName('Transmisiv') == -1:
            feedback.reportError(
                QCoreApplication.translate(
                    'Error',
                    'Please calculate the transmissivity using the Aperture tool or define a new transmissivity field labelled "Transmisiv" in mD.m'
                ))
            return {}

        fields = QgsFields()

        for field in layer.fields():
            if field.name() not in newFields:
                fields.append(QgsField(field.name(), field.type()))

        for field in newFields[:-2]:
            fields.append(QgsField(field, QVariant.Double))
        fields.append(QgsField('StartTime', QVariant.DateTime))
        fields.append(QgsField('EndTime', QVariant.DateTime))

        (writer, dest_id) = self.parameterAsSink(parameters, self.outLine,
                                                 context, fields,
                                                 QgsWkbTypes.LineString,
                                                 layer.sourceCrs())

        #Define fracture geometries
        outDir = os.path.join(tempfile.gettempdir(), 'PorePy')
        if not os.path.exists(outDir):
            os.mkdir(outDir)
        fname = ''.join(
            random.choice(string.ascii_lowercase) for i in range(10))
        outName = os.path.join(outDir, '%s.txt' % (fname))

        k, l = {}, {
        }  #k is the pereambility in m2, l is the fracture length in m
        data = {}

        P = 1000000  #Tolerance for the point precision

        feedback.pushInfo(
            QCoreApplication.translate('Info', 'Reading Fracture Network'))
        field_check = layer2.fields().indexFromName('origLen')
        if field_check != -1:
            feedback.reportError(
                QCoreApplication.translate(
                    'Info',
                    'Warning: Applying the origLen field to calculate fracture length'
                ))
        W = False
        with open(outName, 'w') as f:
            f.write('ID,startx,starty,endx,endy')
            f.write('\n')
            for enum, feature in enumerate(layer2.getFeatures()):
                try:
                    geom = feature.geometry().asPolyline()
                except Exception:
                    geom = feature.geometry().asMultiPolyline()[0]
                start, end = geom[0], geom[-1]

                startx, endx = ceil(start.x() * P) / P, ceil(end.x() * P) / P
                starty, endy = ceil(start.y() * P) / P, ceil(end.y() * P) / P

                t = feature['Transmisiv']
                if t == 0:
                    W = True

                if field_check != -1:
                    lValue = feature['origLen']
                    if type(lValue) != float:
                        feedback.reportError(
                            QCoreApplication.translate(
                                'Info',
                                'Warning: origLen field contains non-float values'
                            ))
                        return {}
                    l[feature.id()] = lValue / feature.geometry().length()
                else:
                    l[feature.id()] = feature.geometry().length()

                if type(t) != float:
                    feedback.reportError(
                        QCoreApplication.translate(
                            'Info',
                            'Warning: Transmisivity field contains non-float values'
                        ))
                    return {}

                k[feature.id()] = t * pp.MILLIDARCY * pp.METER

                row = '%s,%s,%s,%s,%s' % (feature.id(), startx, starty, endx,
                                          endy)
                f.write(row)  #ID,startx,starty,endx,endy
                f.write('\n')

                rows = []
                for field in layer.fields():
                    if field.name() not in newFields:
                        rows.append(feature[field.name()])
                data[feature.id()] = rows

        if len(data) == 0:
            feedback.reportError(
                QCoreApplication.translate(
                    'Info', 'No fractures found in the input dataset'))
            return {}
        elif enum > 2000:
            feedback.reportError(
                QCoreApplication.translate(
                    'Info',
                    'Warning - Fracture network exceeds 2000 branches. To improve performance consider subsampling and/or simplifying the Fracture Network using the "Simplify Network" tool.'
                ))

        if W:
            feedback.reportError(
                QCoreApplication.translate(
                    'Info',
                    'Warning - Transmisivity value(s) of 0 in the fracture network will not produce flow'
                ))

        network, mask, pts_shift = read_network(outName, tol=tol)

        mesh_args = {
            "mesh_size_frac": h,
            "mesh_size_bound": h,
            'file_name': outDir
        }

        feedback.pushInfo(
            QCoreApplication.translate('Info',
                                       'Creating Mesh from %s' % (network)))

        try:
            gb = network.mesh(
                mesh_args,
                dfn=True,
                tol=tol,
            )
        except Exception as e:
            feedback.reportError(QCoreApplication.translate('Info', str(e)))
            feedback.reportError(QCoreApplication.translate('Info', ''))
            feedback.reportError(
                QCoreApplication.translate(
                    'Info',
                    'Failure creating the fracture network mesh. Please check that NetworkGT was properly configured to use gmsh meshing according to the installation guidelines'
                ))
        flow = Flow_Model1(gb)

        param_flow = {
            "tol": tol,
            "k": np.array([k[m] for m in mask]) / mu,
            "length_ratio": np.array([l[m] for m in mask]),
            "flow_direction": direction,
            "low_value": lP,
            "high_value": hP,
            "north": np.array([0, 1, 0])
        }

        param_tracer = {
            "tol": tol,
            "num_steps": steps,
            "end_time": endv,
            "flow_direction": direction,
            "low_value": lP,
            "high_value": hP
        }

        flow.set_data(param_flow, bc_flag)

        feedback.pushInfo(
            QCoreApplication.translate('Info', 'Solving Fluid Flow'))
        flow.solve()

        # get the results for qgis
        if steps > 1:
            feedback.pushInfo(
                QCoreApplication.translate('Info', 'Solving Tracer'))
            tracer = Tracer(gb)
            tracer.set_data(param_tracer, bc_flag)
            tracer.solve()

        feedback.pushInfo(
            QCoreApplication.translate('Info', 'Creating Feature Layer'))
        fet = QgsFeature()
        for g, d in gb:
            # avoid to consider the 0d grids
            if g.dim == 0:
                continue

            # get the data for the current grid
            p = d[pp.STATE][flow.pressure]
            norm_flux = d[pp.STATE][flow.norm_flux]
            azimuth = d[pp.STATE][flow.azimuth]

            # get the cell to nodes map
            cell_nodes = g.cell_nodes()
            indptr = cell_nodes.indptr
            indices = cell_nodes.indices

            # all the data that need to be exported are given as cell_SOMETHING
            for c in np.arange(g.num_cells):
                nodes_loc = indices[indptr[c]:indptr[c + 1]]
                # each column gives a node for the segment
                # NOTE: the nodes are translated compared to the original network
                pnt = g.nodes[:2, nodes_loc] + pts_shift
                # value of the computed fields
                cell_pressure = p[c]
                # flux norm
                cell_norm_flux = norm_flux[c]
                # the fracture id and data
                cell_frac_id = mask[g.frac_num]
                rows = data[cell_frac_id].copy()
                # value of the azimuth
                # NOTE: velocity zero gives nan as azimuth angle
                cell_azimuth = math.degrees(azimuth[c])

                if cell_norm_flux == 0:
                    rows.extend([
                        float(cell_frac_id),
                        float(cell_pressure),
                        float(cell_norm_flux), NULL
                    ])
                else:
                    #cell_azimuth %= 360
                    rows.extend([
                        float(cell_frac_id),
                        float(cell_pressure),
                        float(cell_norm_flux),
                        float(cell_azimuth)
                    ])

                points = [
                    QgsPointXY(pnt[0][0], pnt[1][0]),
                    QgsPointXY(pnt[0][1], pnt[1][1])
                ]
                geom = QgsGeometry.fromPolylineXY(points)
                fet.setGeometry(geom)

                if steps > 1:

                    time = datetime.datetime(1, 1, 1, 0, 0, 0)
                    deltaTime = datetime.timedelta(seconds=endv / steps)

                    for time_step, current_time in enumerate(tracer.all_time):
                        var_name = tracer.variable + "_" + str(time_step)
                        tr = d[pp.STATE][var_name]
                        cell_tracer = tr[c]

                        newRows = rows.copy()
                        newRows.append(float(round(cell_tracer, 6)))
                        newRows.append(str(time))
                        time += deltaTime
                        newRows.append(str(time))

                        fet.setAttributes(newRows)
                        writer.addFeature(fet, QgsFeatureSink.FastInsert)
                else:
                    fet.setAttributes(rows)
                    writer.addFeature(fet, QgsFeatureSink.FastInsert)

        try:
            os.remove(outName)  #Delete temp csv file
        except Exception:
            pass

        return {self.outLine: dest_id}
Ejemplo n.º 2
0
    def processAlgorithm(self, parameters, context, feedback):
        try:
            dir_path = os.path.dirname(os.path.realpath(__file__))
            sys.path.insert(0, dir_path)
            import porepy as pp
            import numpy as np
            import scipy.sparse as sps
            import flow as f
            from fcts import read_cart_grid, bc_flag, argsort_cart_grid
            from tracer import Tracer
        except Exception as e:
            feedback.reportError(
                QCoreApplication.translate('Error', '%s' % (e)))
            feedback.reportError(QCoreApplication.translate('Error', ''))
            feedback.reportError(
                QCoreApplication.translate(
                    'Error', 'Please install porepy dependencies'))
            return {}

        #Parameters
        layer = self.parameterAsLayer(parameters, self.Grid, context)
        xx = self.parameterAsString(parameters, self.xx, context)
        xy = self.parameterAsString(parameters, self.xy, context)
        yy = self.parameterAsString(parameters, self.yy, context)
        steps = self.parameterAsInt(parameters, self.steps, context)
        end = self.parameterAsDouble(parameters, self.end, context) * pp.SECOND
        dV = self.parameterAsInt(parameters, self.Direction, context)
        tol = 1e-4 * pp.METER
        lP = parameters[self.lowPressure] * pp.PASCAL
        hP = parameters[self.highPressure] * pp.PASCAL
        mu = 1e-3 * pp.PASCAL * pp.SECOND  #Define here the dynamic viscosity of the liquid phase in [Pa s]

        if dV == 0:
            direction = "left_to_right"
        elif dV == 1:
            direction = "right_to_left"
        elif dV == 2:
            direction = "bottom_to_top"
        else:
            direction = "top_to_bottom"

        try:
            field_check = layer.fields().indexFromName('Rotation')
            if field_check == -1:
                feedback.reportError(
                    QCoreApplication.translate(
                        'Error',
                        'Invalid Contour Grid layer - please run the contour grid tool prior to the 2D Flow tool'
                    ))
                return {}

        except Exception:
            feedback.reportError(
                QCoreApplication.translate(
                    'Error',
                    'No attribute table found. Do not use the "Selected features only" option'
                ))
            return {}

        if lP > hP:
            feedback.reportError(
                QCoreApplication.translate(
                    'Error',
                    'Low pressure value is higher than high pressure value.'))
            return {}
        newFields = [
            'Pressure', 'Flux', 'Azimuth', 'Tracer', 'StartTime', 'EndTime',
            'Step'
        ]

        fields = QgsFields()
        for field in layer.fields():
            if field.name() not in newFields:
                fields.append(QgsField(field.name(), field.type()))

        for field in newFields[:-3]:
            fields.append(QgsField(field, QVariant.Double))
        fields.append(QgsField('StartTime', QVariant.DateTime))
        fields.append(QgsField('EndTime', QVariant.DateTime))
        fields.append(QgsField('Step', QVariant.Double))

        (writer, dest_id) = self.parameterAsSink(parameters, self.outGrid,
                                                 context, fields,
                                                 QgsWkbTypes.Polygon,
                                                 layer.sourceCrs())

        #Define xx,yy and xy perm
        kxx = []
        kyy = []
        kxy = []

        #Get dictionary of features
        features = {
            feature['Sample_No_']: feature
            for feature in layer.selectedFeatures()
        }
        if len(features) == 0:
            features = {
                feature['Sample_No_']: feature
                for feature in layer.getFeatures()
            }
            extent = layer.extent()
        else:
            extent = layer.boundingBoxOfSelected()
        total = len(features)

        if total == 0:
            feedback.reportError(
                QCoreApplication.translate(
                    'Error', 'No grid cells found in the input dataset'))
            return {}

        c = 0

        # Sort data by Sample No
        features = collections.OrderedDict(sorted(features.items()))
        W = False
        for FID, feature in features.items():
            c += 1
            if total != -1:
                feedback.setProgress(int(c * total))

            xxV, yyV, xyV = feature[xx], feature[yy], feature[xy]
            if xxV == 0 and yyV == 0 and xyV == 0:
                feedback.reportError(
                    QCoreApplication.translate(
                        'Info',
                        'Warning: Grid sample no. %s contains a pereambility of 0 for XX, XY and YY'
                        % (FID)))
                W = True

            kxx.append(xxV * pp.MILLIDARCY)
            kyy.append(yyV * pp.MILLIDARCY)
            kxy.append(xyV * pp.MILLIDARCY)

            if type(xxV) != float or type(xyV) != float or type(yyV) != float:
                feedback.reportError(
                    QCoreApplication.translate(
                        'Info',
                        'Warning: Grid sample no. %s contains non-float values for pereambility measurements'
                        % (FID)))
                W = True
        if W:
            feedback.reportError(
                QCoreApplication.translate(
                    'Info',
                    'Invalid permeability measurements created an empty 2D flow grid!'
                ))
            return {}

        kxx, kyy, kxy = np.array(kxx), np.array(kyy), np.array(kxy)

        rotation = feature['Rotation']
        spacing = feature['Spacing']

        P = 10  #Precision
        #Read grid geometry

        extentGeom = QgsGeometry.fromRect(extent)
        extentGeom = extentGeom.orientedMinimumBoundingBox()

        dWidth = round(extentGeom[4], P)
        dHeight = round(extentGeom[3], P)  #Domain width and height

        Ny = round(dHeight / spacing)
        Nx = round(dWidth / spacing)

        count = Nx * Ny
        if count != c:
            feedback.reportError(
                QCoreApplication.translate(
                    'Warning',
                    'Warning: Selected contour grid does not appear to be a rectangle.'
                ))
            feedback.reportError(QCoreApplication.translate('Warning', ''))

        # Read the grid
        gb = read_cart_grid(Nx, Ny, dWidth, dHeight)

        feedback.pushInfo(
            QCoreApplication.translate(
                'Output',
                'Constructing grid with %s columns and %s rows a domain size (width x height) of %s x %s.'
                % (Nx, Ny, dWidth, dHeight)))

        # mask that map the permeability from qgis to pp, and vice-versa
        mask, inv_mask = argsort_cart_grid(Nx, Ny)

        param_flow = {
            "tol": tol,
            "kxx": kxx[mask] / mu,
            "kyy": kyy[mask] / mu,
            "kxy": kxy[mask] / mu,
            "flow_direction": direction,
            "low_value": lP,
            "high_value": hP,
            "north": np.array([0, 1, 0]),
        }
        try:
            flow = f.Flow(gb)
            flow.set_data(param_flow, bc_flag)
            flow.solve()
        except Exception as e:
            feedback.reportError(QCoreApplication.translate('Error', str(e)))
            return {}

        if steps > 1:
            param_tracer = {
                "tol": tol,
                "num_steps": steps,
                "end_time": end,
                "flow_direction": direction,
                "low_value": lP,
                "high_value": hP
            }

            tracer = Tracer(gb)
            tracer.set_data(param_tracer, bc_flag)
            tracer.solve()

        t = []
        for g, d in gb:
            p = d[pp.STATE][flow.pressure]
            v = d[pp.STATE][flow.norm_flux]
            a = d[pp.STATE][flow.azimuth]
            if steps > 1:
                for time_step, current_time in enumerate(tracer.all_time):
                    var_name = tracer.variable + "_" + str(time_step)
                    traceD = d[pp.STATE][var_name]
                    traceD = traceD[inv_mask]
                    t.append(traceD)

        #Reshape the output data
        p = p[inv_mask]
        v = v[inv_mask]
        a = a[inv_mask]

        feedback.pushInfo(
            QCoreApplication.translate('Output', 'Updating Feature Layer'))

        #Update the dataset
        fet = QgsFeature()
        for enum, FID in enumerate(features):
            feature = features[FID]
            FID = feature.id()
            geom = feature.geometry()
            if total != -1:
                feedback.setProgress(int(enum * total))

            rows = []
            for field in layer.fields():
                if field.name() not in newFields:
                    rows.append(feature[field.name()])

            aV = math.degrees(float(a[enum])) + rotation
            if type(aV) == float:
                aV %= 360
            if dV < 2:
                if aV < 180:
                    aV += 180
                else:
                    aV -= 180

            rows.extend([
                round(float(p[enum]), P),
                round(float(v[enum]), P),
                round(float(aV), 2)
            ])

            if steps > 1:

                time = datetime.datetime(1, 1, 1, 0, 0, 0)
                deltaTime = datetime.timedelta(seconds=end / steps)

                for n in range(len(t)):
                    newRows = rows.copy()
                    newRows.append(round(float(t[n][enum]), P))
                    newRows.append(str(time))
                    time += deltaTime
                    newRows.append(str(time))
                    newRows.append(int(n))

                    fet.setGeometry(geom)
                    fet.setAttributes(newRows)
                    writer.addFeature(fet, QgsFeatureSink.FastInsert)
            else:
                fet.setGeometry(geom)
                fet.setAttributes(rows)
                writer.addFeature(fet, QgsFeatureSink.FastInsert)

        return {self.outGrid: dest_id}