def test_assign(): c0 = Constant(1.) assert c0.values() == (1,) c0.assign(Constant(3)) assert c0.values() == (3,) c1 = Constant([1, 2]) assert (c1.values() == (1, 2)).all() c1.assign(Constant([3, 4])) assert (c1.values() == (3, 4)).all()
def test_values(): import numpy as np c0 = Constant(1.) c0_vals = c0.values() assert np.all(c0_vals == np.array([1.], dtype=np.double)) c1 = Constant((1., 2.)) c1_vals = c1.values() assert np.all(c1_vals == np.array([1., 2.], dtype=np.double)) c2 = Constant((1., 2., 3.)) c2_vals = c2.values() assert np.all(c2_vals == np.array([1., 2., 3.], dtype=np.double))
def test_constant_float_conversion(): c = Constant(3.45) assert float(c.values()[0]) == 3.45
class M3H3(object): def __init__(self, geometry, parameters, *args, **kwargs): self.parameters = parameters self.physics = [Physics(p) for p in parameters.keys() if Physics.has_value(p)] self.interactions = kwargs.get('interactions', []) if len(self.interactions) > 0: self._check_physics_interactions() if 'time' in kwargs.keys(): self.time = kwargs['time'] kwargs.pop('time', None) else: self.time = Constant(self.parameters['start_time']) self._setup_geometries(geometry, self.physics) self._setup_problems(**kwargs) self._setup_solvers(**kwargs) def step(self): # Setup time stepping if running step function for the first time. if self.time.values()[0] == self.parameters['start_time']: self.num_steps, self.max_dt = self._get_num_steps() time = float(self.time) solution_fields = self.get_solution_fields() if Physics.ELECTRO in self.physics: for _ in range(self.num_steps[Physics.ELECTRO]): electro_fields = solution_fields[str(Physics.ELECTRO)] self.electro_solver.step(electro_fields[1]) if Physics.SOLID in self.physics: for _ in range(self.num_steps[Physics.SOLID]): self.solid_solver.step() if Physics.FLUID in self.physics: for _ in range(self.num_steps[Physics.FLUID]): self.fluid_solver.step() if Physics.POROUS in self.physics: for _ in range(self.num_steps[Physics.POROUS]): self.porous_solver.step() self.time.assign(time + self.max_dt) return time, solution_fields def get_solution_fields(self): solution_fields = {} if Physics.ELECTRO in self.physics: solution_fields[str(Physics.ELECTRO)] =\ self.electro_problem._get_solution_fields() if Physics.SOLID in self.physics: pass if Physics.FLUID in self.physics: pass if Physics.POROUS in self.physics: pass return solution_fields def add_stimulus(self, stimulus): assert hasattr(self, 'electro_problem'), \ "Cannot add stimulus if electrophysiology has not been set up." self.electro_problem.add_stimulus(stimulus) def _get_num_steps(self): dt_physics = self._get_physics_dt() min_dt = min(dt_physics.values()) max_dt = max(dt_physics.values()) num_steps = {} if Physics.ELECTRO in self.physics: dt = dt_physics[Physics.ELECTRO] if self._check_dt_is_multiple(dt, min_dt): num_steps[Physics.ELECTRO] = int(max_dt/dt) if Physics.SOLID in self.physics: dt = dt_physics[Physics.SOLID] if self._check_dt_is_multiple(dt, min_dt): num_steps[Physics.SOLID] = int(max_dt/dt) if Physics.FLUID in self.physics: dt = dt_physics[Physics.FLUID] if self._check_dt_is_multiple(dt, min_dt): num_steps[Physics.FLUID] = int(max_dt/dt) if Physics.POROUS in self.physics: dt = dt_physics[Physics.POROUS] if self._check_dt_is_multiple(dt, min_dt): num_steps[Physics.POROUS] = int(max_dt/dt) return num_steps, max_dt def _check_dt_is_multiple(self, dt, min_dt): if not (dt/min_dt).is_integer(): msg = "Time step sizes have to be multiples of each other."\ "{} is not a multiple of {}".format(dt, min_dt) raise ValueError(msg) else: return True def _get_physics_dt(self): dt = {} if Physics.ELECTRO in self.physics: dt[Physics.ELECTRO] = self.parameters[str(Physics.ELECTRO)]['dt'] if Physics.SOLID in self.physics: dt[Physics.SOLID] = self.parameters[str(Physics.SOLID)]['dt'] if Physics.FLUID in self.physics: dt[Physics.FLUID] = self.parameters[str(Physics.FLUID)]['dt'] if Physics.POROUS in self.physics: dt[Physics.POROUS] = self.parameters[str(Physics.POROUS)]['dt'] return dt def _setup_problems(self, **kwargs): if Physics.ELECTRO in self.physics: self.electro_problem = ElectroProblem( self.geometries[Physics.ELECTRO], self.time, self.parameters[str(Physics.ELECTRO)], **kwargs) if Physics.SOLID in self.physics: self.solid_problem = SolidProblem( self.geometries[Physics.SOLID], self.time, self.parameters[str(Physics.SOLID)], **kwargs) if Physics.FLUID in self.physics: self.fluid_problem = FluidProblem( self.geometries[Physics.FLUID], self.time, self.parameters[str(Physics.FLUID)], **kwargs) if Physics.POROUS in self.physics: self.porous_problem = PorousProblem( self.geometries[Physics.POROUS], self.time, self.parameters[str(Physics.POROUS)], **kwargs) def _setup_solvers(self, **kwargs): interval = (self.parameters['start_time'], self.parameters['end_time']) solution_fields = self.get_solution_fields() if Physics.ELECTRO in self.physics: elabel = str(Physics.ELECTRO) electro_fields = solution_fields[elabel] parameters = self.parameters[elabel]['linear_variational_solver'] self.electro_solver = BasicBidomainSolver(self.time, self.electro_problem._form, electro_fields, parameters, **kwargs) if Physics.SOLID in self.physics: parameters = self.parameters[str(Physics.SOLID)] self.solid_solver = SolidSolver( self.solid_problem._form, self.time, interval, parameters['dt'], parameters, **kwargs) if Physics.FLUID in self.physics: parameters = self.parameters[str(Physics.FLUID)] self.fluid_solver = FluidSolver( self.fluid_problem._form, self.time, interval, parameters['dt'], parameters, **kwargs) if Physics.POROUS in self.physics: parameters = self.parameters[str(Physics.POROUS)] self.porous_solver = PorousSolver( self.porous_problem._form, self.time, interval, parameters['dt'], parameters, **kwargs) def _setup_geometries(self, geometry, physics): self.geometries = {} if isinstance(geometry, MultiGeometry): for phys in physics: try: self.geometries[phys] = geometry.geometries[phys.value] except KeyError: msg = "Could not find a geometry for {} physics in "\ "MultiGeometry. Ensure that geometry labels "\ "correspond to values in Physics "\ "enum.".format(phys.value) raise KeyError(msg) assert isinstance(self.geometries[phys], HeartGeometry) elif len(physics) == 1: self.geometries[physics[0]] = geometry else: for phys in physics: self.geometries[phys] = geometry.copy(deepcopy=True) def _check_physics_interactions(self): # If multiple physics are defined, check that all are involved in an # interaction and that physics involved in an interaction are set up if len(self.physics) == 0 and len(self.interactions) > 0: msg = "At least one interaction has been set up, but no physics\n"\ "Interactions: {}".format(self.interactions) raise KeyError(msg) if len(self.physics) > 1: int_physics = set(np.array([ia.to_list() for ia in self.interactions]).flat) for p in int_physics: if p not in self.physics: msg = "Physics {} appears in interaction, but is not set "\ "up.".format(p) raise KeyError(msg) for p in self.physics: if p not in int_physics: msg = "Physcis {} is set up, but does not appear in any "\ "interaction.".format(p) raise KeyError(msg) def _setup_electro_problem(self, parameters): self.electro_problem = ElectroProblem(self.geometries[Physics.ELECTRO], self.time, parameters) def _setup_solid_problem(self, parameters): self.solid_problem = SolidProblem(self.geometries[Physics.SOLID], self.time, parameters) def _setup_fluid_problem(self, parameters): self.fluid_problem = FluidProblem(self.geometries[Physics.FLUID], self.time, parameters) def _setup_porous_problem(self, parameters): self.porous_problem = PorousProblem(self.geometries[Physics.POROUS], self.time, parameters) def update_parameters(self, physics, parameters): if physics == Physics.ELECTRO: self.parameters.set_electro_parameters(parameters) elif physics == Physics.SOLID: self.parameters.set_solid_parameters(parameters) elif physics == Physics.FLUID: self.parameters.set_fluid_parameters(parameters) elif physics == Physics.POROUS: self.parameters.set_porous_parameters(parameters)
class CavityProblemSetup(): def __init__(self, parameters, mesh_name, facet_name): """ Create the required function spaces, functions and boundary conditions for a channel flow problem """ self.mesh = Mesh() with XDMFFile(mesh_name) as infile: infile.read(self.mesh) mvc = MeshValueCollection("size_t", self.mesh, self.mesh.topology().dim() - 1) with XDMFFile(facet_name) as infile: infile.read(mvc, "name_to_read") mf = self.mf = cpp.mesh.MeshFunctionSizet(self.mesh, mvc) self.bc_dict = {"bottom": 1, "right": 2, "top": 3, "left": 4} T_init = Constant(parameters["initial temperature [°C]"]) self.t_amb = Constant(parameters["ambient temperature [°C]"]) self.t_feeder = Constant(parameters["temperature feeder [°C]"]) self.k_top = Constant(parameters["thermal conductivity top [W/(m K)]"]) self.k_lft = Constant( parameters["thermal conductivity left [W/(m K)]"]) self.k_btm = Constant( parameters["thermal conductivity bottom [W/(m K)]"]) self.k_rgt = Constant( parameters["thermal conductivity right [W/(m K)]"]) self.U_m = Constant(parameters["mean velocity lid [m/s]"]) g = parameters["gravity [m/s²]"] self.g = Constant((0.0, -g)) self.dt = Constant(parameters["dt [s]"]) self.D = Constant(parameters["Diffusivity [-]"]) self.V = V = VectorFunctionSpace(self.mesh, 'P', 2) self.Q = Q = FunctionSpace(self.mesh, 'P', 1) self.T = T = FunctionSpace(self.mesh, 'P', 1) self.ds_ = Measure("ds", domain=self.mesh, subdomain_data=mf) self.vu, self.vp, self.vt = (TestFunction(V), TestFunction(Q), TestFunction(T)) self.u_, self.p_, self.t_ = Function(V), Function(Q), Function(T) self.mu, self.rho = Function(T), Function(T) self.u_1, self.p_1, self.t_1, self.rho_1 = (Function(V), Function(Q), Function(T), Function(T)) self.u, self.p, self.t = (TrialFunction(V), TrialFunction(Q), TrialFunction(T)) # boundary conditions self.no_slip = Constant((0., 0)) self.topflow = Expression(("-x[0] * (x[0] - 1.0) * 6.0 * m", "0.0"), m=self.U_m, degree=2) bc0 = DirichletBC(V, self.topflow, mf, self.bc_dict["top"]) bc1 = DirichletBC(V, self.no_slip, mf, self.bc_dict["left"]) bc2 = DirichletBC(V, self.no_slip, mf, self.bc_dict["bottom"]) bc3 = DirichletBC(V, self.no_slip, mf, self.bc_dict["right"]) # bc4 = df.DirichletBC(Q, df.Constant(0), top) # bc3 = df.DirichletBC(T, df.Constant(800), top) self.bcu = [bc0, bc1, bc2, bc3] self.bcp = [DirichletBC(Q, Constant(0), mf, self.bc_dict["top"])] self.bcp = [] self.bct = [] # self.bct = [DirichletBC(T, Constant(self.t_feeder), mf, # self.bc_dict["top"])] self.robin_boundary_terms = ( self.k_btm * (self.t - self.t_amb) * self.vt * self.ds_(1) + self.k_rgt * (self.t - self.t_amb) * self.vt * self.ds_(2) # + self.k_top*(self.t - self.t_feeder)*self.vt*self.ds_(3) + self.k_lft * (self.t - self.t_amb) * self.vt * self.ds_(4)) print("k, T", self.k_btm.values(), self.t_feeder.values()) print("k, T", self.k_rgt.values(), self.t_amb.values()) # print("k, T", self.k_top.values(), self.t_amb.values()) print("k, T", self.k_lft.values(), self.t_amb.values()) # set initial values # TODO: find a better solution x, y = T.tabulate_dof_coordinates().T self.u_1.vector().vec().array[:] = 1e-6 # self.u_k.vector().vec().array[:] = 1e-6 self.p_.vector().vec( ).array[:] = -self.rho.vector().vec().array * g * y self.p_1.vector().vec( ).array[:] = -self.rho.vector().vec().array * g * y self.t_1.vector().vec().array = T_init self.t_.assign(self.t_1) return def stokes(self): P2 = VectorElement("CG", self.mesh.ufl_cell(), 2) P1 = FiniteElement("CG", self.mesh.ufl_cell(), 1) TH = P2 * P1 VQ = FunctionSpace(self.mesh, TH) mf = self.mf self.no_slip = Constant((0., 0)) self.topflow = Expression(("-x[0] * (x[0] - 1.0) * 6.0 * m", "0.0"), m=self.U_m, degree=2) bc0 = DirichletBC(VQ.sub(0), self.topflow, mf, self.bc_dict["top"]) bc1 = DirichletBC(VQ.sub(0), self.no_slip, mf, self.bc_dict["left"]) bc2 = DirichletBC(VQ.sub(0), self.no_slip, mf, self.bc_dict["bottom"]) bc3 = DirichletBC(VQ.sub(0), self.no_slip, mf, self.bc_dict["right"]) # bc4 = DirichletBC(VQ.sub(1), Constant(0), mf, self.bc_dict["top"]) bcs = [bc0, bc1, bc2, bc3] vup = TestFunction(VQ) up = TrialFunction(VQ) # the solution will be in here: up_ = Function(VQ) u, p = split(up) # Trial vu, vp = split(vup) # Test u_, p_ = split(up_) # Function holding the solution F = self.mu*inner(grad(vu), grad(u))*dx - inner(div(vu), p)*dx \ - inner(vp, div(u))*dx + dot(self.g*self.rho, vu)*dx solve(lhs(F) == rhs(F), up_, bcs=bcs) self.u_.assign(project(u_, self.V)) self.p_.assign(project(p_, self.Q)) return def initial_condition_from_file(self, path_u, path_p): f_in = XDMFFile(path_u) f_in.read_checkpoint(self.u_, "f", 0) f_in = XDMFFile(path_p) f_in.read_checkpoint(self.p_, "f", 0) return def get_rho(self): return self.rho.vector().vec().array def set_rho(self, rho): self.rho.vector().vec().array[:] = rho def get_mu(self): return self.mu.vector().vec().array def set_mu(self, mu): self.mu.vector().vec().array[:] = mu def get_t(self): return self.t_.vector().vec().array def set_t(self, t): self.t_.vector().vec().array[:] = t def get_dt(self): return self.dt.values() def set_dt(self, dt): self.dt.assign(dt) def get_D(self): return self.D.values() def set_D(self, D): self.D.assign(D) def get_t_amb(self): return self.t_amb.values() def set_t_amb(self, t_amb): self.t_amb.assign(t_amb) def plot(self): cmap = mpl.cm.inferno cmap_r = mpl.cm.inferno_r u, p, t, m, r = self.u_, self.p_, self.t_, self.mu, self.rho mesh = self.mesh w0 = u.compute_vertex_values(mesh) w0.shape = (2, -1) magnitude = np.linalg.norm(w0, axis=0) x, y = np.split(mesh.coordinates(), 2, 1) u, v = np.split(w0, 2, 0) x, y, u, v = x.ravel(), y.ravel(), u.ravel(), v.ravel() tri = mesh.cells() pressure = p.compute_vertex_values(mesh) temperature = t.compute_vertex_values(mesh) viscosity = m.compute_vertex_values(mesh) density = r.compute_vertex_values(mesh) fig = plt.figure(figsize=(12, 8)) ax1 = plt.subplot(121) ax2 = plt.subplot(243, sharex=ax1, sharey=ax1) ax3 = plt.subplot(244, sharex=ax1, sharey=ax1) ax4 = plt.subplot(247, sharex=ax1, sharey=ax1) ax5 = plt.subplot(248, sharex=ax1, sharey=ax1) ax1.plot(x, y, "k.", ms=.5) not0 = magnitude > 1e-6 if np.sum(not0) > 0: ax1.quiver(x[not0], y[not0], u[not0], v[not0], magnitude[not0]) c2 = ax2.tricontourf(x, y, tri, pressure, levels=40, cmap=cmap) c3 = ax3.tricontourf(x, y, tri, temperature, levels=40, cmap=cmap) c4 = ax4.tricontourf( x, y, tri, viscosity, levels=40, # vmin=self.mu(800, .1), vmax=self.mu(600, .1), cmap=cmap_r) c5 = ax5.tricontourf( x, y, tri, density, levels=40, # vmin=self.rho(800.), vmax=self.rho(600.), cmap=cmap_r) plt.colorbar(c2, ax=ax2) plt.colorbar(c3, ax=ax3, ticks=[temperature.min(), temperature.max()]) plt.colorbar(c4, ax=ax4, ticks=[viscosity.min(), viscosity.max()]) plt.colorbar(c5, ax=ax5, ticks=[density.min(), density.max()]) ax1.set_aspect("equal") ax2.set_aspect("equal") ax3.set_aspect("equal") ax4.set_aspect("equal") ax5.set_aspect("equal") ax1.set_title("velocity\n{:.4f} ... {:.5f} m/s".format( magnitude.min(), magnitude.max())) ax2.set_title("pressure") ax3.set_title("temperature") ax4.set_title("viscosity") ax5.set_title("density") ax1.set_xlim([-.1, 1.1]) ax1.set_ylim([-.1, 1.1]) # plt.tight_layout() return fig, (ax1, ax2)
class VOFMixin(object): """ This is a mixin class to avoid having duplicates of the methods calculating rho, nu and mu. Any subclass using this mixin must define the method "get_colour_function(k)" and can also redefine the boolean property that controls the way mu is calculated, "calculate_mu_directly_from_colour_function". """ calculate_mu_directly_from_colour_function = True default_polynomial_degree_colour = 0 _level_set_view = None @classmethod def create_function_space(cls, simulation): mesh = simulation.data['mesh'] cd = simulation.data['constrained_domain'] Vc_name = simulation.input.get_value( 'multiphase_solver/function_space_colour', 'Discontinuous Lagrange', 'string') Pc = simulation.input.get_value( 'multiphase_solver/polynomial_degree_colour', cls.default_polynomial_degree_colour, 'int', ) Vc = FunctionSpace(mesh, Vc_name, Pc, constrained_domain=cd) simulation.data['Vc'] = Vc simulation.ndofs += Vc.dim() def set_physical_properties(self, rho0=None, rho1=None, nu0=None, nu1=None, read_input=False): """ Set rho and nu (density and kinematic viscosity) in both domain 0 and 1. Either specify all of rho0, rho1, nu0 and nu1 or set read_input to True which will read from the physical_properties section of the simulation input object. """ sim = self.simulation if read_input: rho0 = sim.input.get_value('physical_properties/rho0', required_type='float') rho1 = sim.input.get_value('physical_properties/rho1', required_type='float') nu0 = sim.input.get_value('physical_properties/nu0', required_type='float') nu1 = sim.input.get_value('physical_properties/nu1', required_type='float') self.df_rho0 = Constant(rho0) self.df_rho1 = Constant(rho1) self.df_nu0 = Constant(nu0) self.df_nu1 = Constant(nu1) self.df_smallest_rho = self.df_rho0 if rho0 <= rho1 else self.df_rho1 def set_rho_min(self, rho_min): """ This is used to bring rho_min closer to rho_max for the initial linear solver iterations (to speed up convergence) """ self.df_smallest_rho.assign(Constant(rho_min)) def get_colour_function(self, k): """ Return the colour function on timestep t^{n+k} """ raise NotImplementedError( 'The get_colour_function method must be implemented by subclass!') def get_density(self, k=None, c=None): """ Calculate the blended density function as a weighted sum of rho0 and rho1. The colour function is unity when rho=rho0 and zero when rho=rho1 Return the function as defined on timestep t^{n+k} """ if c is None: assert k is not None c = self.get_colour_function(k) else: assert k is None return self.df_rho0 * c + self.df_rho1 * (1 - c) def get_laminar_kinematic_viscosity(self, k=None, c=None): """ Calculate the blended kinematic viscosity function as a weighted sum of nu0 and nu1. The colour function is unity when nu=nu0 and zero when nu=nu1 Return the function as defined on timestep t^{n+k} """ if c is None: assert k is not None c = self.get_colour_function(k) else: assert k is None return self.df_nu0 * c + self.df_nu1 * (1 - c) def get_laminar_dynamic_viscosity(self, k=None, c=None): """ Calculate the blended dynamic viscosity function as a weighted sum of mu0 and mu1. The colour function is unity when mu=mu0 and zero when mu=mu1 Return the function as defined on timestep t^{n+k} """ if self.calculate_mu_directly_from_colour_function: if c is None: assert k is not None c = self.get_colour_function(k) else: assert k is None mu0 = self.df_nu0 * self.df_rho0 mu1 = self.df_nu1 * self.df_rho1 return mu0 * c + mu1 * (1 - c) else: nu = self.get_laminar_kinematic_viscosity(k, c) rho = self.get_density(k, c) return nu * rho def get_density_range(self): """ Return the maximum and minimum densities, rho """ rho0 = self.df_rho0.values()[0] rho1 = self.df_rho1.values()[0] return min(rho0, rho1), max(rho0, rho1) def get_laminar_kinematic_viscosity_range(self): """ Return the maximum and minimum kinematic viscosities, nu """ nu0 = self.df_nu0.values()[0] nu1 = self.df_nu1.values()[0] return min(nu0, nu1), max(nu0, nu1) def get_laminar_dynamic_viscosity_range(self): """ The minimum and maximum laminar dynamic viscosities, mu. Mu is either calculated directly from the colour function, in this case mu is a linear function, or as a product of nu and rho, where it is a quadratic function and can have (in i.e the case of water and air) have maximum values() in the middle of the range c ∈ (0, 1) """ rho0 = self.df_rho0.values()[0] rho1 = self.df_rho1.values()[0] nu0 = self.df_nu0.values()[0] nu1 = self.df_nu1.values()[0] if self.calculate_mu_directly_from_colour_function: mu0 = nu0 * rho0 mu1 = nu1 * rho1 return min(mu0, mu1), max(mu0, mu1) else: c = numpy.linspace(0, 1, 1000) nu = nu0 * c + nu1 * (1 - c) rho = rho0 * c + rho1 * (1 - c) mu = nu * rho return mu.min(), mu.max() def get_level_set_view(self): """ Get a view of this VOF field as a level set function """ if self._level_set_view is None: self._level_set_view = LevelSetView(self.simulation) c = self.get_colour_function(0) self._level_set_view.set_density_field(c) return self._level_set_view