Example #1
0
 def __call__(self, inverse_solver, count, data):
     """
     :param inverse_sovler: the solver (e.g. :class:`~InvSolver_Tikhonov`) we are listening to.
     :param count: the iteration number.
     :param data: dictionary of data related to the iteration.
     """
     method = inverse_solver.method
     if method != 'sd' and method != 'nlcg' and method != 'ign':
         if not self.didWarning:
             PISM.verbPrintf(
                 1,
                 PISM.Context().com,
                 '\nWarning: unable to monitor adjoint for inverse method: %s\nOption -inv_monitor_adjoint ignored\n'
                 % method)
         self.didWarning = True
         return
     fp = inverse_solver.forward_problem
     d = PISM.invert.sipletools.PISMLocalVector(data.d)
     r = PISM.invert.sipletools.PISMLocalVector(data.r)
     self.Td = fp.T(d, self.Td)
     self.TStarR = fp.TStar(r, out=self.TStarR)
     ip1 = fp.domainIP(d, self.TStarR)
     ip2 = fp.rangeIP(self.Td, r)
     logMessage("adjoint test: <Td,r>=%g <d,T^*r>=%g (percent error %g)",
                ip1, ip2, (abs(ip1 - ip2)) / max(abs(ip1), abs(ip2)))
Example #2
0
File: ssa.py Project: pism/pism
    def __call__(self, invssa_solver, it, data):
        """
        :param inverse_solver: the solver (e.g. :class:`~InvSSASolver_Tikhonov`) we are listening to.
        :param count: the iteration number.
        :param data: dictionary of data related to the iteration.
        """

        grid = invssa_solver.ssarun.grid

        if self.misfit_type is None:
            self.misfit_type = grid.ctx().config().get_string("inverse.state_func")

        method = invssa_solver.method
        if method == 'ign' or method == 'sd' or method == 'nlcg':
            import PISM.invert.sipletools
            fp = invssa_solver.forward_problem
            r = PISM.invert.sipletools.PISMLocalVector(data.residual)
            Jmisfit = fp.rangeIP(r, r)
        elif 'JState' in data:
            Jmisfit = data.JState
        else:
            raise RuntimeError("Unable to report misfits for inversion method: %s" % method)

        if self.misfit_type == "meansquare":
            velScale_m_per_year = grid.ctx().config().get_double("inverse.ssa.velocity_scale")

            rms_misfit = math.sqrt(Jmisfit) * velScale_m_per_year

            logMessage("Misfit: sqrt(J_misfit) = %.8g (m/a)\n" % rms_misfit)
            self.misfit_history.append(rms_misfit)
        else:
            logMessage("Misfit: J_misfit = %.8g (dimensionless)\n" % Jmisfit)
            self.misfit_history.append(Jmisfit)
Example #3
0
def logging_test():
    "Test the PISM.logging module"
    grid = create_dummy_grid()

    import PISM.logging as L

    PISM.PIO(grid.com, "netcdf3", "log.nc", PISM.PISM_READWRITE_MOVE)
    c = L.CaptureLogger("log.nc")

    L.clear_loggers()

    L.add_logger(L.print_logger)
    L.add_logger(c)

    L.log("log message\n", L.kError)

    L.logError("error message\n")

    L.logWarning("warning message\n")

    L.logMessage("log message (again)\n")

    L.logDebug("debug message\n")

    L.logPrattle("prattle message\n")

    c.write()  # default arguments
    c.readOldLog()

    PISM.PIO(grid.com, "netcdf3", "other_log.nc", PISM.PISM_READWRITE_MOVE)
    c.write("other_log.nc", "other_log")  # non-default arguments
Example #4
0
    def __call__(self, invssa_solver, it, data):
        """
        :param inverse_solver: the solver (e.g. :class:`~InvSSASolver_Tikhonov`) we are listening to.
        :param count: the iteration number.
        :param data: dictionary of data related to the iteration.
        """

        grid = invssa_solver.ssarun.grid

        if self.misfit_type is None:
            self.misfit_type = grid.ctx().config().get_string("inverse.state_func")

        method = invssa_solver.method
        if method == 'ign' or method == 'sd' or method == 'nlcg':
            import PISM.invert.sipletools
            fp = invssa_solver.forward_problem
            r = PISM.invert.sipletools.PISMLocalVector(data.residual)
            Jmisfit = fp.rangeIP(r, r)
        elif 'JState' in data:
            Jmisfit = data.JState
        else:
            raise RuntimeError("Unable to report misfits for inversion method: %s" % method)

        if self.misfit_type == "meansquare":
            velScale_m_per_year = grid.ctx().config().get_number("inverse.ssa.velocity_scale")

            rms_misfit = math.sqrt(Jmisfit) * velScale_m_per_year

            logMessage("Misfit: sqrt(J_misfit) = %.8g (m/a)\n" % rms_misfit)
            self.misfit_history.append(rms_misfit)
        else:
            logMessage("Misfit: J_misfit = %.8g (dimensionless)\n" % Jmisfit)
            self.misfit_history.append(Jmisfit)
Example #5
0
 def __call__(self, inverse_solver, count, data):
     """
     :param inverse_sovler: the solver (e.g. :class:`~InvSSASolver_Tikhonov`) we are listening to.
     :param count: the iteration number.
     :param data: dictionary of data related to the iteration.
     """
     fp = inverse_solver.forward_problem
     r = PISM.invert.sipletools.PISMLocalVector(data.r)
     d = PISM.invert.sipletools.PISMLocalVector(data.d)
     self.Td = fp.T(d, self.Td)
     self.TStarR = fp.TStar(r, out=self.TStarR)
     ip1 = fp.domainIP(d, self.TStarR)
     ip2 = fp.rangeIP(self.Td, r)
     logMessage("adjoint test: <Td,r>=%g <d,T^*r>=%g (percent error %g)", ip1, ip2, (abs(ip1 - ip2)) / max(abs(ip1), abs(ip2)))
