コード例 #1
0
def generate_ocean_nudging_factors(mesh):
    """ Set ocean nudging factor.
        This function is specific for the current Bay or Bay-Delta grid.
        The method and numbers are borrowed from Joseph's 'gen_nudge.f90'

        Parameters
        ----------
        mesh: SchismMesh

        Return
        ------
        numpy array
            nudging factors
    """
    ocean_polygon = Polygon(
        np.array([[526062.827808, 4226489.867824],
                  [566004.141418, 4136786.330387],
                  [497563.036325, 4137201.624542],
                  [499019.230051, 4222752.220430]]))

    nodes = mesh.nodes
    center = np.array((542699., 4183642.)).reshape((1, 2))
    r = (32.3e3, 32.3e3)
    r2 = r[0] * r[0]
    rat = 41.e3 / r[0]
    rmax = 1. / 2. / 86400.

    # Square of distance over Square of radius
    rr = np.sum(np.square(np.subtract(nodes[:, :2], center)), axis=1) / r2
    tnu = rmax * (rr - 1.) / (rat * rat - 1)
    cut = np.empty(tnu.shape)
    cut[:] = rmax
    tnu = np.minimum(tnu, rmax)
    cut[:] = 0.
    tnu = np.maximum(tnu, cut)
    for i, node in enumerate(nodes):
        # if ocean_polygon.check_point_inside_polygon(node[:2]):
        if not ocean_polygon.contains(Point(node[:2])):
            tnu[i] = 0.
    return tnu
コード例 #2
0
    def _partition_nodes_with_polygons(self, polygons, default):
        """ Partition the grid with the given polygons.
            Each node (not element) will be assigned with an integer ID
            which is the index of the polygons.
            If some polygons overlap, the latter one will trump the former one.
            The area that are not covered by any of the given polygons
            will have a default negative one attribute.

            Parameters
            ----------
            polygons: list
                a list of polygon dict (from YAML most of time)

            Returns
            -------
            numpy.ndarray
                attributes
        """
        mesh = self.mesh
        if default is None:
            # Use depth
            attr = np.copy(mesh.nodes[:, 2])
        else:
            # Fill default values
            attr = np.empty(mesh.n_nodes())
            attr.fill(float(default))
        for polygon in polygons:
            name = polygon.get('name')
            vertices = np.array(polygon.get('vertices'))
            if vertices.shape[1] != 2:
                raise ValueError(
                    'The number of coordinates in vertices are wrong.')
            vertices = np.array(vertices)
            poly_type = polygon['type'].lower() \
                if 'type' in polygon else "none"
            attribute = polygon['attribute']
            prop = {'name': name, 'type': poly_type, 'attribute': attribute}
            poly = SchismPolygon(shell=vertices, prop=prop)
            if isinstance(attribute, str):
                is_eqn = True
                expr = self._parse_attribute(attribute)
            else:
                expr = None
                is_eqn = False
            box = np.array(poly.bounds)[[0, 2, 1, 3]]
            nodes = mesh.find_nodes_in_box(box)
            empty = True
            for node_i in nodes:
                node = mesh.nodes[node_i]
                flag = poly.contains(Point(node[:2]))
                if flag:
                    if is_eqn:
                        try:
                            value = eval(expr)
                        except Exception as e:
                            msg = "Egn: %s" % attribute
                            self._logger.error(msg)
                            raise ValueError(
                                "The polygon equation does not seem to be well-formed for polygon: {} "
                                .format(name))
                    else:
                        value = attribute
                    empty = False
                    if poly.type == "none":
                        attr[node_i] = value
                    elif poly.type == "min":
                        if attr[node_i] < value:
                            attr[node_i] = value
                    elif poly.type == "max":
                        if attr[node_i] > value:
                            attr[node_i] = value
                    else:
                        raise Exception(
                            "Not supported polygon type ({}) for polygon ({})".
                            format(poly.type, name))
            if empty:
                msg = "This polygon contains no nodes: %s" % poly.name
                self._logger.error(poly)
                self._logger.error(msg)

        n_missed = sum(1 for i, a in enumerate(attr) if a == default)
        if n_missed > 0:
            msg = "There are %d nodes that do not belong " \
                  "to any polygon." % n_missed
            self._logger.warning(msg)
            if default is not None:
                msg = "Default value of %.f is used for them." % default
                self._logger.warning(msg)
        return attr
