def initialize_xdmf_files(self): info(' Initializing output files.') # for creating paraview scripts self.metadata['filename_base'] = self.problem_code + '_' + self.metadata['name'] # assemble file dictionary if self.doSave: if self.doSaveDiff: self.fileDict.update(self.fileDictDiff) if self.metadata['hasTentativeV']: self.fileDict.update(self.fileDictTent) if self.doSaveDiff: self.fileDict.update(self.fileDictTentDiff) if self.metadata['hasTentativeP']: self.fileDict.update(self.fileDictTentP) if self.doSaveDiff: self.fileDict.update(self.fileDictTentPDiff) else: self.fileDict = {} if self.args.wss != 'none': self.fileDict.update(self.fileDictWSSnorm) if self.args.wss_method == 'expression': self.fileDict.update(self.fileDictWSS) if self.args.debug_rot: self.fileDict.update(self.fileDictDebugRot) # create and setup files for key, value in self.fileDict.iteritems(): value['file'] = XDMFFile(mpi_comm_world(), self.str_dir_name + "/" + self.problem_code + '_' + self.metadata['name'] + value['name'] + ".xdmf") value['file'].parameters['rewrite_function_mesh'] = False # save mesh only once per file
def initialize_xdmf_files(self): info(' Initializing output files.') # for creating paraview scripts self.metadata['filename_base'] = self.problem_code + '_' + self.metadata['name'] # assemble file dictionary if self.doSaveDiff: self.fileDict.update(self.fileDictDiff) if self.metadata['hasTentativeV']: self.fileDict.update(self.fileDictTent) if self.doSaveDiff: self.fileDict.update(self.fileDictTentDiff) if self.metadata['hasTentativeP']: self.fileDict.update(self.fileDictTentP) if self.doSaveDiff: self.fileDict.update(self.fileDictTentPDiff) if self.args.ldsg: self.fileDict.update(self.fileDictLDSG) if self.args.wss: self.fileDict.update(self.fileDictWSS) # create files for key, value in self.fileDict.iteritems(): value['file'] = XDMFFile(mpi_comm_world(), self.str_dir_name + "/" + self.problem_code + '_' + self.metadata['name'] + value['name'] + ".xdmf") value['file'].parameters['rewrite_function_mesh'] = False # saves lots of space (for use with static mesh)
def __init__(self): info('Initializing Time control') # watch is list [total_time, last_start, message_when_measured, count into total time, count into selected time] self.watches = {} self.last_measurement = 0 self.measuring = 0 tic()
def __init__(self): info('Initializing Time control') # watch is list [total_time, last_start, message_when_measured, count into total time] self.watches = {} self.last_measurement = 0 self.measuring = 0 tic()
def end(self, what): watch = self.watches[what] elapsed = toc() - watch[1] watch[0] += elapsed if self.watches[what][3]: self.measuring -= 1 info(watch[2]+'. Time: %.4f Total: %.4f' % (elapsed, watch[0])) self.last_measurement = toc()
def end(self, what): watch = self.watches[what] elapsed = toc() - watch[1] watch[0] += elapsed if self.watches[what][3]: self.measuring -= 1 self.last_measurement = toc() info(watch[2]+'. Time: %.4f Total: %.4f' % (elapsed, watch[0]))
def averaging_pressure(self, pressure): self.tc.start('averageP') # averaging pressure (substract average) p_average = assemble((1.0/self.volume) * pressure * dx) info('Average pressure: %f' % p_average) p_average_function = interpolate(Expression("p", p=p_average), self.pSpace) # info(p_average_function, pressure, pressure_Q) pressure.assign(pressure - p_average_function) self.tc.end('averageP')
def start(self, what): if what in self.watches: if self.watches[what][3]: self.measuring += 1 self.watches[what][1] = toc() from_last = toc()-self.last_measurement if self.measuring > 1: info('TC (%s): More watches at same time: %d' % (what, self.measuring)) if from_last > 0.1: info('TC (%s): time from last end of measurement: %f' % (what, from_last))
def start(self, what): if what in self.watches: if self.watches[what][3]: self.measuring += 1 self.watches[what][1] = toc() from_last = toc()-self.last_measurement if self.measuring > 1: info('TC (%s): More watches at same time: %d' % (what, self.measuring)) elif from_last > 0.1 and self.watches[what][3]: info('TC (%s): time from last end of measurement: %f' % (what, from_last))
def initialize(self, V, Q, PS, D): super(Problem, self).initialize(V, Q, PS, D) #info("IC type: " + self.ic) info("Velocity scale factor = %4.2f" % self.factor) itp = Problem.v_function if self.args.itp == 1 else Problem.v_function_2 # generate inflow profiles for obj in self.inflows: obj['velocity_profile'] = Problem.InputVelocityProfile(self.factor*float(obj['reference_coef']), obj['center'], obj['normal'], float(obj['radius']), itp, degree=2)
def averaging_pressure(self, pressure): """ :param pressure: average is subtracted from it """ self.tc.start('averageP') # averaging pressure (subtract average) p_average = assemble((1.0 / self.mesh_volume) * pressure * dx) info('Average pressure: %f' % p_average) p_average_function = interpolate(Expression("p", p=p_average, degree=1), self.pSpace) pressure.assign(pressure - p_average_function) self.tc.end('averageP')
def compute_functionals(self, velocity, pressure, t, step): super(Problem, self).compute_functionals(velocity, pressure, t, step) out = 0 for obj in self.outflows: outflow = assemble(inner(velocity, self.normal)*obj['measure']) out += outflow self.listDict['outflow'+obj['number']]['list'].append(outflow) self.listDict['outflow']['list'].append(out) info('Outflow: %f' % out) self.last_status_functional = out/abs(self.last_inflow) self.listDict['oiratio']['list'].append(self.last_status_functional) info('Outflow/Inflow: %f' % self.last_status_functional)
def compute_functionals(self, velocity, pressure, t, step): super(Problem, self).compute_functionals(velocity, pressure, t, step) out = 0 for obj in self.outflows: outflow = assemble(inner(velocity, self.normal) * obj['measure']) out += outflow self.listDict['outflow' + obj['number']]['list'].append(outflow) self.listDict['outflow']['list'].append(out) info('Outflow: %f' % out) self.last_status_functional = out / abs(self.last_inflow) self.listDict['oiratio']['list'].append(self.last_status_functional) info('Outflow/Inflow: %f' % self.last_status_functional)
def initialize(self, V, Q, PS, D): super(Problem, self).initialize(V, Q, PS, D) #info("IC type: " + self.ic) info("Velocity scale factor = %4.2f" % self.factor) itp = Problem.v_function if self.args.itp == 1 else Problem.v_function_2 # generate inflow profiles for obj in self.inflows: obj['velocity_profile'] = Problem.InputVelocityProfile( self.factor * float(obj['reference_coef']), obj['center'], obj['normal'], float(obj['radius']), itp, degree=2)
def __init__(self, coarse_mesh, nref, p_coarse, p_fine, sym=False): """ :param dolfin.cpp.mesh.Mesh coarse_mesh: :param int nref: :param int p_coarse: :param int p_fine: :param bool sym: :return: """ print0("Creating approximation spaces") self.V_coarse = FunctionSpace(coarse_mesh, "CG", p_coarse) self.ndof_coarse = self.V_coarse.dim() refined_mesh = coarse_mesh for ref in xrange(nref): refined_mesh = refine(refined_mesh) # creates a new Mesh, initial coarse mesh is unchanged self.V_fine = FunctionSpace(refined_mesh, "CG", p_fine) self.ndof_fine = self.V_fine.dim() H = coarse_mesh.hmax() h = refined_mesh.hmax() self.alpha = log(H)/log(h) self.beta = p_fine + 1 if comm.rank == 0: prop = Table("Approximation properties") prop.set("ndof", "coarse", self.ndof_coarse) prop.set("ndof", "fine", self.ndof_fine) prop.set("h", "coarse", H) prop.set("h", "fine", h) info(prop) print "alpha = {}, beta = {}".format(self.alpha, self.beta) self.bc_coarse = None self.A_fine = PETScMatrix() self.B_fine = PETScMatrix() self.A_coarse = PETScMatrix() self.B_coarse = PETScMatrix() self.sym = sym self.switch_gep_matrices = False
def initialize(self, V, Q, PS, D): self.vSpace = V self.divSpace = D self.pSpace = Q self.solutionSpace = V self.vFunction = Function(V) self.divFunction = Function(D) self.pFunction = Function(Q) self.volume = assemble(interpolate(Expression("1.0"), Q) * dx) if self.doSave: # self.pgSpace = VectorFunctionSpace(mesh, "DG", 0) # self.pgFunction = Function(self.pgSpace) self.initialize_xdmf_files() self.stepsInSecond = int(round(1.0 / self.metadata['dt'])) info('stepsInSecond = %d' % self.stepsInSecond)
def update_time(self, actual_time, step_number): super(Problem, self).update_time(actual_time, step_number) if self.actual_time > 0.5 and abs(math.modf(actual_time)[0]) < 0.5*self.metadata['dt']: self.second_list.append(int(round(self.actual_time))) # Update boundary condition self.tc.start('updateBC') self.last_inflow = 0 for obj in self.inflows: obj['velocity_profile'].t = actual_time obj['velocity_profile'].onset_factor = self.onset_factor self.last_inflow += assemble(inner(obj['velocity_profile'], self.normal)*obj['measure']) info('Inflow: %f' % self.last_inflow) self.listDict['inflow']['list'].append(self.last_inflow) self.tc.end('updateBC')
def update_time(self, actual_time, step_number): self.actual_time = actual_time self.step_number = step_number self.time_list.append(self.actual_time) if self.onset < 0.001 or self.actual_time > self.onset: self.onset_factor = 1. else: self.onset_factor = (1. - cos(pi * actual_time / self.onset))*0.5 info('Onset factor: %f' % self.onset_factor) # save only n-th step in first second if self.doSave: if self.save_nth == 1 or actual_time > (1. - self.metadata['dt']/2.) or self.step_number % self.save_nth == 0: self.save_this_step = True else: self.save_this_step = False
def update_time(self, actual_time, step_number): super(Problem, self).update_time(actual_time, step_number) if self.actual_time > 0.5 and abs( math.modf(actual_time)[0]) < 0.5 * self.metadata['dt']: self.second_list.append(int(round(self.actual_time))) # Update boundary condition self.tc.start('updateBC') self.last_inflow = 0 for obj in self.inflows: obj['velocity_profile'].t = actual_time obj['velocity_profile'].onset_factor = self.onset_factor self.last_inflow += assemble( inner(obj['velocity_profile'], self.normal) * obj['measure']) info('Inflow: %f' % self.last_inflow) self.listDict['inflow']['list'].append(self.last_inflow) self.tc.end('updateBC')
def compute_functionals(self, velocity, pressure, t, step): if self.args.wss == 'all' or \ (step >= self.stepsInCycle and self.args.wss == 'peak' and (self.distance_from_chosen_steps < 0.5 * self.metadata['dt'])): # TODO check if choosing time steps works properly # QQ might skip step? change 0.5 to 0.51? self.tc.start('WSS') begin('WSS (%dth step)' % step) if self.args.wss_method == 'expression': stress = project(self.nu*2*sym(grad(velocity)), self.T) # pressure is not used as it contributes only to the normal component stress.set_allow_extrapolation(True) # need because of some inaccuracies in BoundaryMesh coordinates stress_b = interpolate(stress, self.Tb) # restrict stress to boundary mesh # self.fileDict['stress']['file'].write(stress_b, self.actual_time) # info('Saved stress tensor') info('Computing WSS') wss = dot(stress_b, self.nb) - inner(dot(stress_b, self.nb), self.nb)*self.nb wss_func = project(wss, self.Vb) wss_norm = project(sqrt_ufl(inner(wss, wss)), self.Sb) info('Saving WSS') self.fileDict['wss']['file'].write(wss_func, self.actual_time) self.fileDict['wss_norm']['file'].write(wss_norm, self.actual_time) if self.args.wss_method == 'integral': wss_norm = Function(self.SDG) mS = TestFunction(self.SDG) scaling = 1/FacetArea(self.mesh) stress = self.nu*2*sym(grad(velocity)) wss = dot(stress, self.normal) - inner(dot(stress, self.normal), self.normal)*self.normal wss_norm_form = scaling*mS*sqrt_ufl(inner(wss, wss))*ds # ds is integral over exterior facets only assemble(wss_norm_form, tensor=wss_norm.vector()) self.fileDict['wss_norm']['file'].write(wss_norm, self.actual_time) # to get vector WSS values: # NT this works, but in ParaView for (DG,1)-vector space glyphs are displayed in cell centers # wss_vector = [] # for i in range(3): # wss_component = Function(self.SDG) # wss_vector_form = scaling*wss[i]*mS*ds # assemble(wss_vector_form, tensor=wss_component.vector()) # wss_vector.append(wss_component) # wss_func = project(as_vector(wss_vector), self.VDG) # self.fileDict['wss']['file'].write(wss_func, self.actual_time) self.tc.end('WSS') end()
def initialize(self, V, Q, PS, D): """ :param V: velocity space :param Q: pressure space :param PS: scalar space of same order as V, used for analytic solution generation :param D: divergence of velocity space """ self.vSpace = V self.divSpace = D self.pSpace = Q self.solutionSpace = V self.vFunction = Function(V) self.divFunction = Function(D) self.pFunction = Function(Q) # self.pgSpace = VectorFunctionSpace(mesh, "DG", 0) # used to save pressure gradient as vectors # self.pgFunction = Function(self.pgSpace) self.initialize_xdmf_files() self.stepsInCycle = self.cycle_length / self.metadata['dt'] info('stepsInCycle = %f' % self.stepsInCycle) if self.args.wss != 'none': self.tc.start('WSSinit') if self.args.wss_method == 'expression': self.T = TensorFunctionSpace(self.mesh, 'Lagrange', 1) info('Generating boundary mesh') self.wall_mesh = BoundaryMesh(self.mesh, 'exterior') self.wall_mesh_oriented = BoundaryMesh(self.mesh, 'exterior', order=False) info(' Boundary mesh geometric dim: %d' % self.wall_mesh.geometry().dim()) info(' Boundary mesh topologic dim: %d' % self.wall_mesh.topology().dim()) self.Tb = TensorFunctionSpace(self.wall_mesh, 'Lagrange', 1) self.Vb = VectorFunctionSpace(self.wall_mesh, 'Lagrange', 1) info('Generating normal to boundary') normal_expr = self.NormalExpression(self.wall_mesh_oriented) Vn = VectorFunctionSpace(self.wall_mesh, 'DG', 0) self.nb = project(normal_expr, Vn) self.Sb = FunctionSpace(self.wall_mesh, 'DG', 0) if self.args.wss_method == 'integral': self.SDG = FunctionSpace(self.mesh, 'DG', 0) self.tc.end('WSSinit')
def compute_err(self, is_tent, velocity, t): if self.doErrControl: er_list_L2 = self.listDict['u2L2' if is_tent else 'u_L2']['list'] er_list_H1 = self.listDict['u2H1' if is_tent else 'u_H1']['list'] self.tc.start('errorV') # assemble is faster than errornorm errorL2_sq = assemble(inner(velocity - self.solution, velocity - self.solution) * dx) errorH1seminorm_sq = assemble(inner(grad(velocity - self.solution), grad(velocity - self.solution)) * dx) info(' H1 seminorm error: %f' % sqrt(errorH1seminorm_sq)) errorL2 = sqrt(errorL2_sq) errorH1 = sqrt(errorL2_sq + errorH1seminorm_sq) info(" Relative L2 error in velocity = %f" % (errorL2 / self.analytic_v_norm_L2)) self.last_error = errorH1 / self.analytic_v_norm_H1 self.last_status_functional = self.last_error info(" Relative H1 error in velocity = %f" % self.last_error) er_list_L2.append(errorL2) er_list_H1.append(errorH1) self.tc.end('errorV') if self.testErrControl: er_list_test_H1 = self.listDict['u2H1test' if is_tent else 'u_H1test']['list'] er_list_test_L2 = self.listDict['u2L2test' if is_tent else 'u_L2test']['list'] self.tc.start('errorVtest') er_list_test_L2.append(errornorm(velocity, self.solution, norm_type='L2', degree_rise=0)) er_list_test_H1.append(errornorm(velocity, self.solution, norm_type='H1', degree_rise=0)) self.tc.end('errorVtest') # stopping criteria for detecting diverging solution if self.last_error > self.divergence_treshold: raise RuntimeError('STOPPED: Failed divergence test!')
def update_time(self, actual_time, step_number): info('GS_UPDATE_TIME t = %f, step %d' % (actual_time, step_number)) self.actual_time = actual_time self.step_number = step_number self.time_list.append(self.actual_time) if self.onset < 0.001 or self.actual_time > self.onset: # TODO onset time relative to cycle_length self.onset_factor = 1. else: self.onset_factor = (1. - cos(pi * actual_time / self.onset))*0.5 info('GS_UPDATE_TIME Onset factor: %f' % self.onset_factor) # Manage saving choices for this step # save only n-th step in first second dec, i = modf(actual_time / self.cycle_length) i = int(i) info('GS_UPDATE_TIME Cycles done: %d' % i) info('GS_UPDATE_TIME Relative part of cycle: %f' % dec) dec *= self.cycle_length info('GS_UPDATE_TIME Absolute time in cycle: %f' % dec) self.distance_from_chosen_steps = min([abs(dec - d) for d in self.chosen_steps]) # TODO use self.close_to_chosen_steps info('GS_UPDATE_TIME Distance from chosen steps: %f' % self.distance_from_chosen_steps) info('GS_UPDATE_TIME Distance threshold: %f' % (0.5 * self.metadata['dt'] + DOLFIN_EPS)) if self.doSave: if self.args.ST == 'min': self.save_this_step = (i >= 1 and (self.distance_from_chosen_steps < 0.5 * self.metadata['dt'] + DOLFIN_EPS)) # QQ might skip step? change 0.5 to 0.51? elif self.args.ST == 'peak': self.save_this_step = (i >= 1 and 0.1 < dec < 0.20001) # TODO relative to cycle_length elif self.save_nth == 1 or i >= 1 or self.step_number % self.save_nth == 0: self.save_this_step = True else: self.save_this_step = False if self.save_this_step: info('GS_UPDATE_TIME Chose to save this step: (%f, %d)' % (actual_time, step_number))
def get_initial_conditions(self, function_list): """ :param function_list: [{'type': 'v'/'p', 'time':-0.1},...] :return: velocities and pressures in selected times """ info('get_initial_conditions for this problem was not properly implemented.')
def get_boundary_conditions(self, use_pressure_BC, v_space, p_space): info('get_boundary_conditions() for this problem was not properly implemented.')
res.set("Exact", "iter_max", 0) res.set("SLEPc", "eigenvalue", lam_slepc) res.set("SLEPc", "residual", res_slepc) res.set("SLEPc", "time_min", t_slepc_min) res.set("SLEPc", "time_max", t_slepc_max) res.set("SLEPc", "iter_min", it_slepc_min) res.set("SLEPc", "iter_max", it_slepc_max) res.set("EVC", "eigenvalue", lam_evc) res.set("EVC", "residual", res_evc) res.set("EVC", "time_min", t_evc_min) res.set("EVC", "time_max", t_evc_max) res.set("EVC", "iter_min", it_evc_min) res.set("EVC", "iter_max", it_evc_max) info(res) print print "----------------------------------------------------" print " PROBLEM SPECIFICATION TIMINGS" print "____________________________________________________" print print problem_spec_timings print "----------------------------------------------------" print " SOLVER INTERNAL TIMINGS" print "____________________________________________________" print print solver_int_timings
def report(self, report_file, str_name): """ Writes out complete report. Saves times for the selected watches to csv file (if given). """ total_time = toc() info('Total time of %.0f s, (%.2f hours).' % (total_time, total_time/3600.0)) sorted_by_time = [] sorted_by_name = [] sum = 0 sum_percent = 0 # sort watches by time measured for value in self.watches.itervalues(): if value[3]: sum += value[0] if value[4]: sum_percent += value[0] if not sorted_by_time: sorted_by_time.append(value) else: i = 0 l = len(sorted_by_time) while i < l and value[0]<sorted_by_time[i][0]: i += 1 sorted_by_time.insert(i, value) for value in sorted_by_time: if value[0] > 0.000001: if value[4]: info(' %-40s: %12.2f s %5.1f %% (%4.1f %%)' % (value[2], value[0], 100.0*value[0]/sum_percent, 100.0*value[0]/total_time)) else: info(' %-40s: %12.2f s (%4.1f %%)' % (value[2], value[0], 100.0*value[0]/total_time)) else: info(' %-40s: %12.2f s NOT USED' % (value[2], value[0])) info(' %-40s: %12.2f s (%4.1f %%)' % ('Measured', sum, 100.0*sum/total_time)) info(' %-40s: %12.2f s 100.0 %% (%4.1f %%)' % ('Base for percent values', sum_percent, 100.0*sum_percent/total_time)) info(' %-40s: %12.2f s (%4.1f %%)' % ('Unmeasured', total_time-sum, 100.0*(total_time-sum)/total_time)) # report to file report_header = ['Name', 'Total time'] report_data = [str_name, total_time] for key in sorted(self.watches.keys()): value = self.watches[key] if value[4]: report_header.append(value[2]) report_data.append(value[0]) if report_file is not None: writer = csv.writer(report_file, delimiter=';', quotechar='|', quoting=csv.QUOTE_NONE) writer.writerow(report_header) writer.writerow(report_data)
def remove_status_file(self): if self.MPI_rank == 0: try: os.remove(self.metadata['name'] + ".run") except OSError: info('.run file probably not created')
def __init__(self, args, tc, metadata): self.MPI_rank = MPI.rank(mpi_comm_world()) self.metadata = metadata # need to be specified in subclass init before calling this init self.problem_code = self.problem_code self.metadata['pcode'] = self.problem_code self.has_analytic_solution = self.has_analytic_solution self.metadata['hasAnalyticSolution'] = self.has_analytic_solution self.args = args # Time control self.tc = tc self.tc.init_watch('mesh', 'Imported mesh', True) self.tc.init_watch('saveP', 'Saved pressure', True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.init_watch('averageP', 'Averaged pressure', True) self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True) self.tc.init_watch('div', 'Computed and saved divergence', True) self.tc.init_watch('divNorm', 'Computed norm of divergence', True) self.tc.init_watch('WSSinit', 'Initialized mesh for WSS', False) self.tc.init_watch('WSS', 'Computed and saved WSS', True) self.tc.init_watch('errorV', 'Computed velocity error', True) self.tc.init_watch('errorVtest', 'Computed velocity error test', True) # If it is sensible (and implemented) to force pressure gradient on outflow boundary # 1. set self.outflow_area in initialize # 2. implement self.compute_outflow and get_outflow_measures self.can_force_outflow = False self.outflow_area = None self.outflow_measures = [] # stopping criteria (for relative H1 velocity error norm) (if known) self.divergence_treshold = 10 # used for writing status files .run to monitor progress during computation: self.last_status_functional = 0.0 self.status_functional_str = 'to be defined in Problem class' # mesh and function objects self.normal = None self.mesh = None self.facet_function = None self.mesh_volume = 0 # must be specified in subclass (needed to compute pressure average) self.stepsInCycle = None self.vSpace = None self.vFunction = None self.divSpace = None self.divFunction = None self.pSpace = None # self.pgSpace = None # NT computing pressure gradient function not used (commented throughout code) self.pFunction = None # self.pgFunction = None self.solutionSpace = None self.solution = None # time related variables self.actual_time = 0.0 self.cycle_length = 1.0 self.step_number = 0 self.chosen_steps = [0.1, 0.125, 0.15, 0.16, 0.165, 0.166, 0.167, 0.17, 0.188, 0.189, 0.2] # must be specified in subclass # 0.166... is peak for real problem, 0.188 is peak for womersley profile # TODO chosen_steps should depend on problem (and inflow profile) self.distance_from_chosen_steps = 0.0 self.save_this_step = False self.isWholeSecond = None self.N1 = None self.N0 = None # lists used for storing normalization and scaling coefficients # (time independent, mainly used in womersley_cylinder) self.vel_normalization_factor = [] self.pg_normalization_factor = [] self.p_normalization_factor = [] self.scale_factor = [] self.analytic_v_norm_L2 = None self.analytic_v_norm_H1 = None self.analytic_v_norm_H1w = None self.last_error = None # for WSS generation metadata['hasWSS'] = (args.wss != 'none') metadata['WSSmethod'] = self.args.wss_method self.T = None self.Tb = None self.wall_mesh = None self.wall_mesh_oriented = None self.Vb = None self.Sb = None self.VDG = None self.R = None self.SDG = None self.nb = None # dictionaries for output XDMF files self.fileDict = {'u': {'name': 'velocity'}, 'p': {'name': 'pressure'}, # 'pg': {'name': 'pressure_grad'}, 'd': {'name': 'divergence'}} self.fileDictTent = {'u2': {'name': 'velocity_tent'}, 'd2': {'name': 'divergence_tent'}} self.fileDictDiff = {'uD': {'name': 'velocity_diff'}, 'pD': {'name': 'pressure_diff'}, 'pgD': {'name': 'pressure_grad_diff'}} self.fileDictTentDiff = {'u2D': {'name': 'velocity_tent_diff'}} self.fileDictTentP = {'p2': {'name': 'pressure_tent'}} # 'pg2': {'name': 'pressure_grad_tent'}} self.fileDictTentPDiff = {'p2D': {'name': 'pressure_tent_diff'}} # 'pg2D': {'name': 'pressure_grad_tent_diff'}} self.fileDictWSS = {'wss': {'name': 'wss'}, } self.fileDictWSSnorm = {'wss_norm': {'name': 'wss_norm'}, } self.fileDictDebugRot = {'grad_cor': {'name': 'grad_cor'}, } # lists of functionals and other scalar output data self.time_list = [] # list of times, when error is measured (used in report) self.second_list = [] self.listDict = {} # list of fuctionals # dictionary of data lists {list, name, abbreviation, add scaled row to report} # normalisation coefficients (time-independent) are added to some lists to be used in normalized data series # coefficients are lists (updated during initialisation, so we cannot use float type) # coefs are equal to average of respective value of analytic solution # norm lists (time-dependent normalisation coefficients) are added to some lists to be used in relative data # series (to remove natural pulsation of error due to change in volume flow rate) # slist - lists for cycle-averaged values # L2(0) means L2 difference of pressures taken with zero average self.listDict = { 'd': {'list': [], 'name': 'corrected velocity L2 divergence', 'abrev': 'DC', 'scale': self.scale_factor, 'slist': []}, 'd2': {'list': [], 'name': 'tentative velocity L2 divergence', 'abrev': 'DT', 'scale': self.scale_factor, 'slist': []}, 'pg': {'list': [], 'name': 'computed pressure gradient', 'abrev': 'PG', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor}, 'pg2': {'list': [], 'name': 'computed pressure tent gradient', 'abrev': 'PTG', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor}, } if self.has_analytic_solution: self.listDict.update({ 'u_L2': {'list': [], 'name': 'corrected velocity L2 error', 'abrev': 'CE_L2', 'scale': self.scale_factor, 'relative': 'av_norm_L2', 'slist': [], 'norm': self.vel_normalization_factor}, 'u2L2': {'list': [], 'name': 'tentative velocity L2 error', 'abrev': 'TE_L2', 'scale': self.scale_factor, 'relative': 'av_norm_L2', 'slist': [], 'norm': self.vel_normalization_factor}, 'u_L2test': {'list': [], 'name': 'test corrected L2 velocity error', 'abrev': 'TestCE_L2', 'scale': self.scale_factor}, 'u2L2test': {'list': [], 'name': 'test tentative L2 velocity error', 'abrev': 'TestTE_L2', 'scale': self.scale_factor}, 'u_H1': {'list': [], 'name': 'corrected velocity H1 error', 'abrev': 'CE_H1', 'scale': self.scale_factor, 'relative': 'av_norm_H1', 'slist': []}, 'u2H1': {'list': [], 'name': 'tentative velocity H1 error', 'abrev': 'TE_H1', 'scale': self.scale_factor, 'relative': 'av_norm_H1', 'slist': []}, 'u_H1test': {'list': [], 'name': 'test corrected H1 velocity error', 'abrev': 'TestCE_H1', 'scale': self.scale_factor}, 'u2H1test': {'list': [], 'name': 'test tentative H1 velocity error', 'abrev': 'TestTE_H1', 'scale': self.scale_factor}, 'apg': {'list': [], 'name': 'analytic pressure gradient', 'abrev': 'APG', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor}, 'av_norm_L2': {'list': [], 'name': 'analytic velocity L2 norm', 'abrev': 'AVN_L2'}, 'av_norm_H1': {'list': [], 'name': 'analytic velocity H1 norm', 'abrev': 'AVN_H1'}, 'ap_norm': {'list': [], 'name': 'analytic pressure norm', 'abrev': 'APN'}, 'p': {'list': [], 'name': 'pressure L2(0) error', 'abrev': 'PE', 'scale': self.scale_factor, 'slist': [], 'norm': self.p_normalization_factor}, 'pgE': {'list': [], 'name': 'computed pressure gradient error', 'abrev': 'PGE', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor, 'slist': []}, 'pgEA': {'list': [], 'name': 'computed absolute pressure gradient error', 'abrev': 'PGEA', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor}, 'p2': {'list': [], 'name': 'pressure tent L2(0) error', 'abrev': 'PTE', 'scale': self.scale_factor, 'slist': [], 'norm': self.p_normalization_factor}, 'pgE2': {'list': [], 'name': 'computed tent pressure tent gradient error', 'abrev': 'PTGE', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor, 'slist': []}, 'pgEA2': {'list': [], 'name': 'computed absolute pressure tent gradient error', 'abrev': 'PTGEA', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor} }) # parse arguments self.nu = 0.0 # nu should be specified in subclass (fixed or by argument) self.onset = args.onset self.onset_factor = 0 # saving options: self.doSave = False self.saveOnlyVel = False self.doSaveDiff = False self.save_nth = args.saventh option = args.save if option == 'doSave' or option == 'diff' or option == 'only_vel': self.doSave = True if option == 'diff': self.doSaveDiff = True info('Saving velocity differences.') if option == 'only_vel': self.saveOnlyVel = True info('Saving only velocity profiles.') info('Saving solution ON.') elif option == 'noSave': self.doSave = False info('Saving solution OFF.') # error control options: self.doErrControl = None self.testErrControl = False if not self.has_analytic_solution: self.doErrControl = False elif args.error == "noEC": self.doErrControl = False info("Error control OFF") else: self.doErrControl = True if args.error == "test": self.testErrControl = True info("Error control in testing mode") else: info("Error control ON") # set directory for results and reports self.str_dir_name = "%s_%s_results" % (self.problem_code, metadata['name']) self.metadata['dir'] = self.str_dir_name # create directory, needed because of using "with open(..." construction later if not os.path.exists(self.str_dir_name) and self.MPI_rank == 0: os.mkdir(self.str_dir_name)
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) # TODO check proper use of watches self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space if self.bc == 'lagrange': L = FunctionSpace(mesh, "R", 0) QL = self.Q*L problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) if self.bc == 'lagrange': (pQL, rQL) = TrialFunction(QL) (qQL, lQL) = TestFunction(QL) else: p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt}, {'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) if doSave: problem.save_vel(False, u0, 0.0) problem.save_vel(True, u0, 0.0) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity if self.bc == 'lagrange': p_QL = Function(QL) # current pressure or pressure help function from rotation scheme pQ = Function(self.Q) # auxiliary function for conversion between QL.sub(0) and Q else: p_ = Function(self.Q) # current pressure or pressure help function from rotation scheme p_mod = Function(self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5*u0 - 0.5*u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) # CBC delta: if self.cbcDelta: delta = Constant(self.stabCoef)*h/(sqrt(inner(u_ext, u_ext))+h) else: delta = Constant(self.stabCoef)*h**2/(2*nu*k + k*h*inner(u_ext, u_ext)+h**2) if self.use_full_SUPG: v1 = v + delta*0.5*k*dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v def nonlinearity(function): if self.use_ema: return 2*inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function)*u_ext, v1) * dx # return 2*inner(dot(sym(grad(function)), u_ext), v) * dx + inner(div(u_ext)*function, v) * dx # QQ implement this way? else: return inner(dot(grad(function), u_ext), v1) * dx def diffusion(fce): if self.useLaplace: return nu*inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form if self.bcv == 'LAP': return form - inner(nu*dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form() if self.bcv == 'DDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form # additional term must be added to non-constant part def pressure_rhs(): if self.useLaplace or self.bcv == 'LAP': return inner(p0, div(v1)) * dx - inner(p0*n, v1) * problem.get_outflow_measure_form() # NT term inner(inner(p, n), v) is 0 when p=0 on outflow else: return inner(p0, div(v1)) * dx a1_const = (1./k)*inner(u, v1)*dx + diffusion(0.5*u) a1_change = nonlinearity(0.5*u) if self.bcv == 'DDN': # IMP Problem: Does not penalize influx for current step, only for the next one # IMP this can lead to oscilation: DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5*min_value(Constant(0.), inner(u_ext, n))*inner(u, v1)*problem.get_outflow_measure_form() # IMP works only with uflacs compiler L1 = (1./k)*inner(u0, v1)*dx - nonlinearity(0.5*u0) - diffusion(0.5*u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5*min_value(0., inner(u_ext, n))*inner(u0, v1)*problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5*delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) # NT optional: use Crank Nicolson in stabilisation term: change RHS # L1 += -0.5*delta*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F2 = inner(grad(pQL), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: F2 = inner(grad(p), grad(q))*dx + (1./k)*q*div(u_)*dx else: # Projection, solve to p_ if self.bc == 'lagrange': F2 = inner(grad(pQL - p0), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx for m in problem.get_outflow_measures(): F2 += (1./k)*(1./outflow_area)*need_outflow*q*m else: F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0)), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_), v)*dx else: if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0) - p0), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_ - p0), v)*dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure if self.bc == 'lagrange': pr = TrialFunction(self.Q) qr = TestFunction(self.Q) F4 = (pr - p0 - p_QL.sub(0) + nu*div(u_))*qr*dx else: F4 = (p - p0 - p_ + nu*div(u_))*q*dx # TODO zkusit, jestli to nebude rychlejsi? nepocitat soustavu, ale p.assign(...), nutno project(div(u),Q) coz je pocitani podobne soustavy # TODO zkusit v project zadat solver_type='lu' >> primy resic by mel byt efektivnejsi a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble(a1_const) # need to be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('umfpack') if self.useRotationScheme: self.solver_rot = LUSolver('umfpack') else: # NT not needed, chosen not to use hypre_parasails # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = KrylovSolver('gmres', self.prec_v) # nonsymetric > gmres # IMP cannot use 'ilu' in parallel (choose different default option) self.solver_vel_cor = KrylovSolver('cg', 'hypre_amg') # nonsymetric > gmres self.solver_p = KrylovSolver('cg', self.prec_p) # symmetric > CG if self.useRotationScheme: self.solver_rot = KrylovSolver('cg', self.prec_p) solver_options = {'monitor_convergence': True, 'maximum_iterations': 1000, 'nonzero_initial_guess': True} # 'nonzero_initial_guess': True with solver.solbe(A, u, b) means that # Solver will use anything stored in u as an initial guess # Get the nullspace if there are no pressure boundary conditions foo = Function(self.Q) # auxiliary vector for setting pressure nullspace if self.bc in ['nullspace', 'nullspace_s']: null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0/null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) if self.bc == 'nullspace': as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.precision_rel_v_tent) self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.precision_abs_v_tent) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_p.parameters['absolute_tolerance'] = 10E-10 if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.solvers == 'krylov': for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_p, self.solver_rot] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 solver.parameters['preconditioner']['structure'] = 'same' # matrices A2-A4 do not change, so we can reuse preconditioners self.solver_vel_tent.parameters['preconditioner']['structure'] = 'same_nonzero_pattern' # matrix A1 changes every time step, so change of preconditioner must be allowed if self.bc == 'lagrange': fa = FunctionAssigner(self.Q, QL.sub(0)) # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt/2.0): info("t = %f" % t) self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # DDN debug # u_ext_in = assemble(inner(u_ext, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_ext, n))*problem.get_outflow_measure_form()) # print('DDN: u_ext*n dSout = ', u_ext_in) # print('DDN: negative part of u_ext*n dSout = ', DDN_triggered) # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble(a1_change, tensor=A1_change) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble(a1_stab, tensor=A1_stab) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() # DDN debug # u_ext_in = assemble(inner(u_, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_, n))*problem.get_outflow_measure_form()) # print('DDN: u_tent*n dSout = ', u_ext_in) # print('DDN: negative part of u_tent*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow-out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.bc in ['nullspace', 'nullspace_s']: self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') if self.bc == 'lagrange': self.solver_p.solve(A2, p_QL.vector(), b) else: self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) foo.assign(pQ + p0) else: foo.assign(p_+p0) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(True, foo) else: if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) problem.averaging_pressure(pQ) if save_this_step and not onlyVel: problem.save_pressure(False, pQ) else: # we do not want to change p=0 on outflow, it conflicts with do-nothing conditions foo = Function(self.Q) foo.assign(p_) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() # DDN debug # u_ext_in = assemble(inner(u_cor, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_cor, n))*problem.get_outflow_measure_form()) # print('DDN: u_cor*n dSout = ', u_ext_in) # print('DDN: negative part of u_cor*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 problem.averaging_pressure(p_mod) if save_this_step and not onlyVel: problem.save_pressure(False, p_mod) end() # compute functionals (e. g. forces) problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else (pQ if self.bc == 'lagrange' else p_), t) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign(u_cor) # use corretced velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: if self.bc == 'lagrange': p0.assign(pQ) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0
def __init__(self, args, tc, metadata): gs.GeneralSolver.__init__(self, args, tc, metadata) self.metadata['hasTentativeV'] = True self.solver_vel_tent = None self.solver_vel_cor = None self.solver_p = None self.solver_rot = None self.null_space = None # input parameters self.bc = args.bc self.forceOutflow = args.fo self.useLaplace = args.laplace self.use_full_SUPG = args.cs self.bcv = 'NOT' if self.useLaplace else args.bcv if self.bcv == 'CDN': info('Using classical do nothing condition (Tn=0).') if self.bcv == 'DDN': info('Using directional do nothing condition (Tn=0.5*negative(u.n)u).') if self.bcv == 'LAP': info('Using laplace neutral condition (grad(u)n=0).') self.stabCoef = args.stab self.stabilize = (args.stab > DOLFIN_EPS) if self.stabilize: if self.use_full_SUPG: info('Used consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('Used non-consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('No stabilization used.') self.solvers = args.solvers self.useRotationScheme = args.r self.metadata['hasTentativeP'] = self.useRotationScheme self.B = args.B self.use_ema = args.ema self.cbcDelta = args.cbcDelta self.prec_v = args.precV self.prec_p = args.precP self.precision_rel_v_tent = args.prv1 self.precision_abs_v_tent = args.pav1 self.precision_p = args.pp
def solve_elasticity(facet_function, E, nu, dt, T_end, output_dir): """Solves elasticity problem with Young modulus E, Poisson ration nu, timestep dt, until T_end and with output data going to output_dir. Geometry is defined by facet_function which also defines rest boundary by marker 1 and traction boundary by marker 2.""" # Get mesh and prepare boundary measure mesh = facet_function.mesh() gdim = mesh.geometry().dim() ds = Measure("ds", subdomain_data=facet_function) # Build function space U = VectorFunctionSpace(mesh, "Lagrange", 1) P = FunctionSpace(mesh, "Lagrange", 1) W = MixedFunctionSpace([U, U, P]) from dolfin.cpp.common import info info("Num DOFs %d" % W.dim()) # Prepare BCs bcs = [ DirichletBC(W.sub(i), gdim * (0.0, ), facet_function, 1) for i in [0, 1] ] # Define constitutive law def stress(u, p): """Returns 1st Piola-Kirchhoff stress and (local) mass balance for given u, p.""" mu = Constant(E / (2.0 * (1.0 + nu))) F = I + grad(u) J = det(F) B = F * F.T T = -p * I + mu * (B - I) # Cauchy stress S = J * T * inv(F).T # 1st Piola-Kirchhoff stress if nu == 0.5: # Incompressible pp = J - 1.0 else: # Compressible lmbd = Constant(E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu))) pp = 1.0 / lmbd * p + (J * J - 1.0) return S, pp # Timestepping theta-method parameters q = Constant(0.5) dt = Constant(dt) # Unknowns, values at previous step and test functions w = Function(W) (u, v, p) = split(w) w0 = Function(W) (u0, v0, p0) = split(w0) (_u, _v, _p) = TestFunctions(W) I = Identity(W.mesh().geometry().dim()) # Balance of momentum S, pp = stress(u, p) S0, pp0 = stress(u0, p0) F1 = (1.0/dt)*inner(u-u0, _u)*dx \ - ( q*inner(v, _u)*dx + (1.0-q)*inner(v0, _u)*dx ) F2a = inner(S, grad(_v)) * dx + pp * _p * dx F2b = inner(S0, grad(_v)) * dx + pp0 * _p * dx F2 = (1.0 / dt) * inner(v - v0, _v) * dx + q * F2a + (1.0 - q) * F2b # Traction at boundary F = I + grad(u) bF_magnitude = Constant(0.0) bF_direction = { 2: Constant((0.0, 1.0)), 3: Constant((0.0, 0.0, 1.0)) }[gdim] bF = det(F) * dot(inv(F).T, bF_magnitude * bF_direction) FF = inner(bF, _v) * ds(2) # Whole system and its Jacobian F = F1 + F2 + FF J = derivative(F, w) # Initialize solver problem = NonlinearVariationalProblem(F, w, bcs=bcs, J=J) solver = NonlinearVariationalSolver(problem) solver.parameters['newton_solver']['relative_tolerance'] = 1e-6 solver.parameters['newton_solver']['linear_solver'] = 'mumps' # Extract solution components (u, v, p) = w.split() u.rename("u", "displacement") v.rename("v", "velocity") p.rename("p", "pressure") # Create files for storing solution vfile = File("%s/velo.xdmf" % output_dir) ufile = File("%s/disp.xdmf" % output_dir) pfile = File("%s/pres.xdmf" % output_dir) # Prepare plot window plt = plot(u, mode="displacement", interactive=False, wireframe=True) # Time-stepping loop t = 0.0 while t <= T_end: print "Time: %g" % t t += float(dt) # Increase traction bF_magnitude.assign(100.0 * t) # Prepare to solve and solve w0.assign(w) solver.solve() # Store solution to files and plot ufile << (u, t) vfile << (v, t) pfile << (p, t) plt.plot(u)
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('solve', 'Running nonlinear solver', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') mesh = self.problem.mesh # Define function spaces (P2-P1) self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.W = MixedFunctionSpace([self.V, self.Q]) self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space # to assign solution in space W.sub(0) to Function(V) we need FunctionAssigner (cannot be assigned directly) fa = FunctionAssigner(self.V, self.W.sub(0)) velSp = Function(self.V) problem.initialize(self.V, self.Q, self.PS, self.D) # Define unknown and test function(s) NS v, q = TestFunctions(self.W) w = Function(self.W) dw = TrialFunction(self.W) u, p = split(w) # Define fields n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) theta = 0.5 # Crank-Nicholson k = Constant(self.metadata['dt']) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) if doSave: problem.save_vel(False, u0, 0.0) # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.W.sub(0), self.W.sub(1)) # NT bcp is not used # Define steady part of the equation def T(u): return -p * I + 2.0 * nu * sym(grad(u)) def F(u, v, q): return (inner(T(u), grad(v)) - q * div(u)) * dx + inner(grad(u) * u, v) * dx # Define variational forms F_ns = (inner((u - u0), v) / k) * dx + (1.0 - theta) * F(u0, v, q) + theta * F(u, v, q) J_ns = derivative(F_ns, w, dw) # J_ns = derivative(F_ns, w) # did not work # NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns, form_compiler_parameters=ffc_options) NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns) # (var. formulation, unknown, Dir. BC, jacobian, optional) NS_solver = NonlinearVariationalSolver(NS_problem) prm = NS_solver.parameters prm['newton_solver']['absolute_tolerance'] = 1E-08 prm['newton_solver']['relative_tolerance'] = 1E-08 # prm['newton_solver']['maximum_iterations'] = 45 # prm['newton_solver']['relaxation_parameter'] = 1.0 prm['newton_solver']['linear_solver'] = 'mumps' info(NS_solver.parameters, True) self.tc.end('init') # Time-stepping info("Running of direct method") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt/2.0): info("t = %f" % t) self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # Compute begin("Solving NS ....") try: self.tc.start('solve') NS_solver.solve() self.tc.end('solve') except RuntimeError as inst: problem.report_fail(t) return 1 end() # Extract solutions: (u, p) = w.split() fa.assign(velSp, u) # we are assigning twice (now and inside save_vel), but it works with one method save_vel for direct and # projection (we could split save_vel to save one assign) if save_this_step: self.tc.start('saveVel') problem.save_vel(False, velSp, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u) problem.compute_err(False, u, t) problem.compute_div(False, u) # foo = Function(self.Q) # foo.assign(p) # problem.averaging_pressure(foo) # if save_this_step and not onlyVel: # problem.save_pressure(False, foo) if save_this_step and not onlyVel: problem.save_pressure(False, p) # compute functionals (e. g. forces) problem.compute_functionals(u, p, t) # Move to next time step self.tc.start('next') u0.assign(velSp) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: direct method") problem.report() return 0
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace( mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(find_geometric_dimension(u)) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{ 'type': 'v', 'time': -dt }, { 'type': 'v', 'time': 0.0 }, { 'type': 'p', 'time': 0.0 }]) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity p_ = Function( self.Q ) # current pressure or pressure help function from rotation scheme p_mod = Function( self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5 * u0 - 0.5 * u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) if self.args.cbc_tau: # used in Simula cbcflow project tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h) else: # proposed in R. Codina: On stabilized finite element methods for linear systems of # convection-diffusion-reaction equations. tau = Constant(self.stabCoef) * k * h**2 / ( 2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) + h**2) # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0 if self.use_full_SUPG: v1 = v + tau * 0.5 * dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v def nonlinearity(function): if self.args.ema: return 2 * inner(dot(sym(grad(function)), u_ext), v1 ) * dx + inner(div(function) * u_ext, v1) * dx else: return inner(dot(grad(function), u_ext), v1) * dx def diffusion(fce): if self.useLaplace: return nu * inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': return form if self.bcv == 'LAP': return form - inner(nu * dot(grad(fce).T, n), v1 ) * problem.get_outflow_measure_form() if self.bcv == 'DDN': return form # additional term must be added to non-constant part def pressure_rhs(): if self.args.bc == 'outflow': return inner(p0, div(v1)) * dx else: return inner(p0, div(v1)) * dx - inner( p0 * n, v1) * problem.get_outflow_measure_form() a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u) a1_change = nonlinearity(0.5 * u) if self.bcv == 'DDN': # does not penalize influx for current step, only for the next one # this can lead to oscilation: # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5 * min_value(Constant(0.), inner( u_ext, n)) * inner(u, v1) * problem.get_outflow_measure_form() # NT works only with uflacs compiler L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity( 0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner( u0, v1) * problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot( grad(v), u_ext)) * dx(None, {'quadrature_degree': 6}) # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed: # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx else: # Projection, solve to p_ if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx for m in problem.get_outflow_measures(): F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m else: F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx else: F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0), v) * dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure F4 = (p - p0 - p_ + nu * div(u_)) * q * dx a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble( a1_const ) # must be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy( ) # copy to get matrix with same sparse structure (data will be overwritten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy( ) # copy to get matrix with same sparse structure (data will be overwritten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('mumps') if self.useRotationScheme: self.solver_rot = LUSolver('mumps') else: # NT 2016-1 KrylovSolver >> PETScKrylovSolver # not needed, chosen not to use hypre_parasails: # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = PETScKrylovSolver( 'gmres', self.args.precV) # nonsymetric > gmres # cannot use 'ilu' in parallel self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC) self.solver_p = PETScKrylovSolver( self.args.solP, self.args.precP) # almost (up to BC) symmetric > CG if self.useRotationScheme: self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg') # setup Krylov solvers if self.solvers == 'krylov': # Get the nullspace if there are no pressure boundary conditions foo = Function( self.Q) # auxiliary vector for setting pressure nullspace if self.args.bc == 'nullspace': null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers solver_options = { 'monitor_convergence': True, 'maximum_iterations': 10000, 'nonzero_initial_guess': True } # 'nonzero_initial_guess': True with solver.solve(A, u, b) means that # Solver will use anything stored in u as an initial guess for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 if self.args.solP == 'richardson': self.solver_p.parameters['monitor_convergence'] = False self.solver_vel_tent.parameters['relative_tolerance'] = 10**( -self.args.prv1) self.solver_vel_tent.parameters['absolute_tolerance'] = 10**( -self.args.pav1) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10**( -self.args.prp) self.solver_p.parameters['absolute_tolerance'] = 10**( -self.args.pap) if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10E-10 self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.args.Vrestart > 0: self.solver_vel_tent.parameters['gmres'][ 'restart'] = self.args.Vrestart if self.args.solP == 'gmres' and self.args.Prestart > 0: self.solver_p.parameters['gmres'][ 'restart'] = self.args.Prestart # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 # debug function if problem.args.debug_rot: plot_cor_v = Function(self.V) while t < (ttime + dt / 2.0): self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble( a1_change, tensor=A1_change ) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble( a1_stab, tensor=A1_stab ) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow - out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.args.bc == 'nullspace': self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) foo.assign(p_ + p0) if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(True, foo) else: foo = Function(self.Q) foo.assign(p_) # we do not want to change p_ by averaging if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.args.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step and not onlyVel: problem.averaging_pressure(p_mod) problem.save_pressure(False, p_mod) end() if problem.args.debug_rot: # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step) # see comment next to argument definition plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V)) problem.fileDict['grad_cor']['file'].write(plot_cor_v, t) # compute functionals (e. g. forces) problem.compute_functionals( u_cor, p_mod if self.useRotationScheme else p_, t, step) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign( u_cor) # use corrected velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0
def get_outflow_measures(self): """ return list of Measure objects for outflow boundary parts must be overridden in Problem before use (if needed) """ info('Integration over outflow for this problem was not properly implemented.')
def report(self): total = toc() md = self.metadata # compare errors measured by assemble and errornorm # TODO implement generally (if listDict[fcional]['testable']) # if self.testErrControl: # for e in [[self.listDict['u_L2']['list'], self.listDict['u_L2test']['list'], 'L2'], # [self.listDict['u_H1']['list'], self.listDict['u_H1test']['list'], 'H1']]: # print('test ', e[2], sum([abs(e[0][i]-e[1][i]) for i in range(len(self.time_list))])) # report error norms, norms of divergence etc. for individual time steps with open(self.str_dir_name + "/report_time_lines.csv", 'w') as reportFile: report_writer = csv.writer(reportFile, delimiter=';', escapechar='\\', quoting=csv.QUOTE_NONE) # report_writer.writerow(self.problem.get_metadata_to_save()) report_writer.writerow(["name", "what", "time"] + self.time_list) keys = sorted(self.listDict.keys()) for key in keys: l = self.listDict[key] if l['list']: abrev = l['abrev'] report_writer.writerow([md['name'], l['name'], abrev] + l['list']) if 'scale' in l: temp_list = [i/l['scale'][0] for i in l['list']] report_writer.writerow([md['name'], "scaled " + l['name'], abrev+"s"] + temp_list + ['scale factor:' + str(l['scale'])]) if 'norm' in l: if l['norm']: temp_list = [i/l['norm'][0] for i in l['list']] report_writer.writerow([md['name'], "normalized " + l['name'], abrev+"n"] + temp_list) else: info('Norm missing:' + str(l)) l['normalized_list_sec'] = [] if 'relative' in l: norm_list = self.listDict[l['relative']]['list'] temp_list = [l['list'][i]/norm_list[i] for i in range(0, len(l['list']))] self.listDict[key]['relative_list'] = temp_list report_writer.writerow([md['name'], "relative " + l['name'], abrev+"r"] + temp_list) # report error norms, norms of divergence etc. averaged over cycles # IFNEED rewrite to averaging over cycles (must track number of steps in cycle, it can be different for each cycle) # NT for cases when cycle_length % dt != 0 will be inacurate (less with smaller time steps) # note: self.stepsInCycle is float # if self.second_list: # with open(self.str_dir_name + "/report_seconds.csv", 'w') as reportFile: # report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE) # report_writer.writerow(["name", "what", "time"] + self.second_list) # keys = sorted(self.listDict.keys()) # for key in keys: # l = self.listDict[key] # if 'slist' in l and l['list']: # abrev = l['abrev'] # values = l['slist'] # # generate averages over seconds from list # for sec in self.second_list: # N0 = (sec-1)*self.stepsInCycle # N1 = sec*self.stepsInCycle # values.append(sqrt(sum([i*i for i in l['list'][N0:N1]]) / float(self.stepsInCycle))) # # report_writer.writerow([md['name'], l['name'], abrev] + values) # if 'scale' in l: # temp_list = [i/l['scale'][0] for i in values] # report_writer.writerow([md['name'], "scaled " + l['name'], abrev+"s"] + temp_list) # if 'norm' in l: # if l['norm']: # temp_list = [i/l['norm'][0] for i in values] # l['normalized_list_sec'] = temp_list # report_writer.writerow([md['name'], "normalized " + l['name'], abrev+"n"] + temp_list) # else: # info('Norm missing:' + str(l)) # l['normalized_list_sec'] = [] # if 'relative_list' in l: # temp_list = [] # for sec in self.second_list: # N0 = (sec-1)*self.stepsInCycle # N1 = sec*self.stepsInCycle # temp_list.append(sqrt(sum([i*i for i in l['relative_list'][N0:N1]]) / float(self.stepsInCycle))) # l['relative_list_sec'] = temp_list # report_writer.writerow([md['name'], "relative " + l['name'], abrev+"r"] + temp_list) header_row = ["name", "totalTimeHours"] data_row = [md['name'], total / 3600.0] for key in ['u_L2', 'u_H1', 'u_H1w', 'p', 'u2L2', 'u2H1', 'u2H1w', 'p2', 'pgE', 'pgE2', 'd', 'd2', 'force_wall']: if key in self.listDict: l = self.listDict[key] header_row += ['last_cycle_'+l['abrev']] data_row += [l['slist'][-1]] if l['slist'] else [0] if 'relative_list_sec' in l and l['relative_list_sec']: header_row += ['last_cycle_'+l['abrev']+'r'] data_row += [l['relative_list_sec'][-1]] elif key in ['p', 'p2']: header_row += ['last_cycle_'+l['abrev']+'n'] data_row += [l['normalized_list_sec'][-1]] if 'normalized_list_sec' in l \ and l['normalized_list_sec'] else [0] # save metadata. Can be loaded and used in postprocessing with open(self.str_dir_name + "/metadata", 'w') as reportFile: reportFile.write(self.get_metadata_to_save()) # report without header with open(self.str_dir_name + "/report.csv", 'w') as reportFile: report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE) report_writer.writerow(data_row) # report with header with open(self.str_dir_name + "/report_h.csv", 'w') as reportFile: report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE) report_writer.writerow(header_row) report_writer.writerow(data_row) # report time cotrol with open(self.str_dir_name + "/report_timecontrol.csv", 'w') as reportFile: self.tc.report(reportFile, self.metadata['name']) self.remove_status_file() # create file showing all was done well f = open(md['name'] + "_OK.report", "w") f.close()
def __init__(self, args, tc, metadata): gs.GeneralSolver.__init__(self, args, tc, metadata) self.metadata['hasTentativeV'] = True self.solver_vel_tent = None self.solver_vel_cor = None self.solver_p = None self.solver_rot = None self.null_space = None # input parameters self.forceOutflow = args.fo self.useLaplace = args.laplace self.use_full_SUPG = args.cs self.bcv = 'NOT' if self.useLaplace else args.bcv if self.bcv == 'CDN': info('Using classical do nothing condition (Tn=0).') if self.bcv == 'DDN': info( 'Using directional do nothing condition (Tn=0.5*negative(u.n)u).' ) if self.bcv == 'LAP': info('Using laplace neutral condition (grad(u)n=0).') self.stabCoef = args.stab self.stabilize = (args.stab > DOLFIN_EPS) if self.stabilize: if self.use_full_SUPG: info( 'Used consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info( 'Used non-consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('No stabilization used.') self.solvers = args.solvers self.useRotationScheme = args.r self.metadata['hasTentativeP'] = self.useRotationScheme
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('solve', 'Running nonlinear solver', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') mesh = self.problem.mesh # Define function spaces (P2-P1) self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.W = MixedFunctionSpace([self.V, self.Q]) self.PS = FunctionSpace( mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space # to assign solution in space W.sub(0) to Function(V) we need FunctionAssigner (cannot be assigned directly) fa = FunctionAssigner(self.V, self.W.sub(0)) velSp = Function(self.V) problem.initialize(self.V, self.Q, self.PS, self.D) # Define unknown and test function(s) NS v, q = TestFunctions(self.W) w = Function(self.W) dw = TrialFunction(self.W) u, p = split(w) # Define fields n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) theta = 0.5 # Crank-Nicholson k = Constant(self.metadata['dt']) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u0, p0] = self.problem.get_initial_conditions([{ 'type': 'v', 'time': 0.0 }, { 'type': 'p', 'time': 0.0 }]) if doSave: problem.save_vel(False, u0) # boundary conditions bcu, bcp = problem.get_boundary_conditions(False, self.W.sub(0), self.W.sub(1)) # Define steady part of the equation def T(u): return -p * I + 2.0 * nu * sym(grad(u)) def F(u, v, q): return (inner(T(u), grad(v)) - q * div(u)) * dx + inner( grad(u) * u, v) * dx # Define variational forms F_ns = (inner( (u - u0), v) / k) * dx + (1.0 - theta) * F(u0, v, q) + theta * F( u, v, q) J_ns = derivative(F_ns, w, dw) # J_ns = derivative(F_ns, w) # did not work # NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns, form_compiler_parameters=ffc_options) NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns) # (var. formulation, unknown, Dir. BC, jacobian, optional) NS_solver = NonlinearVariationalSolver(NS_problem) prm = NS_solver.parameters prm['newton_solver']['absolute_tolerance'] = 1E-08 prm['newton_solver']['relative_tolerance'] = 1E-08 # prm['newton_solver']['maximum_iterations'] = 45 # prm['newton_solver']['relaxation_parameter'] = 1.0 prm['newton_solver']['linear_solver'] = 'mumps' # prm['newton_solver']['lu_solver']['same_nonzero_pattern'] = True info(NS_solver.parameters, True) self.tc.end('init') # Time-stepping info("Running of direct method") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt / 2.0): self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # Compute begin("Solving NS ....") try: self.tc.start('solve') NS_solver.solve() self.tc.end('solve') except RuntimeError as inst: problem.report_fail(t) return 1 end() # Extract solutions: (u, p) = w.split() fa.assign(velSp, u) # we are assigning twice (now and inside save_vel), but it works with one method save_vel for direct and # projection (we could split save_vel to save one assign) if save_this_step: self.tc.start('saveVel') problem.save_vel(False, velSp) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u) problem.compute_err(False, u, t) problem.compute_div(False, u) # foo = Function(self.Q) # foo.assign(p) # problem.averaging_pressure(foo) # if save_this_step and not onlyVel: # problem.save_pressure(False, foo) if save_this_step and not onlyVel: problem.save_pressure(False, p) # compute functionals (e. g. forces) problem.compute_functionals(u, p, t, step) # Move to next time step self.tc.start('next') u0.assign(velSp) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: direct method") problem.report() return 0
# if args.solver == 'ipcs1': # PETScOptions.set('log_summary') # Paralell run initialization comm = mpi_comm_world() rank = MPI.rank(comm) # parameters["std_out_all_processes"] = False # print only rank==0 process output # parameters["ghost_mode"] = "shared_facet" # may be needed for operating DG elements in parallel # allows output using info() for the main process only if rank == 0 or args.out == 'all': set_log_level(INFO) else: set_log_level(INFO + 1) info('Running on %d processor(s).' % MPI.size(comm)) if MPI.size(comm) > 1 and args.problem == 'womersley_cylinder': info('Womersley cylinder problem is not runnable in parallel due to method of construction of analytic solution,' ' which is used to describe boundary conditions.') # the change of mesh format would be also needed exit() # dynamically import selected solver and problem files exec('from solvers.%s import Solver' % args.solver) exec('from problems.%s import Problem' % args.problem) # setup and parse problem- and solver-specific command-line arguments parser = argparse.ArgumentParser() Solver.setup_parser_options(parser) Problem.setup_parser_options(parser) args, remaining = parser.parse_known_args(remaining, args)
def report(self, report_file, str_name): total_time = toc() info('Total time of %.0f s, (%.2f hours).' % (total_time, total_time/3600.0)) sorted_by_time = [] sorted_by_name = [] sum = 0 sum_percent = 0 for value in self.watches.itervalues(): if value[3]: sum += value[0] if value[4]: sum_percent += value[0] if not sorted_by_time: sorted_by_time.append(value) else: i = 0 l = len(sorted_by_time) while i < l and value[0]<sorted_by_time[i][0]: i += 1 sorted_by_time.insert(i, value) for value in sorted_by_time: if value[0] > 0.000001: if value[4]: info(' %-40s: %12.2f s %5.1f %% (%4.1f %%)' % (value[2], value[0], 100.0*value[0]/sum_percent, 100.0*value[0]/total_time)) else: info(' %-40s: %12.2f s (%4.1f %%)' % (value[2], value[0], 100.0*value[0]/total_time)) else: info(' %-40s: %12.2f s NOT USED' % (value[2], value[0])) info(' %-40s: %12.2f s (%4.1f %%)' % ('Measured', sum, 100.0*sum/total_time)) info(' %-40s: %12.2f s 100.0 %% (%4.1f %%)' % ('Base for percent values', sum_percent, 100.0*sum_percent/total_time)) info(' %-40s: %12.2f s (%4.1f %%)' % ('Unmeasured', total_time-sum, 100.0*(total_time-sum)/total_time)) # report to file for key in self.watches.iterkeys(): # sort keys by name if not sorted_by_name: sorted_by_name.append(key) else: l = len(sorted_by_name) i = l while key < sorted_by_name[i-1] and i > 0: i -= 1 sorted_by_name.insert(i, key) report_header = ['Name', 'Total time'] report_data = [str_name, total_time] for key in sorted_by_name: value = self.watches[key] report_header.append(value[2]) report_header.append('part '+value[2]) report_data.append(value[0]) report_data.append(value[0]/total_time) report_header.append('part unmeasured') report_data.append((total_time-sum)/total_time) if report_file is not None: writer = csv.writer(report_file, delimiter=';', quotechar='|', quoting=csv.QUOTE_NONE) writer.writerow(report_header) writer.writerow(report_data)
def __init__(self, args, tc, metadata): self.has_analytic_solution = False self.problem_code = 'REAL' super(Problem, self).__init__(args, tc, metadata) self.name = 'test on real mesh' self.status_functional_str = 'outflow/inflow' self.last_inflow = 0 # time settings self.itp_lengths = { 1: 1.0, 2: 0.9375, } self.cycle_length = self.itp_lengths[self.args.itp] # input parameters self.nu = self.args.nu # kinematic viscosity self.factor = args.factor self.metadata['factor'] = self.factor self.scale_factor.append(self.factor) self.tc.start('mesh') # Import mesh try: self.mesh, self.facet_function = super(Problem, self).loadMesh(args.mesh) info("Mesh name: " + args.mesh + " " + str(self.mesh)) f_ini = open('meshes/' + args.mesh + '.ini', 'r') reader = csv.reader(f_ini, delimiter=' ', escapechar='\\') except (EnvironmentError, RuntimeError): print( 'Unable to open mesh.hdf5 or mesh.ini file. Check if the mesh was prepared to be used ' 'with \"real\" problem.') exit(1) # load inflows and outflows (interfaces) from mesh.ini file obj = None self.interfaces = [] for row in reader: if not row: pass elif row[0] == 'volume': self.mesh_volume = float(row[1]) elif row[0] == 'in': if obj is not None: self.interfaces.append(obj) obj = {'inflow': True, 'number': row[1]} elif row[0] == 'out': if obj is not None: self.interfaces.append(obj) obj = {'inflow': False, 'number': row[1]} else: if len(row) == 2: # scalar values obj[row[0]] = row[1] else: # vector values obj[row[0]] = [float(f) for f in row[1:]] self.interfaces.append(obj) f_ini.close() self.tc.end('mesh') # collect inflows and outflows into separate lists self.outflow_area = 0 self.inflows = [] self.outflows = [] for obj in self.interfaces: if not obj['inflow']: self.outflow_area += float(obj['S']) self.outflows.append(obj) else: self.inflows.append(obj) info('Outflow area: %f' % self.outflow_area) # self.dsWall = Measure("ds", subdomain_id=1, subdomain_data=self.facet_function) self.normal = FacetNormal(self.mesh) # generate measures, collect measure lists self.inflow_measures = [] for obj in self.interfaces: obj['measure'] = Measure("ds", subdomain_id=int(obj['number']), subdomain_data=self.facet_function) if obj['inflow']: self.inflow_measures.append(obj['measure']) else: self.outflow_measures.append(obj['measure']) self.listDict.update({ 'outflow': { 'list': [], 'name': 'outflow rate', 'abrev': 'OUT', 'slist': [] }, 'inflow': { 'list': [], 'name': 'inflow rate', 'abrev': 'IN', 'slist': [] }, 'oiratio': { 'list': [], 'name': 'outflow/inflow ratio (mass conservation)', 'abrev': 'O/I', 'slist': [] }, }) for obj in self.outflows: n = obj['number'] self.listDict.update({ 'outflow' + n: { 'list': [], 'name': 'outflow rate ' + n, 'abrev': 'OUT' + n, 'slist': [] } }) self.can_force_outflow = True
def print_info(): if MPI.rank(comm) == 0: info(dolfin.parameters, True)
# if args.solver == 'ipcs1': # PETScOptions.set('log_summary') # Paralell run initialization comm = mpi_comm_world() rank = MPI.rank(comm) # parameters["std_out_all_processes"] = False # print only rank==0 process output # parameters["ghost_mode"] = "shared_facet" # may be needed for operating DG elements in parallel # allows output using info() for the main process only if rank == 0 or args.out == 'all': set_log_level(INFO) else: set_log_level(INFO + 1) info('Running on %d processor(s).' % MPI.size(comm)) if MPI.size(comm) > 1 and args.problem == 'womersley_cylinder': info( 'Womersley cylinder problem is not runnable in parallel due to method of construction of analytic solution,' ' which is used to describe boundary conditions.' ) # the change of mesh format would be also needed exit() # dynamically import selected solver and problem files exec('from solvers.%s import Solver' % args.solver) exec('from problems.%s import Problem' % args.problem) # setup and parse problem- and solver-specific command-line arguments parser = argparse.ArgumentParser() Solver.setup_parser_options(parser)