Example #6
0
File: pismi.py Project: pism/pism
def adjustTauc(mask, tauc):
    """Where ice is floating or land is ice-free, tauc should be adjusted to have some preset default values."""

    logMessage("  Adjusting initial estimate of 'tauc' to match PISM model for floating ice and ice-free bedrock.\n")

    grid = mask.grid()
    high_tauc = grid.ctx().config().get_double("basal_yield_stress.ice_free_bedrock")

    with PISM.vec.Access(comm=tauc, nocomm=mask):
        for (i, j) in grid.points():
            if mask.ocean(i, j):
                tauc[i, j] = 0
            elif mask.ice_free(i, j):
                tauc[i, j] = high_tauc
Example #7
0
 def __call__(self, inverse_solver, count, data):
     """
     :param inverse_sovler: the solver (e.g. :class:`~InvSSASolver_Tikhonov`) we are listening to.
     :param count: the iteration number.
     :param data: dictionary of data related to the iteration.
     """
     fp = inverse_solver.forward_problem
     r = PISM.invert.sipletools.PISMLocalVector(data.r)
     d = PISM.invert.sipletools.PISMLocalVector(data.d)
     self.Td = fp.T(d, self.Td)
     self.TStarR = fp.TStar(r, out=self.TStarR)
     ip1 = fp.domainIP(d, self.TStarR)
     ip2 = fp.rangeIP(self.Td, r)
     logMessage("adjoint test: <Td,r>=%g <d,T^*r>=%g (percent error %g)", ip1, ip2, (abs(ip1 - ip2)) / max(abs(ip1), abs(ip2)))
Example #8
0
def adjustTauc(mask, tauc):
    """Where ice is floating or land is ice-free, tauc should be adjusted to have some preset default values."""

    logMessage(
        "  Adjusting initial estimate of 'tauc' to match PISM model for floating ice and ice-free bedrock.\n"
    )

    grid = mask.grid()
    high_tauc = grid.ctx().config().get_number(
        "basal_yield_stress.ice_free_bedrock")

    with PISM.vec.Access(comm=tauc, nocomm=mask):
        for (i, j) in grid.points():
            if mask.ocean(i, j):
                tauc[i, j] = 0
            elif mask.ice_free(i, j):
                tauc[i, j] = high_tauc
Example #9
0
 def __call__(self, inverse_solver, count, data):
     """
     :param inverse_sovler: the solver (e.g. :class:`~InvSolver_Tikhonov`) we are listening to.
     :param count: the iteration number.
     :param data: dictionary of data related to the iteration.
     """
     method = inverse_solver.method
     if method != 'sd' and method != 'nlcg' and method != 'ign':
         if not self.didWarning:
             PISM.verbPrintf(1, PISM.Context().com, '\nWarning: unable to monitor adjoint for inverse method: %s\nOption -inv_monitor_adjoint ignored\n' % method)
         self.didWarning = True
         return
     fp = inverse_solver.forward_problem
     d = PISM.invert.sipletools.PISMLocalVector(data.d)
     r = PISM.invert.sipletools.PISMLocalVector(data.r)
     self.Td = fp.T(d, self.Td)
     self.TStarR = fp.TStar(r, out=self.TStarR)
     ip1 = fp.domainIP(d, self.TStarR)
     ip2 = fp.rangeIP(self.Td, r)
     logMessage("adjoint test: <Td,r>=%g <d,T^*r>=%g (percent error %g)", ip1, ip2, (abs(ip1 - ip2)) / max(abs(ip1), abs(ip2)))
Example #10
0
def logging_test():
    "Test the PISM.logging module"
    grid = create_dummy_grid()

    import PISM.logging as L

    log_filename = filename("log")
    try:
        PISM.File(grid.com, log_filename, PISM.PISM_NETCDF3,
                  PISM.PISM_READWRITE_MOVE)
        c = L.CaptureLogger(log_filename)

        L.clear_loggers()

        L.add_logger(L.print_logger)
        L.add_logger(c)

        L.log("log message\n", L.kError)

        L.logError("error message\n")

        L.logWarning("warning message\n")

        L.logMessage("log message (again)\n")

        L.logDebug("debug message\n")

        L.logPrattle("prattle message\n")

        c.write()  # default arguments
        c.readOldLog()
    finally:
        os.remove(log_filename)

    log_filename = filename("other_log")
    try:
        PISM.File(grid.com, log_filename, PISM.PISM_NETCDF3,
                  PISM.PISM_READWRITE_MOVE)
        c.write(log_filename, "other_log")  # non-default arguments
    finally:
        os.remove(log_filename)
Example #11
0
def logging_test():
    "Test the PISM.logging module"
    grid = create_dummy_grid()
    pio = PISM.PIO(grid.com, "netcdf3")

    import PISM.logging as L

    pio.open("log.nc", PISM.PISM_READWRITE_MOVE)
    pio.close()
    c = L.CaptureLogger("log.nc")

    L.clear_loggers()

    L.add_logger(L.print_logger)
    L.add_logger(c)

    PISM.setVerbosityLevel(2)

    L.log("log message\n", L.kError)

    L.logError("error message\n")

    L.logWarning("warning message\n")

    L.logMessage("log message (again)\n")

    L.logDebug("debug message\n")

    L.logPrattle("prattle message\n")

    c.write()                   # default arguments
    c.readOldLog()

    pio.open("other_log.nc", PISM.PISM_READWRITE_MOVE)
    pio.close()
    c.write("other_log.nc", "other_log")  # non-default arguments
Example #12
0
def printTikhonovProgress(invssasolver, it, data):
    "Report on the progress of a Tikhonov iteration."
    eta = data.tikhonov_penalty
    stateVal = data.JState
    designVal = data.JDesign
    sWeight = 1
    dWeight = 1.0 / eta

    norm_type = PISM.PETSc.NormType.NORM_2

    logMessage("design objective %.8g; weighted %.8g\n" % (designVal, designVal * dWeight))
    if 'grad_JTikhonov' in data:
        logMessage("gradient: design %.8g state %.8g sum %.8g\n" % (data.grad_JDesign.norm(norm_type) * dWeight,
                                                                    data.grad_JState.norm(norm_type) * sWeight,
                                                                    data.grad_JTikhonov.norm(norm_type)))
    else:
        logMessage("gradient: design %.8g state %.8g; constraints: %.8g\n" % (data.grad_JDesign.norm(norm_type) * dWeight,
                                                                              data.grad_JState.norm(norm_type) * sWeight,
                                                                              data.constraints.norm(norm_type)))
    logMessage("tikhonov functional: %.8g\n" % (stateVal * sWeight + designVal * dWeight))
