def run_momsolve(config_file): # Read run config file params = ConfigParser(config_file) log = inout.setup_logging(params) input_data = inout.InputData(params) # Get model mesh mesh = fice_mesh.get_mesh(params) # Initialize model mdl = model.model(mesh, input_data, params, init_vel_obs=False) # Get alpha from file mdl.alpha_from_data() try: Bglen = mdl.input_data.interpolate("Bglen", mdl.M) mdl.init_beta(mdl.bglen_to_beta(Bglen), False) except (AttributeError, KeyError) as e: log.warning('Using default bglen (constant)') # Forward Solve slvr = solver.ssa_solver(mdl) slvr.def_mom_eq() slvr.solve_mom_eq() # Output model variables in ParaView+Fenics friendly format outdir = params.io.output_dir h5file = HDF5File(mesh.mpi_comm(), str(Path(outdir)/'U.h5'), 'w') h5file.write(slvr.U, 'U') h5file.write(mesh, 'mesh') h5file.attributes('mesh')['periodic'] = params.mesh.periodic_bc inout.write_variable(slvr.U, params)
def run_inv(config_file): """Run the inversion part of the simulation""" # Read run config file params = ConfigParser(config_file) inout.setup_logging(params) inout.log_preamble("inverse", params) # Load the static model data (geometry, smb, etc) input_data = inout.InputData(params) # Get the model mesh mesh = fice_mesh.get_mesh(params) mdl = model.model(mesh, input_data, params) # pts_lengthscale = params.obs.pts_len mdl.gen_alpha() # Add random noise to Beta field iff we're inverting for it mdl.bglen_from_data() mdl.init_beta(mdl.bglen_to_beta(mdl.bglen), pert=False) # Next line will output the initial guess for alpha fed into the inversion # File(os.path.join(outdir,'alpha_initguess.pvd')) << mdl.alpha ##################### # Run the Inversion # ##################### slvr = solver.ssa_solver(mdl) slvr.inversion() ############################################## # Write out variables in outdir and # # diagnostics folder # ############################################# phase_name = params.inversion.phase_name phase_suffix = params.inversion.phase_suffix outdir = Path(params.io.output_dir) / phase_name / phase_suffix diag_dir = Path(params.io.diagnostics_dir) # Required for next phase (HDF5): invout_file = params.io.inversion_file phase_suffix = params.inversion.phase_suffix if len(phase_suffix) > 0: invout_file = params.io.run_name + phase_suffix + '_invout.h5' invout = HDF5File(mesh.mpi_comm(), str(outdir / invout_file), 'w') invout.parameters.add("gamma_alpha", slvr.gamma_alpha) invout.parameters.add("delta_alpha", slvr.delta_alpha) invout.parameters.add("gamma_beta", slvr.gamma_beta) invout.parameters.add("delta_beta", slvr.delta_beta) invout.parameters.add("delta_beta_gnd", slvr.delta_beta_gnd) invout.parameters.add("timestamp", str(datetime.datetime.now())) invout.write(mdl.alpha, 'alpha') invout.write(mdl.beta, 'beta') # For visualisation (XML & VTK): if params.io.write_diagnostics: inout.write_variable(slvr.U, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.beta, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) mdl.beta_bgd.rename("beta_bgd", "") inout.write_variable(mdl.beta_bgd, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.bed, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) H = project(mdl.H, mdl.M) H.rename("thick", "") inout.write_variable(H, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) fl_ex = project(slvr.float_conditional(H), mdl.M) inout.write_variable(fl_ex, params, name='float', outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.mask_vel_M, params, name="mask_vel", outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.u_obs_Q, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.v_obs_Q, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.u_std_Q, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.v_std_Q, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) U_obs = project((mdl.v_obs_Q**2 + mdl.u_obs_Q**2)**(1.0 / 2.0), mdl.M) U_obs.rename("uv_obs", "") inout.write_variable(U_obs, params, name="uv_obs", outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.alpha, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) Bglen = project(slvr.beta_to_bglen(slvr.beta), mdl.M) Bglen.rename("Bglen", "") inout.write_variable(Bglen, params, outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(slvr.bmelt, params, name="bmelt", outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(slvr.smb, params, name="smb", outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) inout.write_variable(mdl.surf, params, name="surf", outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix) return mdl
def run_invsigma(config_file): """Compute control sigma values from eigendecomposition""" comm = MPI.comm_world rank = comm.rank # Read run config file params = ConfigParser(config_file) # Setup logging log = inout.setup_logging(params) inout.log_preamble("inv sigma", params) outdir = params.io.output_dir diags_dir = params.io.diagnostics_dir # Load the static model data (geometry, smb, etc) input_data = inout.InputData(params) # Eigen decomposition params phase_suffix_e = params.eigendec.phase_suffix eigendir = Path(outdir)/params.eigendec.phase_name/phase_suffix_e lamfile = params.io.eigenvalue_file vecfile = params.io.eigenvecs_file threshlam = params.eigendec.eigenvalue_thresh if len(phase_suffix_e) > 0: lamfile = params.io.run_name + phase_suffix_e + '_eigvals.p' vecfile = params.io.run_name + phase_suffix_e + '_vr.h5' # Get model mesh mesh = fice_mesh.get_mesh(params) # Define the model (only need alpha & beta though) mdl = model.model(mesh, input_data, params, init_fields=True) # Load alpha/beta fields mdl.alpha_from_inversion() mdl.beta_from_inversion() mdl.bglen_from_data(mask_only=True) # Setup our solver object slvr = solver.ssa_solver(mdl, mixed_space=params.inversion.dual) cntrl = slvr.get_control()[0] space = slvr.get_control_space() # sigma_old, sigma_prior_old = [Function(space) for i in range(3)] x, y, z = [Function(space) for i in range(3)] # Regularization operator using inversion delta/gamma values Prior = mdl.get_prior() reg_op = Prior(slvr, space) # Load the eigenvalues with open(os.path.join(eigendir, lamfile), 'rb') as ff: eigendata = pickle.load(ff) lam = eigendata[0].real.astype(np.float64) nlam = len(lam) # Check if eigendecomposition successfully produced num_eig # or if some are NaN if np.any(np.isnan(lam)): nlam = np.argwhere(np.isnan(lam))[0][0] lam = lam[:nlam] # Read in the eigenvectors and check they are normalised # w.r.t. the prior (i.e. the B matrix in our GHEP) eps = params.constants.float_eps W = [] with HDF5File(comm, os.path.join(eigendir, vecfile), 'r') as hdf5data: for i in range(nlam): w = Function(space) hdf5data.read(w, f'v/vector_{i}') print(f"Getting eigenvector {i} of {nlam}") # # Test norm in prior == 1.0 # reg_op.action(w.vector(), y.vector()) # norm_in_prior = w.vector().inner(y.vector()) # assert (abs(norm_in_prior - 1.0) < eps) W.append(w) # Which eigenvalues are larger than our threshold? pind = np.flatnonzero(lam > threshlam) lam = lam[pind] W = [W[i] for i in pind] # this is a diagonal matrix but we only ever address it element-wise # bit of a waste of space. D = np.diag(lam / (lam + 1)) # TODO make this a model method cntrl_names = [] if params.inversion.alpha_active: cntrl_names.append("alpha") if params.inversion.beta_active: cntrl_names.append("beta") dual = params.inversion.dual ############################################ # Isaac Eq. 20 # P2 = prior # P1 = WDW # Note - don't think we're considering the cross terms # in the posterior covariance. # Generate patches of cells for computing invsigma clust_fun, npatches = patch_fun(mesh, params) # Create standard & mixed DG spaces dg_space = FunctionSpace(mesh, 'DG', 0) if(dual): dg_el = FiniteElement("DG", mesh.ufl_cell(), 0) mixedEl = dg_el * dg_el dg_out_space = FunctionSpace(mesh, mixedEl) else: dg_out_space = dg_space sigmas = [Function(dg_space) for i in range(len(cntrl_names))] sigma_priors = [Function(dg_space) for i in range(len(cntrl_names))] indic_1 = Function(dg_space) indic = Function(dg_out_space) test = TestFunction(space) neg_flag = 0 for i in range(npatches): print(f"Working on patch {i+1} of {npatches}") # Create DG indicator function for patch i indic_1.vector()[:] = (clust_fun.vector()[:] == i).astype(int) indic_1.vector().apply("insert") # Loop alpha & beta as appropriate for j in range(len(cntrl_names)): if(dual): indic.vector()[:] = 0.0 indic.vector().apply("insert") assign(indic.sub(j), indic_1) else: assign(indic, indic_1) clust_lump = assemble(inner(indic, test)*dx) patch_area = clust_lump.sum() # Duplicate work here... clust_lump /= patch_area # Prior variance reg_op.inv_action(clust_lump, x.vector()) cov_prior = x.vector().inner(clust_lump) # P_i^T W D W^T P_i # P_i is clust_lump # P_i^T has dims [1 x M], W has dims [M x N] # where N is num eigs & M is size of ev function space PiW = np.asarray([clust_lump.inner(w.vector()) for w in W]) # PiW & PiWD are [1 x N] PiWD = PiW * D.diagonal() # PiWDWPi, [1 x N] * [N x 1] PiWDWPi = np.inner(PiWD, PiW) # np.inner OK here because already parallel reduced cov_reduction = PiWDWPi cov_post = cov_prior - cov_reduction if cov_post < 0: log.warning(f'WARNING: Negative Sigma: {cov_post}') log.warning('Setting as Zero and Continuing.') neg_flag = 1 continue # NB: "+=" here but each DOF will only be contributed to *once* # Essentially we are constructing the sigmas functions from # non-overlapping patches. sigmas[j].vector()[:] += indic_1.vector()[:] * np.sqrt(cov_post) sigmas[j].vector().apply("insert") sigma_priors[j].vector()[:] += indic_1.vector()[:] * np.sqrt(cov_prior) sigma_priors[j].vector().apply("insert") if neg_flag: log.warning('Negative value(s) of sigma encountered.' 'Examine the range of eigenvalues and check if ' 'the threshlam paramater is set appropriately.') # # Previous approach for comparison # ##################################### # # Isaac Eq. 20 # # P2 = prior # # P1 = WDW # # Note - don't think we're considering the cross terms # # in the posterior covariance. # # TODO - this isn't particularly well parallelised - can it be improved? # neg_flag = 0 # for j in range(space.dim()): # # Who owns this DOF? # own_idx = y.vector().owns_index(j) # ownership = np.where(comm.allgather(own_idx))[0] # assert len(ownership) == 1 # idx_root = ownership[0] # # Prior (P2) # y.vector().zero() # y.vector().vec().setValue(j, 1.0) # y.vector().apply('insert') # reg_op.inv_action(y.vector(), x.vector()) # P2 = x # # WDW (P1) ~ lam * V_r**2 # tmp2 = np.asarray([D[i, i] * w.vector().vec().getValue(j) for i, w in enumerate(W)]) # tmp2 = comm.bcast(tmp2, root=idx_root) # P1 = Function(space) # for tmp, w in zip(tmp2, W): # P1.vector().axpy(tmp, w.vector()) # P_vec = P2.vector() - P1.vector() # # Extract jth component & save # # TODO why does this need to be communicated here? surely owning proc # # just inserts? # dprod = comm.bcast(P_vec.vec().getValue(j), root=idx_root) # dprod_prior = comm.bcast(P2.vector().vec().getValue(j), root=idx_root) # if dprod < 0: # log.warning(f'WARNING: Negative Sigma: {dprod}') # log.warning('Setting as Zero and Continuing.') # neg_flag = 1 # continue # sigma_old.vector().vec().setValue(j, np.sqrt(dprod)) # sigma_prior_old.vector().vec().setValue(j, np.sqrt(dprod_prior)) # sigma_old.vector().apply("insert") # sigma_prior_old.vector().apply("insert") # For testing - whole thing at once: # wdw = (np.matrix(W) * np.matrix(D) * np.matrix(W).T) # wdw[:,0] == P1 for j = 0 # if neg_flag: # log.warning('Negative value(s) of sigma encountered.' # 'Examine the range of eigenvalues and check if ' # 'the threshlam paramater is set appropriately.') # Write sigma & sigma_prior to files # sigma_var_name = "_".join((cntrl.name(), "sigma")) # sigma_prior_var_name = "_".join((cntrl.name(), "sigma_prior")) # sigma_old.rename(sigma_var_name, "") # sigma_prior_old.rename(sigma_prior_var_name, "") # inout.write_variable(sigma_old, params, # name=sigma_var_name+"_old") # inout.write_variable(sigma_prior_old, params, # name=sigma_prior_var_name+"_old") for i, name in enumerate(cntrl_names): sigmas[i].rename("sigma_"+name, "") sigma_priors[i].rename("sigma_prior_"+name, "") phase_suffix_sigma = params.inv_sigma.phase_suffix inout.write_variable(sigmas[i], params, outdir=outdir, phase_name=params.inv_sigma.phase_name, phase_suffix=phase_suffix_sigma) inout.write_variable(sigma_priors[i], params, outdir=outdir, phase_name=params.inv_sigma.phase_name, phase_suffix=phase_suffix_sigma) mdl.cntrl_sigma = sigmas mdl.cntrl_sigma_prior = sigma_priors return mdl
def run_forward(config_file): # Read run config file params = ConfigParser(config_file) log = inout.setup_logging(params) inout.log_preamble("forward", params) outdir = params.io.output_dir diag_dir = params.io.diagnostics_dir phase_name = params.time.phase_name # Load the static model data (geometry, smb, etc) input_data = inout.InputData(params) # Get model mesh mesh = fice_mesh.get_mesh(params) # Define the model mdl = model.model(mesh, input_data, params) mdl.alpha_from_inversion() mdl.beta_from_inversion() # Solve slvr = solver.ssa_solver(mdl, mixed_space=params.inversion.dual) slvr.save_ts_zero() cntrl = slvr.get_control() qoi_func = slvr.get_qoi_func() # TODO here - cntrl now returns a list - so compute_gradient returns a list of tuples # Run the forward model Q = slvr.timestep(adjoint_flag=1, qoi_func=qoi_func) # Run the adjoint model, computing gradient of Qoi w.r.t cntrl dQ_ts = compute_gradient(Q, cntrl) # Isaac 27 # Output model variables in ParaView+Fenics friendly format # Output QOI & DQOI (needed for next steps) inout.write_qval(slvr.Qval_ts, params) inout.write_dqval(dQ_ts, [var.name() for var in cntrl], params) # Output final velocity, surface & thickness (visualisation) inout.write_variable(slvr.U, params, name="U_fwd", outdir=diag_dir, phase_name=phase_name, phase_suffix=params.time.phase_suffix) inout.write_variable(mdl.surf, params, name="surf_fwd", outdir=diag_dir, phase_name=phase_name, phase_suffix=params.time.phase_suffix) H = project(mdl.H, mdl.Q) inout.write_variable(H, params, name="H_fwd", outdir=diag_dir, phase_name=phase_name, phase_suffix=params.time.phase_suffix) return mdl
def run_invsigma(config_file): """Compute control sigma values from eigendecomposition""" comm = MPI.comm_world rank = comm.rank # Read run config file params = ConfigParser(config_file) # Setup logging log = inout.setup_logging(params) inout.log_preamble("inv sigma", params) outdir = params.io.output_dir # Load the static model data (geometry, smb, etc) input_data = inout.InputData(params) eigendir = outdir lamfile = params.io.eigenvalue_file vecfile = params.io.eigenvecs_file threshlam = params.eigendec.eigenvalue_thresh # Get model mesh mesh = fice_mesh.get_mesh(params) # Define the model (only need alpha & beta though) mdl = model.model(mesh, input_data, params, init_fields=False) # Load alpha/beta fields mdl.alpha_from_inversion() mdl.beta_from_inversion() # Regularization operator using inversion delta/gamma values # TODO - this won't handle dual inversion case if params.inversion.alpha_active: delta = params.inversion.delta_alpha gamma = params.inversion.gamma_alpha cntrl = mdl.alpha elif params.inversion.beta_active: delta = params.inversion.delta_beta gamma = params.inversion.gamma_beta cntrl = mdl.beta space = cntrl.function_space() sigma, sigma_prior, x, y, z = [Function(space) for i in range(5)] reg_op = prior.laplacian(delta, gamma, space) # Load the eigenvalues with open(os.path.join(eigendir, lamfile), 'rb') as ff: eigendata = pickle.load(ff) lam = eigendata[0].real.astype(np.float64) nlam = len(lam) # Read in the eigenvectors and check they are normalised # w.r.t. the prior (i.e. the B matrix in our GHEP) eps = params.constants.float_eps W = [] with HDF5File(comm, os.path.join(eigendir, vecfile), 'r') as hdf5data: for i in range(nlam): w = Function(space) hdf5data.read(w, f'v/vector_{i}') # Test norm in prior == 1.0 reg_op.action(w.vector(), y.vector()) norm_in_prior = w.vector().inner(y.vector()) assert (abs(norm_in_prior - 1.0) < eps) W.append(w) # Which eigenvalues are larger than our threshold? pind = np.flatnonzero(lam > threshlam) lam = lam[pind] W = [W[i] for i in pind] D = np.diag(lam / (lam + 1)) neg_flag = 0 # Isaac Eq. 20 # P2 = prior # P1 = WDW # Note - don't think we're considering the cross terms # in the posterior covariance. # TODO - this isn't particularly well parallelised - can it be improved? for j in range(space.dim()): # Who owns this index? own_idx = y.vector().owns_index(j) ownership = np.where(comm.allgather(own_idx))[0] assert len(ownership) == 1 idx_root = ownership[0] y.vector().zero() y.vector().vec().setValue(j, 1.0) y.vector().apply('insert') tmp2 = np.asarray( [D[i, i] * w.vector().vec().getValue(j) for i, w in enumerate(W)]) tmp2 = comm.bcast(tmp2, root=idx_root) P1 = Function(space) for tmp, w in zip(tmp2, W): P1.vector().axpy(tmp, w.vector()) reg_op.inv_action(y.vector(), x.vector()) P2 = x P_vec = P2.vector() - P1.vector() dprod = comm.bcast(P_vec.vec().getValue(j), root=idx_root) dprod_prior = comm.bcast(P2.vector().vec().getValue(j), root=idx_root) if dprod < 0: log.warning(f'WARNING: Negative Sigma: {dprod}') log.warning('Setting as Zero and Continuing.') neg_flag = 1 continue sigma.vector().vec().setValue(j, np.sqrt(dprod)) sigma_prior.vector().vec().setValue(j, np.sqrt(dprod_prior)) sigma.vector().apply("insert") sigma_prior.vector().apply("insert") # For testing - whole thing at once: # wdw = (np.matrix(W) * np.matrix(D) * np.matrix(W).T) # wdw[:,0] == P1 for j = 0 if neg_flag: log.warning('Negative value(s) of sigma encountered.' 'Examine the range of eigenvalues and check if ' 'the threshlam paramater is set appropriately.') # Write sigma & sigma_prior to files sigma_var_name = "_".join((cntrl.name(), "sigma")) sigma_prior_var_name = "_".join((cntrl.name(), "sigma_prior")) sigma.rename(sigma_var_name, "") sigma_prior.rename(sigma_prior_var_name, "") inout.write_variable(sigma, params, name=sigma_var_name) inout.write_variable(sigma_prior, params, name=sigma_prior_var_name) mdl.cntrl_sigma = sigma mdl.cntrl_sigma_prior = sigma_prior return mdl
def run_forward(config_file): #Read run config file params = ConfigParser(config_file) log = inout.setup_logging(params) inout.log_preamble("forward", params) outdir = params.io.output_dir # Load the static model data (geometry, smb, etc) input_data = inout.InputData(params) # Get model mesh mesh = fice_mesh.get_mesh(params) # Define the model mdl = model.model(mesh, input_data, params) mdl.alpha_from_inversion() mdl.beta_from_inversion() # Solve slvr = solver.ssa_solver(mdl) slvr.save_ts_zero() cntrl = slvr.get_control() qoi_func = slvr.get_qoi_func() #TODO here - cntrl now returns a list - so compute_gradient returns a list of tuples #Run the forward model Q = slvr.timestep(adjoint_flag=1, qoi_func=qoi_func) #Run the adjoint model, computing gradient of Qoi w.r.t cntrl dQ_ts = compute_gradient(Q, cntrl) #Isaac 27 #Uncomment for Taylor Verification, Comment above two lines # param['num_sens'] = 1 # J = slvr.timestep(adjoint_flag=1, cst_func=slvr.comp_Q_vaf) # dJ = compute_gradient(J, slvr.alpha) # # # def forward_ts(alpha_val=None): # slvr.reset_ts_zero() # if alpha_val: # slvr.alpha = alpha_val # return slvr.timestep(adjoint_flag=1, cst_func=slvr.comp_Q_vaf) # # # min_order = taylor_test(lambda alpha : forward_ts(alpha_val = alpha), slvr.alpha, # J_val = J.value(), dJ = dJ, seed = 1e-2, size = 6) # sys.exit(os.EX_OK) # Output model variables in ParaView+Fenics friendly format outdir = params.io.output_dir # Output QOI & DQOI (needed for next steps) inout.write_qval(slvr.Qval_ts, params) inout.write_dqval(dQ_ts, [var.name() for var in cntrl], params) # Output final velocity, surface & thickness (visualisation) inout.write_variable(slvr.U, params, name="U_fwd") inout.write_variable(mdl.surf, params, name="surf_fwd") H = project(mdl.H, mdl.Q) inout.write_variable(H, params, name="H_fwd") return mdl