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)))
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)
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
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)
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)))
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
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
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)))
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)
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
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))
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)
def printIteration(invssa_solver, it, data): "Print a header for an iteration report." logMessage("----------------------------------------------------------\n") logMessage("Iteration %d\n" % it)
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)
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)
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)