Example #13
0
File: ssa.py Project: pism/pism
def printTikhonovProgress(invssasolver, it, data):
    "Report on the progress of a Tikhonov iteration."
    eta = data.tikhonov_penalty
    stateVal = data.JState
    designVal = data.JDesign
    sWeight = 1
    dWeight = 1.0 / eta

    norm_type = PISM.PETSc.NormType.NORM_2

    logMessage("design objective %.8g; weighted %.8g\n" % (designVal, designVal * dWeight))
    if 'grad_JTikhonov' in data:
        logMessage("gradient: design %.8g state %.8g sum %.8g\n" % (data.grad_JDesign.norm(norm_type) * dWeight,
                                                                    data.grad_JState.norm(norm_type) * sWeight,
                                                                    data.grad_JTikhonov.norm(norm_type)))
    else:
        logMessage("gradient: design %.8g state %.8g; constraints: %.8g\n" % (data.grad_JDesign.norm(norm_type) * dWeight,
                                                                              data.grad_JState.norm(norm_type) * sWeight,
                                                                              data.constraints.norm(norm_type)))
    logMessage("tikhonov functional: %.8g\n" % (stateVal * sWeight + designVal * dWeight))
Example #14
0
        if not PISM.util.fileHasVariable(input_filename, design_var):
            PISM.verbPrintf(1, com, "Initial guess for design variable is not available as '%s' in %s.\nYou can provide an initial guess in the inverse data file.\n" % (design_var, input_filename))
            exit(1)
        PISM.logging.logMessage("Reading '%s_prior' from '%s' in input file.\n" % (design_var, design_var))
        design = createDesignVec(grid, design_var)
        design.regrid(input_filename, True)
        design_prior.copy_from(design)

    if using_zeta_fixed_mask:
        if PISM.util.fileHasVariable(inv_data_filename, "zeta_fixed_mask"):
            zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
            zeta_fixed_mask.regrid(inv_data_filename)
            vecs.add(zeta_fixed_mask)
        else:
            if design_var == 'tauc':
                logMessage("  Computing 'zeta_fixed_mask' (i.e. locations where design variable '%s' has a fixed value).\n" % design_var)
                zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
                zeta_fixed_mask.set(1)
                mask = vecs.ice_mask
                with PISM.vec.Access(comm=zeta_fixed_mask, nocomm=mask):
                    mq = PISM.MaskQuery(mask)
                    for (i, j) in grid.points():
                        if mq.grounded_ice(i, j):
                            zeta_fixed_mask[i, j] = 0
                vecs.add(zeta_fixed_mask)

                adjustTauc(vecs.ice_mask, design_prior)
            elif design_var == 'hardav':
                pass
            else:
                raise NotImplementedError("Unable to build 'zeta_fixed_mask' for design variable %s.", design_var)
Example #15
0
def printIteration(invssa_solver, it, data):
    "Print a header for an iteration report."
    logMessage("----------------------------------------------------------\n")
    logMessage("Iteration %d\n" % it)
Example #16
0
File: ssa.py Project: pism/pism
def printIteration(invssa_solver, it, data):
    "Print a header for an iteration report."
    logMessage("----------------------------------------------------------\n")
    logMessage("Iteration %d\n" % it)
