def solve_problem(self): start = time.time() # print header utilities.print_problem(self.pb.problem_physics, self.pb.pbs.comm, self.pb.pbs.ndof) # read restart information if self.pb.pbs.restart_step > 0: self.pb.pbs.io.readcheckpoint(self.pb.pbs, self.pb.pbs.restart_step) self.pb.pbs.simname += '_r' + str(self.pb.pbs.restart_step) self.pb.set_pressure_fem(self.pb.lm_old, self.pb.coupfuncs_old) # in case we want to prestress with MULF (Gee et al. 2010) prior to solving the 3D-0D problem if self.pb.pbs.prestress_initial and self.pb.pbs.restart_step == 0: # solve solid prestress problem self.solverprestr.solve_initial_prestress() del self.solverprestr else: # set flag definitely to False if we're restarting self.pb.pbs.prestress_initial = False self.pb.constr, self.pb.constr_old = [], [] for i in range(self.pb.num_coupling_surf): lm_sq, lm_old_sq = allgather_vec(self.pb.lm, self.pb.comm), allgather_vec( self.pb.lm_old, self.pb.comm) con = assemble_scalar(self.pb.cq[i]) con = self.pb.pbs.comm.allgather(con) self.pb.constr.append(sum(con)) self.pb.constr_old.append(sum(con)) # consider consistent initial acceleration if self.pb.pbs.timint != 'static' and self.pb.pbs.restart_step == 0: # weak form at initial state for consistent initial acceleration solve weakform_a = self.pb.pbs.deltaW_kin_old + self.pb.pbs.deltaW_int_old - self.pb.pbs.deltaW_ext_old - self.pb.work_coupling_old jac_a = derivative(weakform_a, self.pb.pbs.a_old, self.pb.pbs.du) # actually linear in a_old # solve for consistent initial acceleration a_old self.solnln.solve_consistent_ini_acc(weakform_a, jac_a, self.pb.pbs.a_old) # write mesh output self.pb.pbs.io.write_output(self.pb.pbs, writemesh=True) # solid constraint main time loop for N in range(self.pb.pbs.restart_step + 1, self.pb.pbs.numstep_stop + 1): wts = time.time() # current time t = N * self.pb.pbs.dt # set time-dependent functions self.pb.pbs.ti.set_time_funcs(self.pb.pbs.ti.funcs_to_update, self.pb.pbs.ti.funcs_to_update_vec, t) # take care of active stress if self.pb.pbs.have_active_stress and self.pb.pbs.active_stress_trig == 'ode': self.pb.pbs.evaluate_active_stress_ode(t) # solve self.solnln.newton(self.pb.pbs.u, self.pb.pbs.p, self.pb.lm, t, locvar=self.pb.pbs.theta, locresform=self.pb.pbs.r_growth, locincrform=self.pb.pbs.del_theta) # update time step self.pb.pbs.ti.update_timestep( self.pb.pbs.u, self.pb.pbs.u_old, self.pb.pbs.v_old, self.pb.pbs.a_old, self.pb.pbs.p, self.pb.pbs.p_old, self.pb.pbs.internalvars, self.pb.pbs.internalvars_old, self.pb.pbs.ti.funcs_to_update, self.pb.pbs.ti.funcs_to_update_old, self.pb.pbs.ti.funcs_to_update_vec, self.pb.pbs.ti.funcs_to_update_vec_old) # update old pressures on solid self.pb.lm.assemble(), self.pb.lm_old.axpby(1.0, 0.0, self.pb.lm) self.pb.set_pressure_fem(self.pb.lm_old, self.pb.coupfuncs_old) # update old 3D constraint variable for i in range(self.pb.num_coupling_surf): self.pb.constr_old[i] = self.pb.constr[i] # solve time for time step wte = time.time() wt = wte - wts # print time step info to screen self.pb.pbs.ti.print_timestep(N, t, wt=wt) # write output and restart info self.pb.pbs.io.write_output(self.pb.pbs, N=N, t=t) if self.pb.comm.rank == 0: # only proc 0 should print this print('Time for computation: %.4f s (= %.2f min)' % (time.time() - start, (time.time() - start) / 60.)) sys.stdout.flush()
def solve_problem(self): start = time.time() # print header utilities.print_problem(self.pb.problem_physics, self.pb.comm, self.pb.ndof) # read restart information if self.pb.restart_step > 0: self.pb.io.readcheckpoint(self.pb, self.pb.restart_step) self.pb.simname += '_r'+str(self.pb.restart_step) # in case we want to prestress with MULF (Gee et al. 2010) prior to solving the full solid problem if self.pb.prestress_initial and self.pb.restart_step == 0: self.solve_initial_prestress() else: # set flag definitely to False if we're restarting self.pb.prestress_initial = False # consider consistent initial acceleration if self.pb.timint != 'static' and self.pb.restart_step == 0: # weak form at initial state for consistent initial acceleration solve weakform_a = self.pb.deltaW_kin_old + self.pb.deltaW_int_old - self.pb.deltaW_ext_old jac_a = derivative(weakform_a, self.pb.a_old, self.pb.du) # actually linear in a_old # solve for consistent initial acceleration a_old self.solnln.solve_consistent_ini_acc(weakform_a, jac_a, self.pb.a_old) # write mesh output self.pb.io.write_output(self.pb, writemesh=True) # solid main time loop for N in range(self.pb.restart_step+1, self.pb.numstep_stop+1): wts = time.time() # current time t = N * self.pb.dt # set time-dependent functions self.pb.ti.set_time_funcs(self.pb.ti.funcs_to_update, self.pb.ti.funcs_to_update_vec, t) # take care of active stress if self.pb.have_active_stress and self.pb.active_stress_trig == 'ode': self.pb.evaluate_active_stress_ode(t) # solve self.solnln.newton(self.pb.u, self.pb.p, locvar=self.pb.theta, locresform=self.pb.r_growth, locincrform=self.pb.del_theta) # compute the growth rate (has to be called before update_timestep) if self.pb.have_growth: self.pb.compute_solid_growth_rate(N, t) # write output self.pb.io.write_output(self.pb, N=N, t=t) # update - displacement, velocity, acceleration, pressure, all internal variables, all time functions self.pb.ti.update_timestep(self.pb.u, self.pb.u_old, self.pb.v_old, self.pb.a_old, self.pb.p, self.pb.p_old, self.pb.internalvars, self.pb.internalvars_old, self.pb.ti.funcs_to_update, self.pb.ti.funcs_to_update_old, self.pb.ti.funcs_to_update_vec, self.pb.ti.funcs_to_update_vec_old) # solve time for time step wte = time.time() wt = wte - wts # print time step info to screen self.pb.ti.print_timestep(N, t, wt=wt) # write restart info - old and new quantities are the same at this stage self.pb.io.write_restart(self.pb, N) if self.pb.problem_type == 'solid_flow0d_multiscale_gandr' and abs(self.pb.growth_rate) <= self.pb.tol_stop_large: break if self.pb.comm.rank == 0: # only proc 0 should print this print('Time for computation: %.4f s (= %.2f min)' % ( time.time()-start, (time.time()-start)/60. )) sys.stdout.flush()
def solve_problem(self): start = time.time() # print header utilities.print_problem(self.pb.problem_physics, self.pb.comm) # multiscale growth and remodeling solid 0D flow main time loop for N in range(self.pb.restart_cycle + 1, self.pb.N_cycles + 1): wts = time.time() # time offset from previous small scale times self.pb.pbsmall.t_prev = (self.pb.pbsmall.pbf.ti.cycle[0] - 1 ) * self.pb.pbsmall.pbf.cardvasc0D.T_cycl # change output names self.pb.pbsmall.pbs.simname = self.pb.simname_small + str(N) self.pb.pblarge.simname = self.pb.simname_large + str(N) self.set_state_small() if not self.pb.restart_from_small: if self.pb.comm.rank == 0: print( "Solving small scale 3D-0D coupled solid-flow0d problem:" ) sys.stdout.flush() # solve small scale 3D-0D coupled solid-flow0d problem with fixed growth self.solversmall.solve_problem() # next small scale run is a resumption of a previous one self.pb.pbsmall.restart_multiscale = True if self.pb.write_checkpoints: # write checkpoint for potential restarts self.pb.pbsmall.pbs.io.writecheckpoint( self.pb.pbsmall.pbs, N) self.pb.pbsmall.pbf.writerestart( self.pb.pbsmall.pbs.simname, N, ms=True) else: # read small scale checkpoint if we restart from this scale self.pb.pbsmall.pbs.io.readcheckpoint( self.pb.pbsmall.pbs, self.pb.restart_cycle + 1) self.pb.pbsmall.pbf.readrestart(self.pb.pbsmall.pbs.simname, self.pb.restart_cycle + 1, ms=True) # induce the perturbation if not self.pb.pbsmall.pbf.have_induced_pert: self.pb.pbsmall.induce_perturbation() # no need to do after restart self.pb.pbsmall.pbs.prestress_initial = False # set flag to False again self.pb.restart_from_small = False # next small scale run is a resumption of a previous one self.pb.pbsmall.restart_multiscale = True # set large scale state self.set_state_large(N) # compute volume prior to G&R vol_prior = self.compute_volume_large() if self.pb.comm.rank == 0: print( "Solving large scale solid growth and remodeling problem:") sys.stdout.flush() # solve large scale static G&R solid problem with fixed loads self.solverlarge.solve_problem() # compute volume after G&R vol_after = self.compute_volume_large() if self.pb.write_checkpoints: # write checkpoint for potential restarts self.pb.pblarge.io.writecheckpoint(self.pb.pblarge, N) # relative volume increase over large scale run volchange = (vol_after - vol_prior) / vol_prior if self.pb.comm.rank == 0: print('Volume change due to growth: %.4e' % (volchange)) sys.stdout.flush() # check if below tolerance if abs(volchange) <= self.pb.tol_outer: break if self.pb.comm.rank == 0: # only proc 0 should print this print('Time for full multiscale computation: %.4f s (= %.2f min)' % (time.time() - start, (time.time() - start) / 60.)) sys.stdout.flush()
def solve_problem(self): start = time.time() # print header utilities.print_problem(self.pb.problem_physics, self.pb.comm, self.pb.ndof) # read restart information if self.pb.restart_step > 0: self.pb.io.readcheckpoint(self.pb, self.pb.restart_step) self.pb.simname += '_r' + str(self.pb.restart_step) # consider consistent initial acceleration if self.pb.timint != 'static' and self.pb.restart_step == 0: # weak form at initial state for consistent initial acceleration solve weakform_a = self.pb.deltaP_kin_old + self.pb.deltaP_int_old - self.pb.deltaP_ext_old jac_a = derivative(weakform_a, self.pb.a_old, self.pb.dv) # actually linear in a_old # solve for consistent initial acceleration a_old self.solnln.solve_consistent_ini_acc(weakform_a, jac_a, self.pb.a_old) # write mesh output self.pb.io.write_output(self.pb, writemesh=True) # fluid main time loop for N in range(self.pb.restart_step + 1, self.pb.numstep_stop + 1): wts = time.time() # current time t = N * self.pb.dt # set time-dependent functions self.pb.ti.set_time_funcs(self.pb.ti.funcs_to_update, self.pb.ti.funcs_to_update_vec, t) # solve self.solnln.newton(self.pb.v, self.pb.p) # write output self.pb.io.write_output(self.pb, N=N, t=t) # update self.pb.ti.update_timestep(self.pb.v, self.pb.v_old, self.pb.a_old, self.pb.p, self.pb.p_old, self.pb.ti.funcs_to_update, self.pb.ti.funcs_to_update_old, self.pb.ti.funcs_to_update_vec, self.pb.ti.funcs_to_update_vec_old) # solve time for time step wte = time.time() wt = wte - wts # print time step info to screen self.pb.ti.print_timestep(N, t, wt=wt) # write restart info - old and new quantities are the same at this stage self.pb.io.write_restart(self.pb, N) if self.pb.comm.rank == 0: # only proc 0 should print this print('Time for computation: %.4f s (= %.2f min)' % (time.time() - start, (time.time() - start) / 60.)) sys.stdout.flush()
def solve_problem(self): start = time.time() # print header utilities.print_problem(self.pb.problem_type, self.pb.comm, self.pb.cardvasc0D.numdof) # read restart information if self.pb.restart_step > 0: self.pb.readrestart(self.pb.simname, self.pb.restart_step) self.pb.simname += '_r' + str(self.pb.restart_step) # evaluate old state if self.pb.excitation_curve is not None: self.pb.c = [] self.pb.c.append( self.pb.ti.timecurves(self.pb.excitation_curve)( self.pb.t_init)) if bool(self.pb.chamber_models): self.pb.y = [] for ch in ['lv', 'rv', 'la', 'ra']: if self.pb.chamber_models[ch]['type'] == '0D_elast': self.pb.y.append( self.pb.ti.timecurves( self.pb.chamber_models[ch]['activation_curve'])( self.pb.t_init)) if self.pb.chamber_models[ch]['type'] == '0D_elast_prescr': self.pb.y.append( self.pb.ti.timecurves( self.pb.chamber_models[ch]['elastance_curve'])( self.pb.t_init)) if self.pb.chamber_models[ch]['type'] == '0D_prescr': self.pb.c.append( self.pb.ti.timecurves( self.pb.chamber_models[ch]['prescribed_curve'])( self.pb.t_init)) self.pb.cardvasc0D.evaluate(self.pb.s_old, self.pb.t_init, self.pb.df_old, self.pb.f_old, None, None, self.pb.c, self.pb.y, self.pb.aux_old) # flow 0d main time loop for N in range(self.pb.restart_step + 1, self.pb.numstep_stop + 1): wts = time.time() # current time t = N * self.pb.dt # offset time for multiple cardiac cycles t_off = ( self.pb.ti.cycle[0] - 1 ) * self.pb.cardvasc0D.T_cycl # zero if T_cycl variable is not specified # external volume/flux from time curve if self.pb.excitation_curve is not None: self.pb.c[0] = self.pb.ti.timecurves( self.pb.excitation_curve)(t - t_off) # activation curves self.pb.evaluate_activation(t - t_off) # solve self.solnln.newton(self.pb.s, t - t_off) # get midpoint dof values for post-processing (has to be called before update!) self.pb.cardvasc0D.midpoint_avg( self.pb.s, self.pb.s_old, self.pb.s_mid, self.pb.theta_ost), self.pb.cardvasc0D.midpoint_avg( self.pb.aux, self.pb.aux_old, self.pb.aux_mid, self.pb.theta_ost) # raw txt file output of 0D model quantities if self.pb.write_results_every_0D > 0 and N % self.pb.write_results_every_0D == 0: self.pb.cardvasc0D.write_output(self.pb.output_path_0D, t, self.pb.s_mid, self.pb.aux_mid, self.pb.simname) # update timestep self.pb.cardvasc0D.update(self.pb.s, self.pb.df, self.pb.f, self.pb.s_old, self.pb.df_old, self.pb.f_old, self.pb.aux, self.pb.aux_old) # print to screen self.pb.cardvasc0D.print_to_screen(self.pb.s_mid, self.pb.aux_mid) # solve time for time step wte = time.time() wt = wte - wts # print time step info to screen self.pb.ti.print_timestep(N, t, self.pb.numstep, wt=wt) # check for periodicity in cardiac cycle and stop if reached (only for syspul* models - cycle counter gets updated here) is_periodic = self.pb.cardvasc0D.cycle_check( self.pb.s, self.pb.sTc, self.pb.sTc_old, t - t_off, self.pb.ti.cycle, self.pb.ti.cycleerror, self.pb.eps_periodic, check=self.pb.periodic_checktype, inioutpath=self.pb.output_path_0D, nm=self.pb.simname, induce_pert_after_cycl=self.pb.perturb_after_cylce) # induce some disease/perturbation for cardiac cycle (i.e. valve stenosis or leakage) if self.pb.perturb_type is not None and not self.pb.have_induced_pert: self.pb.induce_perturbation() # write 0D restart info - old and new quantities are the same at this stage (except cycle values sTc) if self.pb.write_restart_every > 0 and N % self.pb.write_restart_every == 0: self.pb.writerestart(self.pb.simname, N) if is_periodic: if self.pb.comm.rank == 0: print( "Periodicity reached after %i heart cycles with cycle error %.4f! Finished. :-)" % (self.pb.ti.cycle[0] - 1, self.pb.ti.cycleerror[0])) sys.stdout.flush() break if self.pb.comm.rank == 0: # only proc 0 should print this print('Time for computation: %.4f s (= %.2f min)' % (time.time() - start, (time.time() - start) / 60.)) sys.stdout.flush()
def solve_problem(self): start = time.time() # print header utilities.print_problem(self.pb.problem_physics, self.pb.pbs.comm, self.pb.pbs.ndof) # read restart information if self.pb.pbs.restart_step > 0: self.pb.pbs.io.readcheckpoint(self.pb.pbs, self.pb.pbs.restart_step) self.pb.pbf.readrestart(self.pb.pbs.simname, self.pb.pbs.restart_step) self.pb.pbs.simname += '_r' + str(self.pb.pbs.restart_step) # set pressure functions for old state - s_old already initialized by 0D flow problem if self.pb.coupling_type == 'monolithic_direct': self.pb.pbf.cardvasc0D.set_pressure_fem( self.pb.pbf.s_old, self.pb.pbf.cardvasc0D.v_ids, self.pb.pr0D, self.pb.coupfuncs_old) if self.pb.coupling_type == 'monolithic_lagrange': self.pb.pbf.cardvasc0D.set_pressure_fem( self.pb.lm_old, list(range(self.pb.num_coupling_surf)), self.pb.pr0D, self.pb.coupfuncs_old) if self.pb.coupling_type == 'monolithic_direct': # old 3D coupling quantities (volumes or fluxes) for i in range(self.pb.num_coupling_surf): cq = assemble_scalar(self.pb.cq_old[i]) cq = self.pb.pbs.comm.allgather(cq) self.pb.pbf.c.append(sum(cq) * self.pb.cq_factor[i]) if self.pb.coupling_type == 'monolithic_lagrange': for i in range(self.pb.num_coupling_surf): lm_sq, lm_old_sq = allgather_vec(self.pb.lm, self.pb.comm), allgather_vec( self.pb.lm_old, self.pb.comm) self.pb.pbf.c.append(lm_sq[i]) con = assemble_scalar(self.pb.cq_old[i]) con = self.pb.pbs.comm.allgather(con) self.pb.constr.append(sum(con) * self.pb.cq_factor[i]) self.pb.constr_old.append(sum(con) * self.pb.cq_factor[i]) if bool(self.pb.pbf.chamber_models): self.pb.pbf.y = [] for ch in self.pb.pbf.chamber_models: if self.pb.pbf.chamber_models[ch]['type'] == '0D_elast': self.pb.pbf.y.append( self.pb.pbs.ti.timecurves( self.pb.pbf.chamber_models[ch]['activation_curve']) (self.pb.pbs.t_init)) # initially evaluate 0D model at old state self.pb.pbf.cardvasc0D.evaluate(self.pb.pbf.s_old, self.pb.pbs.dt, self.pb.pbs.t_init, self.pb.pbf.df_old, self.pb.pbf.f_old, None, self.pb.pbf.c, self.pb.pbf.y, self.pb.pbf.aux_old) # consider consistent initial acceleration if self.pb.pbs.timint != 'static' and self.pb.pbs.restart_step == 0: # weak form at initial state for consistent initial acceleration solve weakform_a = self.pb.pbs.deltaP_kin_old + self.pb.pbs.deltaP_int_old - self.pb.pbs.deltaP_ext_old - self.pb.power_coupling_old jac_a = derivative(weakform_a, self.pb.pbs.a_old, self.pb.pbs.dv) # actually linear in a_old # solve for consistent initial acceleration a_old self.solnln.solve_consistent_ini_acc(weakform_a, jac_a, self.pb.pbs.a_old) # write mesh output self.pb.pbs.io.write_output(self.pb.pbs, writemesh=True) # fluid 0D flow main time loop for N in range(self.pb.restart_step + 1, self.pb.numstep_stop + 1): wts = time.time() # current time t = N * self.pb.pbs.dt # offset time for multiple cardiac cycles t_off = ( self.pb.pbf.ti.cycle[0] - 1 ) * self.pb.pbf.cardvasc0D.T_cycl # zero if T_cycl variable is not specified # set time-dependent functions self.pb.pbs.ti.set_time_funcs(self.pb.pbs.ti.funcs_to_update, self.pb.pbs.ti.funcs_to_update_vec, t - t_off) # activation curves for 0D chambers (if present) self.pb.pbf.evaluate_activation(t - t_off) # solve self.solnln.newton(self.pb.pbs.v, self.pb.pbs.p, self.pb.pbf.s, t - t_off) # get midpoint dof values for post-processing (has to be called before update!) self.pb.pbf.cardvasc0D.midpoint_avg( self.pb.pbf.s, self.pb.pbf.s_old, self.pb.pbf.s_mid), self.pb.pbf.cardvasc0D.midpoint_avg( self.pb.pbf.aux, self.pb.pbf.aux_old, self.pb.pbf.aux_mid) # update time step - fluid and 0D model self.pb.pbs.ti.update_timestep( self.pb.pbs.v, self.pb.pbs.v_old, self.pb.pbs.a_old, self.pb.pbs.p, self.pb.pbs.p_old, self.pb.pbs.ti.funcs_to_update, self.pb.pbs.ti.funcs_to_update_old, self.pb.pbs.ti.funcs_to_update_vec, self.pb.pbs.ti.funcs_to_update_vec_old) self.pb.pbf.cardvasc0D.update(self.pb.pbf.s, self.pb.pbf.df, self.pb.pbf.f, self.pb.pbf.s_old, self.pb.pbf.df_old, self.pb.pbf.f_old, self.pb.pbf.aux, self.pb.pbf.aux_old) # update old pressures on fluid if self.pb.coupling_type == 'monolithic_direct': self.pb.pbf.cardvasc0D.set_pressure_fem( self.pb.pbf.s_old, self.pb.pbf.cardvasc0D.v_ids, self.pb.pr0D, self.pb.coupfuncs_old) if self.pb.coupling_type == 'monolithic_lagrange': self.pb.lm.assemble(), self.pb.lm_old.axpby( 1.0, 0.0, self.pb.lm) self.pb.pbf.cardvasc0D.set_pressure_fem( self.pb.lm_old, list(range(self.pb.num_coupling_surf)), self.pb.pr0D, self.pb.coupfuncs_old) # update old 3D fluxes for i in range(self.pb.num_coupling_surf): self.pb.constr_old[i] = self.pb.constr[i] # solve time for time step wte = time.time() wt = wte - wts # print to screen self.pb.pbf.cardvasc0D.print_to_screen(self.pb.pbf.s_mid, self.pb.pbf.aux_mid) # print time step info to screen self.pb.pbf.ti.print_timestep(N, t, self.pb.pbs.numstep, wt=wt) # check for periodicity in cardiac cycle and stop if reached (only for syspul* models - cycle counter gets updated here) is_periodic = self.pb.pbf.cardvasc0D.cycle_check( self.pb.pbf.s, self.pb.pbf.sTc, self.pb.pbf.sTc_old, t - t_off, self.pb.pbf.ti.cycle, self.pb.pbf.ti.cycleerror, self.pb.pbf.eps_periodic, check=self.pb.pbf.periodic_checktype, inioutpath=self.pb.pbf.output_path_0D, nm=self.pb.pbs.simname, induce_pert_after_cycl=self.pb.pbf.perturb_after_cylce) # induce some disease/perturbation for cardiac cycle (i.e. valve stenosis or leakage) if self.pb.pbf.perturb_type is not None and not self.pb.pbf.have_induced_pert: self.pb.induce_perturbation() # write output and restart info self.pb.pbs.io.write_output(self.pb.pbs, N=N, t=t) # raw txt file output of 0D model quantities if self.pb.pbf.write_results_every_0D > 0 and N % self.pb.pbf.write_results_every_0D == 0: self.pb.pbf.cardvasc0D.write_output(self.pb.pbf.output_path_0D, t, self.pb.pbf.s_mid, self.pb.pbf.aux_mid, self.pb.pbs.simname) # write 0D restart info - old and new quantities are the same at this stage (except cycle values sTc) if self.pb.pbs.io.write_restart_every > 0 and N % self.pb.pbs.io.write_restart_every == 0: self.pb.pbf.writerestart(self.pb.pbs.simname, N) if is_periodic: if self.pb.comm.rank == 0: print( "Periodicity reached after %i heart cycles with cycle error %.4f! Finished. :-)" % (self.pb.pbf.ti.cycle[0] - 1, self.pb.pbf.ti.cycleerror[0])) sys.stdout.flush() break if self.pb.comm.rank == 0: # only proc 0 should print this print('Time for computation: %.4f s (= %.2f min)' % (time.time() - start, (time.time() - start) / 60.)) sys.stdout.flush()