コード例 #3
0
def main():
    """ Just a main function
    """
    log = setup_logger()
    # Read mesh
    fpath = "hgrid.gr3"
    mesh = SchismMeshIoFactory().get_reader('gr3').read(fpath)
    nodes_as_point = [Point(node) for node in mesh.nodes]

    # Create salts and nudging_factors
    n_nodes = mesh.n_nodes()
    nvrt = 23
    nudging_factors = np.zeros((n_nodes, ))

    # Read station db
    fpath = "stations_utm.csv"
    stations_db = StationDB(fpath)

    # Read salt time series
    # obs_dir = "../../selfe/BayDeltaSELFE/Data/CDEC_realtime/salt"
    time_basis = datetime(2015, 8, 26)
    time_start = time_basis
    time_window = (time_start, datetime(2015, 9, 2))
    # padding = timedelta(days=1)
    # time_window_padded = (time_window[0] - padding, time_window[1] + padding)

    fpath_csv_15min = '../Data/ec_15min.csv'
    ec_15min = read_csv_from_dss(fpath_csv_15min)
    fpath_csv_1hr = '../Data/ec_1hour.csv'
    ec_1hr = read_csv_from_dss(fpath_csv_1hr)
    ec_1hr.extend([
        rts(ts.data[::4],
            ts.times[0],
            timedelta(hours=1),
            props=deepcopy(ts.props)) for ts in ec_15min
    ])

    # Read RKI to CDEC mapping
    fpath_rki = '../Data/cdec_stas_nearterm.list'
    rki_to_cdec = read_rki_to_cdec(fpath_rki)
    for ts in ec_1hr:
        if rki_to_cdec.get(ts.props['name']) is not None:
            ts.props['name'] = rki_to_cdec[ts.props['name']]

    ec_for_nudging = select_ec_in_stations_db(ec_1hr, stations_db)
    # Cut out time windows
    ec_for_nudging = [ts.window(*time_window) for ts in ec_for_nudging]
    max_gap = 8  # 8 hours
    ec_for_nudging = [
        interpolate_ts_nan(ts, max_gap=max_gap) for ts in ec_for_nudging
    ]
    ec_for_nudging = [
        ts for ts in ec_for_nudging if not np.any(np.isnan(ts.data))
    ]

    log.info("Total %d stations to nudge", len(ec_for_nudging))
    if len(ec_for_nudging) < 1:
        log.warning("No station to nudge...")

    # Convert to PSU
    log.info("Convert EC to PSU...")
    list(map(ec_psu_25c, ec_for_nudging))

    # Filtering, not doing it now
    # ts_filt, filt = med_outliers(ts, level=6., range=[100., None])

    # Collect masks nodes in the mesh for nudging of CDEC stations
    log.info("Creating nudging masks...")
    radius_of_nudging = 500.
    radius_padding = 0.
    nudging_pos = [ts.props['pos'] for ts in ec_for_nudging]
    nudging_areas = [
        Point(p).buffer(radius_of_nudging + radius_padding)
        for p in nudging_pos
    ]
    nudging_mask = np.full((n_nodes, ), -1., dtype=np.int32)
    for node_idx, node in enumerate(nodes_as_point):
        for nudging_idx, ball in enumerate(nudging_areas):
            if ball.contains(node):
                nudging_mask[node_idx] = nudging_idx

    # Nudging factor
    log.info("Creating nudging factors...")
    station_nudging_factor = 1. / 86400.
    station_nudging = np.zeros((n_nodes, ))
    for node_idx, mask in enumerate(nudging_mask):
        if mask >= 0:
            center = Point(ec_for_nudging[mask].props['pos'])
            dist = center.distance(nodes_as_point[node_idx])
            station_nudging[node_idx] = np.max(1. - dist / radius_of_nudging,
                                               0.) * station_nudging_factor
    nudging_factors += station_nudging

    log.info("Add ocean boundary...")
    # Ocean nudging
    # Nudging factor
    ocean_nudging = generate_ocean_nudging_factors(mesh)
    nudging_factors += ocean_nudging

    # Add ocean nudging time series
    ec_for_nudging.append(create_ocean_salt_ts(ec_for_nudging[0]))

    # Add the ocean nudging mask
    for node_idx in range(n_nodes):
        if ocean_nudging[node_idx] > 0.:
            nudging_mask[node_idx] = len(ec_for_nudging) - 1

    # Write SAL_nudge.gr3
    fpath_nudge_out = "SAL_nudge.gr3"
    log.info("Creating %s", fpath_nudge_out)
    SchismMeshIoFactory().get_writer('gr3').write(mesh=mesh,
                                                  fpath=fpath_nudge_out,
                                                  node_attr=nudging_factors)

    # Write nu file
    fpath_salt_nu = 'SAL_nu.in'
    log.info("Creating %s", fpath_salt_nu)
    if os.path.exists(fpath_salt_nu):
        os.remove(fpath_salt_nu)

    times = ec_for_nudging[0].times
    print(times[0], times[-1])
    times = [(t - time_basis).total_seconds() for t in times]
    salt_background = 0.1
    data = np.full((n_nodes, nvrt), salt_background)
    for ts_idx, t in enumerate(times):
        for node_idx, mask in enumerate(nudging_mask):
            if mask >= 0:
                data[node_idx, :] = ec_for_nudging[mask].data[ts_idx]
        with open(fpath_salt_nu, 'ab') as f:
            write_fortran_binary(f, np.array([t]))
            for i in range(data.shape[0]):
                write_fortran_binary(f, data[i])

    # Quick copy and paste: Need to make these a function to reuse
    # Write TEM_nudge.gr3
    fpath_nudge_out = "TEM_nudge.gr3"
    log.info("Creating %s", fpath_nudge_out)
    SchismMeshIoFactory().get_writer('gr3').write(
        mesh=mesh,
        fpath=fpath_nudge_out,
        node_attr=np.zeros_like(nudging_factors))

    # Write nu file
    fpath_temp_nu = 'TEM_nu.in'
    log.info("Creating %s", fpath_temp_nu)
    if os.path.exists(fpath_temp_nu):
        os.remove(fpath_temp_nu)

    # No temperature nudging here. 20 deg C everywhere
    temp_background = 20.
    data = np.full((n_nodes, nvrt), temp_background)
    for ts_idx, t in enumerate(times):
        # for node_idx, mask in enumerate(nudging_mask):
        #     if mask >= 0:
        #         data[node_idx, :] = ec_for_nudging[mask].data[ts_idx]
        with open(fpath_temp_nu, 'ab') as f:
            write_fortran_binary(f, np.array([t]))
            for i in range(data.shape[0]):
                write_fortran_binary(f, data[i])