Example #17
0
def run():
    context = PISM.Context()
    config = context.config
    com = context.com
    PISM.set_abort_on_sigint(True)

    WIDE_STENCIL = int(config.get_double("grid_max_stencil_width"))

    usage = \
        """  pismi.py [-i IN.nc [-o OUT.nc]]/[-a INOUT.nc] [-inv_data inv_data.nc] [-inv_forward model] 
                [-inv_design design_var] [-inv_method meth] 
    where:
    -i            IN.nc       is input file in NetCDF format: contains PISM-written model state
    -o            OUT.nc      is output file in NetCDF format to be overwritten
    -a            INOUT.nc    is input/output file in NetCDF format to be appended to
    -inv_data     inv_data.nc is data file containing extra inversion data (e.g. observed surface velocities)
    -inv_forward  model       forward model: only 'ssa' supported
    -inv_design   design_var  design variable name; one of 'tauc'/'hardav' for SSA inversions
    -inv_method   meth        algorithm for inversion [sd,nlcg,ign,tikhonov_lmvm]

    notes:
      * only one of -i/-a is allowed; both specify the input file
      * only one of -o/-a is allowed; both specify the output file
      * if -o is used, only the variables involved in inversion are written to the output file.
      * if -a is used, the varaibles involved in inversion are appended to the given file. No
        original variables in the file are changed.
   """

    append_mode = False
    PISM.setVerbosityLevel(1)

    input_filename = PISM.optionsString("-i", "input file")
    append_filename = PISM.optionsString("-a", "append file", default=None)
    output_filename = PISM.optionsString("-o", "output file", default=None)

    if (input_filename is None) and (append_filename is None):
        PISM.verbPrintf(1, com, "\nError: No input file specified. Use one of -i [file.nc] or -a [file.nc].\n")
        sys.exit(0)

    if (input_filename is not None) and (append_filename is not None):
        PISM.verbPrintf(1, com, "\nError: Only one of -i/-a is allowed.\n")
        sys.exit(0)

    if (output_filename is not None) and (append_filename is not None):
        PISM.verbPrintf(1, com, "\nError: Only one of -a/-o is allowed.\n")
        sys.edit(0)

    if append_filename is not None:
        input_filename = append_filename
        output_filename = append_filename
        append_mode = True

    inv_data_filename = PISM.optionsString("-inv_data", "inverse data file", default=input_filename)
    verbosity = PISM.optionsInt("-verbose", "verbosity level", default=2)

    do_plotting = PISM.optionsFlag("-inv_plot", "perform visualization during the computation", default=False)
    do_final_plot = PISM.optionsFlag("-inv_final_plot", "perform visualization at the end of the computation", default=False)
    Vmax = PISM.optionsReal("-inv_plot_vmax", "maximum velocity for plotting residuals", default=30)

    design_var = PISM.optionsList("-inv_ssa",
                                  "design variable for inversion",
                                  "tauc,hardav", "tauc")
    do_pause = PISM.optionsFlag("-inv_pause", "pause each iteration", default=False)

    do_restart = PISM.optionsFlag("-inv_restart", "Restart a stopped computation.", default=False)
    use_design_prior = PISM.optionsFlag("-inv_use_design_prior", "Use prior from inverse data file as initial guess.", default=True)

    prep_module = PISM.optionsString("-inv_prep_module", "Python module used to do final setup of inverse solver", default=None)

    is_regional = PISM.optionsFlag("-regional", "Compute SIA/SSA using regional model semantics", default=False)

    using_zeta_fixed_mask = PISM.optionsFlag("-inv_use_zeta_fixed_mask",
                                             "Enforce locations where the parameterized design variable should be fixed. (Automatically determined if not provided)", default=True)

    inv_method = config.get_string("inv_ssa_method")

    if output_filename is None:
        output_filename = "pismi_" + os.path.basename(input_filename)

    saving_inv_data = (inv_data_filename != output_filename)

    PISM.setVerbosityLevel(verbosity)
    forward_run = SSAForwardRun(input_filename, inv_data_filename, design_var)
    forward_run.setup()
    design_param = forward_run.designVariableParameterization()
    solver = PISM.invert.ssa.createInvSSASolver(forward_run)

    modeldata = forward_run.modeldata
    vecs = modeldata.vecs
    grid = modeldata.grid

    # Determine the prior guess for tauc/hardav. This can be one of
    # a) tauc/hardav from the input file (default)
    # b) tauc/hardav_prior from the inv_datafile if -inv_use_design_prior is set
    design_prior = createDesignVec(grid, design_var, '%s_prior' % design_var)
    long_name = design_prior.metadata().get_string("long_name")
    units = design_prior.metadata().get_string("units")
    design_prior.set_attrs("", "best prior estimate for %s (used for inversion)" % long_name, units, "")
    if PISM.util.fileHasVariable(inv_data_filename, "%s_prior" % design_var) and use_design_prior:
        PISM.logging.logMessage("  Reading '%s_prior' from inverse data file %s.\n" % (design_var, inv_data_filename))
        design_prior.regrid(inv_data_filename, critical=True)
        vecs.add(design_prior, writing=saving_inv_data)
    else:
        if not PISM.util.fileHasVariable(input_filename, design_var):
            PISM.verbPrintf(1, com, "Initial guess for design variable is not available as '%s' in %s.\nYou can provide an initial guess in the inverse data file.\n" % (design_var, input_filename))
            exit(1)
        PISM.logging.logMessage("Reading '%s_prior' from '%s' in input file.\n" % (design_var, design_var))
        design = createDesignVec(grid, design_var)
        design.regrid(input_filename, True)
        design_prior.copy_from(design)
        vecs.add(design_prior, writing=True)

    if using_zeta_fixed_mask:
        if PISM.util.fileHasVariable(inv_data_filename, "zeta_fixed_mask"):
            zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
            zeta_fixed_mask.regrid(inv_data_filename)
            vecs.add(zeta_fixed_mask)
        else:
            if design_var == 'tauc':
                logMessage("  Computing 'zeta_fixed_mask' (i.e. locations where design variable '%s' has a fixed value).\n" % design_var)
                zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
                zeta_fixed_mask.set(1)
                mask = vecs.mask
                with PISM.vec.Access(comm=zeta_fixed_mask, nocomm=mask):
                    mq = PISM.MaskQuery(mask)
                    for (i, j) in grid.points():
                        if mq.grounded_ice(i, j):
                            zeta_fixed_mask[i, j] = 0
                vecs.add(zeta_fixed_mask)

                adjustTauc(vecs.mask, design_prior)
            elif design_var == 'hardav':
                PISM.logging.logPrattle("Skipping 'zeta_fixed_mask' for design variable 'hardav'; no natural locations to fix its value.")
                pass
            else:
                raise NotImplementedError("Unable to build 'zeta_fixed_mask' for design variable %s.", design_var)

    # Convert design_prior -> zeta_prior
    zeta_prior = PISM.IceModelVec2S()
    zeta_prior.create(grid, "zeta_prior", PISM.WITH_GHOSTS, WIDE_STENCIL)
    design_param.convertFromDesignVariable(design_prior, zeta_prior)
    vecs.add(zeta_prior, writing=True)

    # Determine the initial guess for zeta.  If we are restarting, load it from
    # the output file.  Otherwise, if 'zeta_inv' is in the inverse data file, use it.
    # If none of the above, copy from 'zeta_prior'.
    zeta = PISM.IceModelVec2S()
    zeta.create(grid, "zeta_inv", PISM.WITH_GHOSTS, WIDE_STENCIL)
    zeta.set_attrs("diagnostic", "zeta_inv", "1", "zeta_inv")
    if do_restart:
        # Just to be sure, verify that we have a 'zeta_inv' in the output file.
        if not PISM.util.fileHasVariable(output_filename, 'zeta_inv'):
            PISM.verbPrintf(1, com, "Unable to restart computation: file %s is missing variable 'zeta_inv'", output_filename)
            exit(1)
        PISM.logging.logMessage("  Inversion starting from 'zeta_inv' found in %s\n" % output_filename)
        zeta.regrid(output_filename, True)

    elif PISM.util.fileHasVariable(inv_data_filename, 'zeta_inv'):
        PISM.logging.logMessage("  Inversion starting from 'zeta_inv' found in %s\n" % inv_data_filename)
        zeta.regrid(inv_data_filename, True)
    else:
        zeta.copy_from(zeta_prior)

    vel_ssa_observed = None
    vel_ssa_observed = PISM.model.create2dVelocityVec(grid, '_ssa_observed', stencil_width=2)
    if PISM.util.fileHasVariable(inv_data_filename, "u_ssa_observed"):
        vel_ssa_observed.regrid(inv_data_filename, True)
        vecs.add(vel_ssa_observed, writing=saving_inv_data)
    else:
        if not PISM.util.fileHasVariable(inv_data_filename, "u_surface_observed"):
            PISM.verbPrintf(1, context.com, "Neither u/v_ssa_observed nor u/v_surface_observed is available in %s.\nAt least one must be specified.\n" % inv_data_filename)
            exit(1)
        vel_surface_observed = PISM.model.create2dVelocityVec(grid, '_surface_observed', stencil_width=2)
        vel_surface_observed.regrid(inv_data_filename, True)
        vecs.add(vel_surface_observed, writing=saving_inv_data)

        sia_solver = PISM.SIAFD
        if is_regional:
            sia_solver = PISM.SIAFD_Regional
        vel_sia_observed = PISM.sia.computeSIASurfaceVelocities(modeldata, sia_solver)

        vel_sia_observed.metadata(0).set_name('u_sia_observed')
        vel_sia_observed.metadata(0).set_string('long_name', "x-component of the 'observed' SIA velocities")

        vel_sia_observed.metadata(1).set_name('v_sia_observed')
        vel_sia_observed.metadata(1).set_string('long_name', "y-component of the 'observed' SIA velocities")

        vel_ssa_observed.copy_from(vel_surface_observed)
        vel_ssa_observed.add(-1, vel_sia_observed)
        vecs.add(vel_ssa_observed, writing=True)

    # If the inverse data file has a variable tauc/hardav_true, this is probably
    # a synthetic inversion.  We'll load it now so that it will get written
    # out, if needed, at the end of the computation in the output file.
    if PISM.util.fileHasVariable(inv_data_filename, "%s_true" % design_var):
        design_true = createDesignVec(grid, design_var, '%s_true' % design_var)
        design_true.regrid(inv_data_filename, True)
        design_true.read_attributes(inv_data_filename)
        vecs.add(design_true, writing=saving_inv_data)

    # Establish a logger which will save logging messages to the output file.
    message_logger = PISM.logging.CaptureLogger(output_filename, 'pismi_log')
    PISM.logging.add_logger(message_logger)
    if append_mode or do_restart:
        message_logger.readOldLog()

    # Prep the output file from the grid so that we can save zeta to it during the runs.
    if not append_mode:
        pio = PISM.PIO(grid.com, "netcdf3")
        pio.open(output_filename, PISM.PISM_READWRITE_MOVE)
        PISM.define_time(pio,
                         grid.ctx().config().get_string("time_dimension_name"),
                         grid.ctx().config().get_string("calendar"),
                         grid.ctx().time().units_string(),
                         grid.ctx().unit_system())
        PISM.append_time(pio,
                         grid.ctx().config().get_string("time_dimension_name"),
                         grid.ctx().time().current())
        pio.close()
    zeta.write(output_filename)

    # Log the command line to the output file now so that we have a record of
    # what was attempted
    PISM.util.writeProvenance(output_filename)

    # Attach various iteration listeners to the solver as needed for:

    # Iteration report.
    solver.addIterationListener(PISM.invert.ssa.printIteration)

    # Misfit reporting/logging.
    misfit_logger = PISM.invert.ssa.MisfitLogger()
    solver.addIterationListener(misfit_logger)

    if inv_method.startswith('tikhonov'):
        solver.addIterationListener(PISM.invert.ssa.printTikhonovProgress)

    # Saving the current iteration
    solver.addDesignUpdateListener(PISM.invert.ssa.ZetaSaver(output_filename))

    # Plotting
    if do_plotting:
        solver.addIterationListener(InvSSAPlotListener(grid, Vmax))
        if solver.method == 'ign':
            solver.addLinearIterationListener(InvSSALinPlotListener(grid, Vmax))

    # Solver is set up.  Give the user's prep module a chance to do any final
    # setup.

    if prep_module is not None:
        if prep_module.endswith(".py"):
            prep_module = prep_module[0:-2]
        exec "import %s as user_prep_module" % prep_module
        user_prep_module.prep_solver(solver)

    # Pausing (add this after the user's listeners)
    if do_pause:
        solver.addIterationListener(PISM.invert.listener.pauseListener)

    # Run the inverse solver!
    if do_restart:
        PISM.logging.logMessage('************** Restarting inversion. ****************\n')
    else:
        PISM.logging.logMessage('============== Starting inversion. ==================\n')

    # Try solving
    reason = solver.solveInverse(zeta_prior, vel_ssa_observed, zeta)
    if reason.failed():
        PISM.logging.logError("Inverse solve FAILURE:\n%s\n" % reason.nested_description(1))
        quit()

    PISM.logging.logMessage("Inverse solve success (%s)!\n" % reason.description())

    (zeta, u) = solver.inverseSolution()

    # It may be that a 'tauc'/'hardav' was read in earlier.  We replace it with
    # our newly generated one.
    if vecs.has(design_var):
        design = vecs.get(design_var)
        design_param.convertToDesignVariable(zeta, design)
    else:
        # Convert back from zeta to tauc or hardav
        design = createDesignVec(grid, design_var)
        design_param.convertToDesignVariable(zeta, design)
        vecs.add(design, writing=True)

    vecs.add(zeta, writing=True)

    u.metadata(0).set_name("u_ssa_inv")
    u.metadata(0).set_string("long_name", "x-component of SSA velocity computed by inversion")

    u.metadata(1).set_name("v_ssa_inv")
    u.metadata(1).set_string("long_name", "y-component of SSA velocity computed by inversion")

    vecs.add(u, writing=True)

    residual = PISM.model.create2dVelocityVec(grid, name='_inv_ssa_residual')
    residual.copy_from(u)
    residual.add(-1, vel_ssa_observed)

    r_mag = PISM.IceModelVec2S()
    r_mag.create(grid, "inv_ssa_residual", PISM.WITHOUT_GHOSTS, 0)

    sys = grid.ctx().unit_system()
    r_mag.set_attrs("diagnostic", "magnitude of mismatch between observed surface velocities and their reconstrution by inversion",
                    "m s-1", "inv_ssa_residual", 0)
    r_mag.metadata().set_double("_FillValue", PISM.convert(sys, -0.01, 'm/year', 'm/s'))
    r_mag.metadata().set_double("valid_min", 0.0)
    r_mag.metadata().set_string("glaciological_units", "m year-1")
    r_mag.write_in_glaciological_units = True

    r_mag.set_to_magnitude(residual)
    r_mag.mask_by(vecs.land_ice_thickness)

    vecs.add(residual, writing=True)
    vecs.add(r_mag, writing=True)

    # Write solution out to netcdf file
    forward_run.write(output_filename, append=append_mode)
    # If we're not in append mode, the previous command just nuked
    # the output file.  So we rewrite the siple log.
    if not append_mode:
        message_logger.write(output_filename)

    # Save the misfit history
    misfit_logger.write(output_filename)
Example #18
0
            PISM.verbPrintf(1, com, "Initial guess for design variable is not available as '%s' in %s.\nYou can provide an initial guess in the inverse data file.\n" % (
                design_var, input_filename))
            exit(1)
        PISM.logging.logMessage("Reading '%s_prior' from '%s' in input file.\n" % (design_var, design_var))
        design = createDesignVec(grid, design_var)
        design.regrid(input_filename, True)
        design_prior.copy_from(design)

    if using_zeta_fixed_mask:
        if PISM.util.fileHasVariable(inv_data_filename, "zeta_fixed_mask"):
            zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
            zeta_fixed_mask.regrid(inv_data_filename)
            vecs.add(zeta_fixed_mask)
        else:
            if design_var == 'tauc':
                logMessage(
                    "  Computing 'zeta_fixed_mask' (i.e. locations where design variable '%s' has a fixed value).\n" % design_var)
                zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
                zeta_fixed_mask.set(1)
                mask = vecs.ice_mask
                with PISM.vec.Access(comm=zeta_fixed_mask, nocomm=mask):
                    for (i, j) in grid.points():
                        if mask.grounded_ice(i, j):
                            zeta_fixed_mask[i, j] = 0
                vecs.add(zeta_fixed_mask)

                adjustTauc(vecs.ice_mask, design_prior)
            elif design_var == 'hardav':
                pass
            else:
                raise NotImplementedError("Unable to build 'zeta_fixed_mask' for design variable %s.", design_var)
Example #19
0
def run():
    context = PISM.Context()
    config = context.config
    com = context.com
    PISM.set_abort_on_sigint(True)

    WIDE_STENCIL = int(config.get_number("grid.max_stencil_width"))

    usage = \
        """  pismi.py [-i IN.nc [-o OUT.nc]]/[-a INOUT.nc] [-inv_data inv_data.nc] [-inv_forward model]
                [-inv_design design_var] [-inv_method meth]
    where:
    -i            IN.nc       is input file in NetCDF format: contains PISM-written model state
    -o            OUT.nc      is output file in NetCDF format to be overwritten
    -a            INOUT.nc    is input/output file in NetCDF format to be appended to
    -inv_data     inv_data.nc is data file containing extra inversion data (e.g. observed surface velocities)
    -inv_forward  model       forward model: only 'ssa' supported
    -inv_design   design_var  design variable name; one of 'tauc'/'hardav' for SSA inversions
    -inv_method   meth        algorithm for inversion [sd,nlcg,ign,tikhonov_lmvm]

    notes:
      * only one of -i/-a is allowed; both specify the input file
      * only one of -o/-a is allowed; both specify the output file
      * if -o is used, only the variables involved in inversion are written to the output file.
      * if -a is used, the varaibles involved in inversion are appended to the given file. No
        original variables in the file are changed.
   """

    append_mode = False

    input_filename = config.get_string("input.file")
    if len(input_filename) == 0:
        input_filename = None

    append = PISM.OptionString("-a", "append file")
    append_filename = append.value() if append.is_set() else None

    output_filename = config.get_string("output.file_name")
    if len(output_filename) == 0:
        output_filename = None

    if (input_filename is None) and (append_filename is None):
        PISM.verbPrintf(
            1, com,
            "\nError: No input file specified. Use one of -i [file.nc] or -a [file.nc].\n"
        )
        sys.exit(0)

    if (input_filename is not None) and (append_filename is not None):
        PISM.verbPrintf(1, com, "\nError: Only one of -i/-a is allowed.\n")
        sys.exit(0)

    if (output_filename is not None) and (append_filename is not None):
        PISM.verbPrintf(1, com, "\nError: Only one of -a/-o is allowed.\n")
        sys.edit(0)

    if append_filename is not None:
        input_filename = append_filename
        output_filename = append_filename
        append_mode = True

    inv_data_filename = PISM.OptionString("-inv_data", "inverse data file",
                                          input_filename).value()

    do_plotting = PISM.OptionBool(
        "-inv_plot", "perform visualization during the computation")
    do_final_plot = PISM.OptionBool(
        "-inv_final_plot",
        "perform visualization at the end of the computation")
    Vmax = PISM.OptionReal(context.unit_system, "-inv_plot_vmax",
                           "maximum velocity for plotting residuals",
                           "m / year", 30)

    design_var = PISM.OptionKeyword("-inv_ssa",
                                    "design variable for inversion",
                                    "tauc,hardav", "tauc").value()
    do_pause = PISM.OptionBool("-inv_pause", "pause each iteration")

    do_restart = PISM.OptionBool("-inv_restart",
                                 "Restart a stopped computation.")
    use_design_prior = config.get_flag("inverse.use_design_prior")

    prep_module = PISM.OptionString(
        "-inv_prep_module",
        "Python module used to do final setup of inverse solver")
    prep_module = prep_module.value() if prep_module.is_set() else None

    is_regional = PISM.OptionBool(
        "-regional", "Compute SIA/SSA using regional model semantics")

    using_zeta_fixed_mask = config.get_flag("inverse.use_zeta_fixed_mask")

    inv_method = config.get_string("inverse.ssa.method")

    if output_filename is None:
        output_filename = "pismi_" + os.path.basename(input_filename)

    saving_inv_data = (inv_data_filename != output_filename)

    forward_run = SSAForwardRun(input_filename, inv_data_filename, design_var)
    forward_run.setup()
    design_param = forward_run.designVariableParameterization()
    solver = PISM.invert.ssa.createInvSSASolver(forward_run)

    modeldata = forward_run.modeldata
    vecs = modeldata.vecs
    grid = modeldata.grid

    # Determine the prior guess for tauc/hardav. This can be one of
    # a) tauc/hardav from the input file (default)
    # b) tauc/hardav_prior from the inv_datafile if -inv_use_design_prior is set
    design_prior = createDesignVec(grid, design_var, '%s_prior' % design_var)
    long_name = design_prior.metadata().get_string("long_name")
    units = design_prior.metadata().get_string("units")
    design_prior.set_attrs(
        "", "best prior estimate for %s (used for inversion)" % long_name,
        units, units, "", 0)
    if PISM.util.fileHasVariable(inv_data_filename,
                                 "%s_prior" % design_var) and use_design_prior:
        PISM.logging.logMessage(
            "  Reading '%s_prior' from inverse data file %s.\n" %
            (design_var, inv_data_filename))
        design_prior.regrid(inv_data_filename, critical=True)
        vecs.add(design_prior, writing=saving_inv_data)
    else:
        if not PISM.util.fileHasVariable(input_filename, design_var):
            PISM.verbPrintf(
                1, com,
                "Initial guess for design variable is not available as '%s' in %s.\nYou can provide an initial guess in the inverse data file.\n"
                % (design_var, input_filename))
            exit(1)
        PISM.logging.logMessage(
            "Reading '%s_prior' from '%s' in input file.\n" %
            (design_var, design_var))
        design = createDesignVec(grid, design_var)
        design.regrid(input_filename, True)
        design_prior.copy_from(design)
        vecs.add(design_prior, writing=True)

    if using_zeta_fixed_mask:
        if PISM.util.fileHasVariable(inv_data_filename, "zeta_fixed_mask"):
            zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
            zeta_fixed_mask.regrid(inv_data_filename)
            vecs.add(zeta_fixed_mask)
        else:
            if design_var == 'tauc':
                logMessage(
                    "  Computing 'zeta_fixed_mask' (i.e. locations where design variable '%s' has a fixed value).\n"
                    % design_var)
                zeta_fixed_mask = PISM.model.createZetaFixedMaskVec(grid)
                zeta_fixed_mask.set(1)
                mask = vecs.mask
                with PISM.vec.Access(comm=zeta_fixed_mask, nocomm=mask):
                    for (i, j) in grid.points():
                        if mask.grounded_ice(i, j):
                            zeta_fixed_mask[i, j] = 0
                vecs.add(zeta_fixed_mask)

                adjustTauc(vecs.mask, design_prior)
            elif design_var == 'hardav':
                PISM.logging.logPrattle(
                    "Skipping 'zeta_fixed_mask' for design variable 'hardav'; no natural locations to fix its value."
                )
                pass
            else:
                raise NotImplementedError(
                    "Unable to build 'zeta_fixed_mask' for design variable %s.",
                    design_var)

    # Convert design_prior -> zeta_prior
    zeta_prior = PISM.IceModelVec2S(grid, "zeta_prior", PISM.WITH_GHOSTS,
                                    WIDE_STENCIL)
    design_param.convertFromDesignVariable(design_prior, zeta_prior)
    vecs.add(zeta_prior, writing=True)

    # Determine the initial guess for zeta.  If we are restarting, load it from
    # the output file.  Otherwise, if 'zeta_inv' is in the inverse data file, use it.
    # If none of the above, copy from 'zeta_prior'.
    zeta = PISM.IceModelVec2S(grid, "zeta_inv", PISM.WITH_GHOSTS, WIDE_STENCIL)
    zeta.set_attrs("diagnostic", "zeta_inv", "1", "1", "zeta_inv", 0)
    if do_restart:
        # Just to be sure, verify that we have a 'zeta_inv' in the output file.
        if not PISM.util.fileHasVariable(output_filename, 'zeta_inv'):
            PISM.verbPrintf(
                1, com,
                "Unable to restart computation: file %s is missing variable 'zeta_inv'",
                output_filename)
            exit(1)
        PISM.logging.logMessage(
            "  Inversion starting from 'zeta_inv' found in %s\n" %
            output_filename)
        zeta.regrid(output_filename, True)

    elif PISM.util.fileHasVariable(inv_data_filename, 'zeta_inv'):
        PISM.logging.logMessage(
            "  Inversion starting from 'zeta_inv' found in %s\n" %
            inv_data_filename)
        zeta.regrid(inv_data_filename, True)
    else:
        zeta.copy_from(zeta_prior)

    vel_ssa_observed = None
    vel_ssa_observed = PISM.model.create2dVelocityVec(grid,
                                                      '_ssa_observed',
                                                      stencil_width=2)
    if PISM.util.fileHasVariable(inv_data_filename, "u_ssa_observed"):
        vel_ssa_observed.regrid(inv_data_filename, True)
        vecs.add(vel_ssa_observed, writing=saving_inv_data)
    else:
        if not PISM.util.fileHasVariable(inv_data_filename,
                                         "u_surface_observed"):
            PISM.verbPrintf(
                1, context.com,
                "Neither u/v_ssa_observed nor u/v_surface_observed is available in %s.\nAt least one must be specified.\n"
                % inv_data_filename)
            exit(1)
        vel_surface_observed = PISM.model.create2dVelocityVec(
            grid, '_surface_observed', stencil_width=2)
        vel_surface_observed.regrid(inv_data_filename, True)
        vecs.add(vel_surface_observed, writing=saving_inv_data)

        sia_solver = PISM.SIAFD
        if is_regional:
            sia_solver = PISM.SIAFD_Regional
        vel_sia_observed = PISM.sia.computeSIASurfaceVelocities(
            modeldata, sia_solver)

        vel_sia_observed.metadata(0).set_name('u_sia_observed')
        vel_sia_observed.metadata(0).set_string(
            'long_name', "x-component of the 'observed' SIA velocities")

        vel_sia_observed.metadata(1).set_name('v_sia_observed')
        vel_sia_observed.metadata(1).set_string(
            'long_name', "y-component of the 'observed' SIA velocities")

        vel_ssa_observed.copy_from(vel_surface_observed)
        vel_ssa_observed.add(-1, vel_sia_observed)
        vecs.add(vel_ssa_observed, writing=True)

    # If the inverse data file has a variable tauc/hardav_true, this is probably
    # a synthetic inversion.  We'll load it now so that it will get written
    # out, if needed, at the end of the computation in the output file.
    if PISM.util.fileHasVariable(inv_data_filename, "%s_true" % design_var):
        design_true = createDesignVec(grid, design_var, '%s_true' % design_var)
        design_true.regrid(inv_data_filename, True)
        try:
            f = PISM.File(com, inv_data_filename, PISM.PISM_NETCDF3,
                          PISM.PISM_READONLY)
            PISM.read_attributes(f, design_true.get_name(),
                                 design_true.metadata())
        finally:
            f.close()
        vecs.add(design_true, writing=saving_inv_data)

    # Establish a logger which will save logging messages to the output file.
    message_logger = PISM.logging.CaptureLogger(output_filename, 'pismi_log')
    PISM.logging.add_logger(message_logger)
    if append_mode or do_restart:
        message_logger.readOldLog()

    # Prep the output file from the grid so that we can save zeta to it during the runs.
    if not append_mode:
        pio = PISM.util.prepare_output(output_filename)
        pio.close()
    zeta.write(output_filename)

    # Log the command line to the output file now so that we have a record of
    # what was attempted
    PISM.util.writeProvenance(output_filename)

    # Attach various iteration listeners to the solver as needed for:

    # Iteration report.
    solver.addIterationListener(PISM.invert.ssa.printIteration)

    # Misfit reporting/logging.
    misfit_logger = PISM.invert.ssa.MisfitLogger()
    solver.addIterationListener(misfit_logger)

    if inv_method.startswith('tikhonov'):
        solver.addIterationListener(PISM.invert.ssa.printTikhonovProgress)

    # Saving the current iteration
    solver.addDesignUpdateListener(PISM.invert.ssa.ZetaSaver(output_filename))

    # Plotting
    if do_plotting:
        solver.addIterationListener(InvSSAPlotListener(grid, Vmax))
        if solver.method == 'ign':
            solver.addLinearIterationListener(InvSSALinPlotListener(
                grid, Vmax))

    # Solver is set up.  Give the user's prep module a chance to do any final
    # setup.

    if prep_module is not None:
        if prep_module.endswith(".py"):
            prep_module = prep_module[0:-2]
        exec("import %s as user_prep_module" % prep_module)
        user_prep_module.prep_solver(solver)

    # Pausing (add this after the user's listeners)
    if do_pause:
        solver.addIterationListener(PISM.invert.listener.pauseListener)

    # Run the inverse solver!
    if do_restart:
        PISM.logging.logMessage(
            '************** Restarting inversion. ****************\n')
    else:
        PISM.logging.logMessage(
            '============== Starting inversion. ==================\n')

    # Try solving
    reason = solver.solveInverse(zeta_prior, vel_ssa_observed, zeta)
    if reason.failed():
        PISM.logging.logError("Inverse solve FAILURE:\n%s\n" %
                              reason.nested_description(1))
        quit()

    PISM.logging.logMessage("Inverse solve success (%s)!\n" %
                            reason.description())

    (zeta, u) = solver.inverseSolution()

    # It may be that a 'tauc'/'hardav' was read in earlier.  We replace it with
    # our newly generated one.
    if vecs.has(design_var):
        design = vecs.get(design_var)
        design_param.convertToDesignVariable(zeta, design)
    else:
        # Convert back from zeta to tauc or hardav
        design = createDesignVec(grid, design_var)
        design_param.convertToDesignVariable(zeta, design)
        vecs.add(design, writing=True)

    vecs.add(zeta, writing=True)

    u.metadata(0).set_name("u_ssa_inv")
    u.metadata(0).set_string(
        "long_name", "x-component of SSA velocity computed by inversion")

    u.metadata(1).set_name("v_ssa_inv")
    u.metadata(1).set_string(
        "long_name", "y-component of SSA velocity computed by inversion")

    vecs.add(u, writing=True)

    residual = PISM.model.create2dVelocityVec(grid, name='_inv_ssa_residual')
    residual.copy_from(u)
    residual.add(-1, vel_ssa_observed)

    r_mag = PISM.IceModelVec2S(grid, "inv_ssa_residual", PISM.WITHOUT_GHOSTS,
                               0)

    r_mag.set_attrs(
        "diagnostic",
        "magnitude of mismatch between observed surface velocities and their reconstrution by inversion",
        "m s-1", "m year-1", "inv_ssa_residual", 0)
    r_mag.metadata().set_number("_FillValue", convert(-0.01, 'm/year', 'm/s'))
    r_mag.metadata().set_number("valid_min", 0.0)

    PISM.compute_magnitude(residual, r_mag)
    PISM.apply_mask(vecs.land_ice_thickness, 0.0, r_mag)

    vecs.add(residual, writing=True)
    vecs.add(r_mag, writing=True)

    # Write solution out to netcdf file (always append because the file was created already)
    forward_run.write(output_filename, append=True)
    # If we're not in append mode, the previous command just nuked
    # the output file.  So we rewrite the siple log.
    if not append_mode:
        message_logger.write(output_filename)

    # Save the misfit history
    misfit_logger.write(output_filename)