def __solve_diffusion(self, model): oxygen_grid = model.environments[self.oxygen_env_name] nx = oxygen_grid.xsize ny = oxygen_grid.ysize nz = oxygen_grid.zsize dx = dy = dz = 1.0 D = self.oxygen_diffusion_coeff mesh = Grid3D(dx=dx, dy=dy, nx=nx, ny=ny, dz=dz, nz=nz) phi = CellVariable(name="solutionvariable", mesh=mesh) phi.setValue(0.) start = time.time() for _ in range(self.diffusion_solve_iterations): source_grid, sink_grid = self.__get_source_sink_grids(phi, model) eq = TransientTerm() == DiffusionTerm( coeff=D) + source_grid - sink_grid eq.solve(var=phi, dt=1) eq = TransientTerm() == DiffusionTerm(coeff=D) eq.solve(var=phi, dt=self.dt) end = time.time() print("Solving oxygen diffusion took %s seconds" % str(end - start)) return phi, nx, ny, nz
def define_convection_variable(self, current_time): x_convection, y_convection = self.get_convection_x_and_y(current_time) if self.baseline_convection is None: convection_variable = CellVariable(mesh=self.mesh, rank=1) # Only define the variable from scratch once else: convection_variable = self.baseline_convection convection_variable.setValue((x_convection, y_convection)) return convection_variable
def __init__(self, X=None, T=None, time_steps=5, max_time=0.4, num_cells=25, L=1.): """Initialize the objet. Keyword Arguments: X --- The sensor locations. T --- The sensor measurment times. time_steps --- How many timesteps do you want to measure. max_time --- The maximum solution time. num_cells --- The number of cells per dimension. L --- The size of the computational domain. """ assert isinstance(num_cells, int) self._num_cells = num_cells assert isinstance(L, float) and L > 0. self._dx = L / self.num_cells self._mesh = Grid2D(dx=self.dx, dy=self.dx, nx=self.num_cells, ny=self.num_cells) self._phi = CellVariable(name='solution variable', mesh=self.mesh) self._source = CellVariable(name='source term', mesh=self.mesh, hasOld=True) self._eqX = TransientTerm() == ExplicitDiffusionTerm( coeff=1.) + self.source self._eqI = TransientTerm() == DiffusionTerm(coeff=1.) + self.source self._eq = self._eqX + self._eqI assert isinstance(max_time, float) and max_time > 0. self._max_time = max_time #self.max_time / time_steps #. if X is None: idx = range(self.num_cells**2) else: idx = [] x1, x2 = self.mesh.cellCenters for x in X: dist = (x1 - x[0])**2 + (x2 - x[1])**2 idx.append(np.argmin(dist)) self._idx = idx if T is None: T = np.linspace(0, self.max_time, time_steps)[1:] self._max_time = T[-1] self._T = T self._dt = self.T[0] / time_steps super(Diffusion, self).__init__(5, len(self.T) * len(self.idx), name='Diffusion Solver')
def __get_source_sink_grids(self, phi, model): xsize = model.environments[self.vegf_env_name].xsize ysize = model.environments[self.vegf_env_name].ysize zsize = model.environments[self.vegf_env_name].zsize agent_grid = model.environments["agentEnv"].grid phi_tmp = np.reshape(phi._array, (xsize, ysize, zsize)) source_grid = CellVariable(name="source", mesh=Grid3D(dx=1, dy=1, dz=1, nx=xsize, ny=ysize, nz=zsize)) for coordinate, agents in agent_grid.items(): if len(agents) == 0: continue concentration_at_pos = phi_tmp[coordinate[0]][coordinate[1]][ coordinate[2]] source_rate = sum([ a.current_vegf_secretion_rate for a in agents if (a.__class__.__name__ == "CancerCell" and not (a.quiescent or a.dead)) ]) # A pre-estimate of what the concentration at this position will # be. This of course neglects diffusion, # but can give an estimate of how we should regulate our sources # and sinks estimated_concentration = concentration_at_pos + source_rate # If our estimated concentration is greater than our maximum # source rate, this means we really are outputting # too much. At most, we want to achieve equilibrium between # sources and environment, so we reduce our # output rate. Of course, we can't reduce our output rate by # more than the output rate itself if estimated_concentration >= self.max_vegf: source_rate -= min(source_rate, estimated_concentration - self.max_vegf) i = np.ravel_multi_index( [coordinate[0], coordinate[1], coordinate[2]], (xsize, ysize, zsize)) source_grid.value[i] = source_rate return source_grid
def setup(self, ncell): m = Grid1D(nx=ncell, Lx=1.) v = CellVariable(mesh=m, hasOld=True, value=[[0.5], [0.5]], elementshape=(2,)) v.constrain([[0], [1]], m.facesLeft) v.constrain([[1], [0]], m.facesRight) eqn = TransientTerm([[1,0], [0,1]]) == DiffusionTerm([[[0.01, -1], [1, 0.01]]]) self.v = v self.eqn = eqn for step in range(2): self.time_step()
def view_sol(self, times, conc): """ Show the solution in conc with times. conc[i][:] contains solution at time times[i] """ if self.plotevery: self.solution_view = CellVariable(name="Yarn radial concentration", mesh=self.mesh_yarn, value=conc[0][:self.nr_cell]) if isinstance(conc, np.ndarray): maxv = conc.max() * 1.2 minv = conc.min() * 0.9 else: maxv = np.max(conc) * 1.2 minv = np.min(conc) * 0.9 self.viewer = Matplotlib1DViewer(vars=self.solution_view, datamin=minv, datamax=maxv) self.viewerplotcount = 0 self.viewerwritecount = 0 for time, con in zip(times, conc): self.solution_view.setValue(con[:self.nr_cell]) if self.viewerplotcount == 0 or (self.writeevery and self.viewerwritecount == 0): self.viewer.axes.set_title('time %s' %str(time)) if self.writeevery and self.viewerwritecount == 0: #plot and savefig self.viewer.plot(filename=utils.OUTPUTDIR + os.sep \ + 'yarnconc%08.4f.png' % time) else: #only plot self.viewer.plot() self.viewerplotcount += 1 self.viewerplotcount = self.viewerplotcount % self.plotevery if self.writeevery: self.viewerwritecount += 1 self.viewerwritecount = self.viewerwritecount % self.writeevery
def initializeTright(self): extrapol_dist = ( self.mesh.mesh.faceCenters[0, self.mesh.mesh.facesRight()][0] - self.mesh.cell_mid_points) self.dxf = CellVariable(mesh=self.mesh.mesh, value=extrapol_dist) self.variables['T_right'] = (self.variables['T'] + self.variables['T'].grad[0] * self.dxf)
def icir_rect(self): # step 1: Mesh mesh = Grid2D(dx=self.gsize, dy=self.gsize, nx=self.xy_n[0], ny=self.xy_n[1]) # step 2: Equation phi = CellVariable(mesh=mesh, name='potential phi', value=0.) eqn = (DiffusionTerm(coeff = 1.) == 0.) # step 3: Boundary conditions # compute flow of 4 vertexes # one vertex has 2 components of wind vector, (x, y) vertexWindVec = np.array([ [0.0 for i in range(2)] for i in range(4)]) for i in range(4): # 4 vertexes for j in range(2): # 2 components vertexWindVec[i, j] = self.mean_flow[j] \ + self.colored_noise(self.vertexWindVecRanInc[i][j]) # interpolate flow vector on sim area edges, and set neumann boundary # conditions, because /grad /phi = V(x,y) # get all points which lie on the center of edges of cells X, Y = mesh.faceCenters # /grad /phi array, of points of mesh face centers # grad_phi_bc[0, :] /grad/phi_x # grad_phi_bc[1, :] /grad/phi_y grad_phi_bc = np.zeros_like(mesh.faceCenters()) # p: points on one edge to interpolate, 1 dimension # vx, vy: x&y components of interpolated wind vectors of points list p # vertex index, for interpolate bc on 4 edges # vertexIndex[0] = [1,2], down boundary, 0<x<nx*dx, y=0 # vertexIndex[1] = [1,0], left boundary, x=0, 0<y<ny*dy # vertexIndex[2] = [0,3], up boundary, 0<x<nx*dx, y=ny*dy # vertexIndex[3] = [2,3], right boundary, x=nx*dx, 0<y<ny*dy vertexIndex = np.array([ [1,2], [1,0], [0,3], [2,3] ]) for i in range(4): # 4 edges for 2D rect area p = np.arange(self.gsize/2, self.gsize*self.xy_n[i%2], self.gsize) vx = np.interp(p, [0.0, self.gsize*self.xy_n[i%2]], \ [vertexWindVec[vertexIndex[0,0], 0], \ vertexWindVec[vertexIndex[0,1], 0]]).T.reshape(1,-1)[0] vy = np.interp(p, [0.0, self.gsize*self.xy_n[i%2]], \ [vertexWindVec[vertexIndex[0,0], 1], \ vertexWindVec[vertexIndex[0,1], 1]]).T.reshape(1,-1)[0] print 'vx = ' + str(vx) if i == 0: # down boundary grad_phi_bc[:, mesh.facesDown()] = np.array([vx, vy]) elif i == 1: # left boundary grad_phi_bc[:, mesh.facesLeft()] = np.array([vx, vy]) elif i == 2: # up boundary grad_phi_bc[:, mesh.facesUp()] = np.array([vx, vy]) elif i == 3: # right boundary grad_phi_bc[:, mesh.facesRight()] = np.array([vx, vy]) # set neumann boundary condition phi.faceGrad.constrain(((grad_phi_bc[0]),(grad_phi_bc[1])), where=mesh.exteriorFaces) # step 4: Solve eqn.solve(var=phi) #print str(phi) #print str(type(np.array(phi))) self.wind_phi_field = np.array(phi) self.wind_mesh_centers = mesh.cellCenters()
def _setup_equation(self, biomass_height): height = biomass_height + self._layer_height size = height * self._layer mesh = self.space.construct_mesh(height) variables, terms = [], [] phi = CellVariable(name=self.solute.name, mesh=mesh, hasOld=True) for r in self.reactions: variables.append( CellVariable(name=f"{r.bacteria.name}_rate", mesh=mesh, value=0.0)) terms.append( ImplicitSourceTerm(coeff=(variables[-1] / (phi + self._sr)))) equation = DiffusionTerm(coeff=self.diffusivity) - sum(terms) phi.constrain(1, where=mesh.facesTop) for var, coef in zip( variables, [r.rate_coefficient()[:size] for r in self.reactions]): try: var.setValue(coef / self.space.dV) except ValueError as err: print("Boundary layer height greater than system size") raise err phi.setValue(self.solute.value.reshape(-1)[:size]) return equation, phi, size
def solve_pde( xmin, # domain min xmax, # domain max Tmax, # time max theta=1, # dynamics drift mu=0, # dynamics stable level sigma=1, # dynamics noise dx=0.01, # space discretization dt=0.01, # time discretization gbm=False): # state-dependent drift mesh = Grid1D(dx=dx, nx=(xmax - xmin) / dx) + xmin Tsteps = int(Tmax / dt) + 1 x_face = mesh.faceCenters x_cell = mesh.cellCenters[0] V = CellVariable(name="V", mesh=mesh, value=0.) # PDE if gbm: eq = TransientTerm( var=V) == (DiffusionTerm(coeff=float(sigma) * sigma / 2, var=V) - UpwindConvectionTerm( coeff=float(theta) * (x_face - float(mu)), var=V) + V * (float(theta) - x_cell)) else: eq = TransientTerm( var=V) == (DiffusionTerm(coeff=float(sigma) * sigma / 2, var=V) - UpwindConvectionTerm(coeff=float(theta) * (x_face - float(mu)), var=V) + V * float(theta)) # Boundary conditions V.constrain(1., mesh.facesRight) V.faceGrad.constrain([0.], mesh.facesLeft) # Solve by stepping in time sol = np.zeros((Tsteps, mesh.nx)) for step in range(Tsteps): eq.solve(var=V, dt=dt) sol[step] = V.value X = mesh.cellCenters.value[0] T = dt * np.arange(Tsteps) return T, X, sol
def define_cellvariables(self, phi_s_initial, cs_surface_initial, phi_e_initial, theta_initial, ce_initial, sim_cell): self.phi_s = CellVariable(mesh=self.axial_mesh, value=phi_s_initial) # self.deltaPhiS = CellVariable(mesh=self.axial_mesh, value = 0.) # For Newton iteration self.phi_e_copy_from_supermesh = CellVariable(mesh=self.axial_mesh, value=phi_e_initial) self.Cs_surface = CellVariable(mesh=self.axial_mesh, value=cs_surface_initial) self.overpotential = CellVariable( mesh=self.axial_mesh, value=(phi_s_initial - phi_e_initial - self.calc_ocp_interp(theta_initial))) self.Cs_p2d = CellVariable(mesh=self.p2d_mesh, value=cs_surface_initial[0], hasOld=True) self.j0 = CellVariable( mesh=self.axial_mesh, value=self.k_norm * numerix.absolute( ((self.cs_max - cs_surface_initial) / self.cs_max) **(1 - sim_cell.alpha)) * numerix.absolute( (cs_surface_initial / self.cs_max)**sim_cell.alpha) * numerix.absolute( (ce_initial / ce_initial)**(1.0 - sim_cell.alpha))) self.uocp = CellVariable(mesh=self.axial_mesh, value=self.calc_ocp_interp(self.Cs_surface / self.cs_max))
def _genTorque(self): """Generate Torque""" self.Lambda = FaceVariable(name='Torque at cell faces', mesh=self.mesh, rank=1) self.LambdaCell = CellVariable(name='Torque at cell centers', mesh=self.mesh) LambdaArr = np.zeros(self.rF.shape) LambdaArr[1:] = self.chi*np.power(1.0/(self.rF[1:]*self.gamma-1.0), 4) #LambdaArr[self.gap] = 0.0; LambdaArr[self.gap] = LambdaArr.max() self.Lambda.setValue(LambdaArr) self.LambdaCell.setValue(self.chi*np.power(1.0/(self.r*self.gamma-1.0), 4)) self.LambdaCell[np.where(self.LambdaCell > LambdaArr.max())] = LambdaArr.max()
def Battery_cellvariables(self, neg_epsilon_e, sep_epsilon_e, pos_epsilon_e, neg_a_s, pos_a_s, nx_neg, nx_sep, nx_pos, j_battery_value, ce_initial, phi_e_initial, neg_L, sep_L, pos_L, neg_De_eff, sep_De_eff, pos_De_eff): self.Ce = CellVariable(mesh=self.axial_mesh, value=ce_initial, hasOld=True) self.phi_e = CellVariable(mesh=self.axial_mesh, value=phi_e_initial) self.epsilon_e_value = numerix.zeros(nx_neg + nx_sep + nx_pos) self.epsilon_e_value[0:nx_neg], self.epsilon_e_value[ nx_neg:nx_neg + nx_sep] = neg_epsilon_e, sep_epsilon_e self.epsilon_e_value[nx_neg + nx_sep:] = pos_epsilon_e self.epsilon_e = CellVariable(mesh=self.axial_mesh, value=self.epsilon_e_value) self.epsilon_e_eff_value = numerix.zeros(nx_neg + nx_sep + nx_pos) self.epsilon_e_eff_value[0:nx_neg] = neg_epsilon_e**self.brug self.epsilon_e_eff_value[nx_neg:nx_neg + nx_sep] = sep_epsilon_e**self.brug self.epsilon_e_eff_value[nx_neg + nx_sep:] = pos_epsilon_e**self.brug self.epsilon_e_eff = CellVariable(mesh=self.axial_mesh, value=self.epsilon_e_eff_value) self.a_s_value = numerix.zeros(nx_neg + nx_pos + nx_sep) self.a_s_value[0:nx_neg] = neg_a_s self.a_s_value[nx_neg:nx_neg + nx_sep] = 0.0 self.a_s_value[nx_neg + nx_sep:] = pos_a_s self.a_s = CellVariable(mesh=self.axial_mesh, value=self.a_s_value) self.L_value = numerix.zeros(nx_neg + nx_pos + nx_sep) self.L_value[0:nx_neg], self.L_value[nx_neg:nx_neg + nx_sep] = neg_L, sep_L self.L_value[nx_neg + nx_sep:] = pos_L self.L = CellVariable(mesh=self.axial_mesh, value=self.L_value) self.De_eff_value = numerix.zeros(nx_neg + nx_pos + nx_sep) self.De_eff_value[0:nx_neg], self.De_eff_value[ nx_neg:nx_neg + nx_sep], self.De_eff_value[ nx_neg + nx_sep:] = neg_De_eff, sep_De_eff, pos_De_eff self.De_eff = CellVariable(mesh=self.axial_mesh, value=self.De_eff_value) self.j_battery = CellVariable(mesh=self.axial_mesh, value=j_battery_value)
def _genSigma(self, width=0.1): """Create dependent variable Sigma""" # Gaussian initial condition value = self.mDisk*M/np.sqrt(2*np.pi)/(self.gamma*a*width)*\ np.exp(-0.5*np.square(self.r-1.0)/width**2)/(2*np.pi*self.gamma*self.r*a) # Make it dimensionless value /= self.mDisk*M/(self.gamma*a)**2 idxs = np.where(self.r < 0.1) value[idxs] = 0.0 value = tuple(value) # Create the dependent variable and set the boundary conditions # to zero self.Sigma = CellVariable(name='Surface density', mesh=self.mesh, hasOld=True, value=value)
def initializeDiagnostic(self, variable, funpointer, default=0.0, face_variable=False, output_variable=True): if not face_variable: self.variables[variable] = CellVariable(name=variable, mesh=self.mesh.mesh, value=default) else: self.variables[variable] = FaceVariable(name=variable, mesh=self.mesh.mesh, value=default) self.diagnostic_modules[variable] = DiagnosticModule(funpointer, self) if output_variable: self.variables_store.append(variable) self.diagnostic_update_order.append(variable)
def define_solution_variable(self, existing_solution=None, boundary_source = None): if existing_solution is None: initial_condition_value = self.parameter.IC_value ic_region = self.parameter.IC_region ic_array = copy.deepcopy(self.mesh.cellCenters[0]) xmesh = self.mesh.cellCenters[0] ymesh = self.mesh.cellCenters[1] ic_array[xmesh < ic_region['xmin']] = 0 ic_array[xmesh >= ic_region['xmin']] = initial_condition_value ic_array[xmesh > ic_region['xmax']] = 0 ic_array[ymesh < ic_region['ymin']] = 0 ic_array[ymesh > ic_region['ymax']] = 0 ic_array.mesh = self.mesh else: ic_array = existing_solution # Create solution variable phi = CellVariable(name="solution variable", mesh=self.mesh, value=ic_array) # Edit to add boundary condition if requried. if boundary_source is not 'no flux' or boundary_source is not None: x, y = self.mesh.faceCenters boundary_source_value = self.parameter.boundary_source_value boundary_source_region = self.parameter.boundary_source_region boundary_source_mask = ( (x > boundary_source_region.xmin) & (x < boundary_source_region.xmax) & (y > boundary_source_region.ymin) & (y < boundary_source_region.ymax) ) # phi.faceGrad.constrain(0*self.mesh.faceNormals, self.mesh.exteriorFaces) phi.faceGrad.constrain(boundary_source_value*self.mesh.faceNormals, self.mesh.exteriorFaces & boundary_source_mask) return phi
def __init__(self, tsmesh, time_step_module=None, output_step_module=None, h_initial=0.0, T_initial=None, time_initial=None, proportion_frozen_initial=None, forcing_module=None, thermal_properties=None, bc_inside=None, bc_headwall=None): # T_initial only works if thermal_properties are provided ThawSlump.__init__(self, tsmesh, time_step_module=time_step_module, output_step_module=output_step_module, time_initial=time_initial, forcing_module=forcing_module, thermal_properties=thermal_properties) self._initializeSourcesZero(source_name='S') self._initializeSourcesZero(source_name='S_inside') self._initializeSourcesZero(source_name='S_headwall') # specific volumetric enthalpy self.variables['h'] = CellVariable(name='h', mesh=self.mesh.mesh, value=h_initial, hasOld=True) self.addStoredVariable('h') if T_initial is not None: # essentially overrides h_initial self.initializeEnthalpyTemperature( T_initial, proportion_frozen=proportion_frozen_initial) if (bc_inside is not None and bc_headwall is not None and self.thermal_properties is not None and self.forcing_module is not None): bcc = BoundaryConditionCollection1D(bc_headwall=bc_headwall, bc_inside=bc_inside) self.specifyBoundaryConditions(bcc) self._output_module.storeInitial(self)
def run(self, initialConditions, step_n): pinit = np.zeros( self.nx * self.ny * self.nz) #TODO: how to map from the ABM domain to this form if initialConditions == None: for i in range(self.nx * self.ny * self.nz): pinit[i] = float( decimal.Decimal(random.randrange(8, 50)) / 1000000) #ng/mm3 else: pass print("Initial values:") print(pinit) phi = CellVariable(name="Concentration (ng/ml)", mesh=self.mesh, value=pinit) t = Variable(0.) for step in range(step_n): print("Time = {}".format(t.value)) self.eq.solve(var=phi, dt=self.timeStepDuration) t.setValue(t.value + self.timeStepDuration) return phi.value
## build the mesh dx = L / (nx - 1.5) mesh = Grid1D(nx = nx, dx = dx, communicator=serialComm) ## build the distance variable value = mesh.cellCenters[0] - 1.499 * dx ##distanceVar = DistanceVariable(mesh = mesh, value = dx * (arange(nx) - 0.999)) distanceVar = DistanceVariable(mesh = mesh, value = value, hasOld = 1) ## Build the bulk diffusion equation bulkVar = CellVariable(mesh = mesh, value = cinf) surfactantVar = SurfactantVariable(distanceVar = distanceVar) from .surfactantBulkDiffusionEquation import buildSurfactantBulkDiffusionEquation bulkEqn = buildSurfactantBulkDiffusionEquation(bulkVar, distanceVar = distanceVar, surfactantVar = surfactantVar, diffusionCoeff = diffusion, rateConstant = rateConstant * siteDensity) bulkVar.constrain(cinf, mesh.facesRight) ## Build the surfactant equation surfEqn = AdsorbingSurfactantEquation(surfactantVar = surfactantVar,
#!/usr/bin/env python from fipy import Grid2D, CellVariable, TransientTerm, DiffusionTerm, FixedValue import pylab import sys nx = 300 dx = 0.05 L = nx * dx mesh = Grid2D(dx=dx,dy=dx,nx=nx,ny=nx) x,y = mesh.getCellCenters() x0,y0 = L/2,L/2 X,Y = mesh.getFaceCenters() potential = CellVariable(mesh=mesh, name='potential', value=0.) potential.equation = (DiffusionTerm(coeff = 1.) == 0.) bcs = ( FixedValue(value=5,faces=mesh.getFacesLeft() & (Y<y0) ), FixedValue(value=0,faces=mesh.getFacesRight() & (Y<y0) ), FixedValue(value=2,faces=mesh.getFacesTop() ), ) potential.equation.solve(var=potential, boundaryConditions=bcs) # The follow evaluation of the solution is only # possible with "common" meshes. result = pylab.array(potential) result = result.reshape((nx,nx)) xx,yy = pylab.array(x), pylab.array(y)
from builtins import range from fipy import input from fipy import CellVariable, Tri2D, TransientTerm, ExplicitDiffusionTerm, DefaultSolver, Viewer from fipy.tools import numerix dx = 1. dy = 1. nx = 10 ny = 1 valueLeft = 0. valueRight = 1. timeStepDuration = 0.02 mesh = Tri2D(dx, dy, nx, ny) var = CellVariable(name="concentration", mesh=mesh, value=valueLeft) eq = TransientTerm() == ExplicitDiffusionTerm() solver = DefaultSolver(tolerance=1e-6, iterations=1000) var.constrain(valueLeft, mesh.facesLeft) var.constrain(valueRight, mesh.facesRight) answer = numerix.array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.58508452e-07, 6.84325019e-04, 7.05111362e-02, 7.81376523e-01, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 4.99169535e-05, 1.49682805e-02, 3.82262622e-01, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
Line Loop(1) = {1, 2, 3, 4, 5, 6}; Line Loop(2) = {7, 8, 9, 10}; Line Loop(3) = {11, 12, 13, 14}; Plane Surface(1) = {1, 2, 3}; Plane Surface(2) = {2}; Plane Surface(3) = {3}; Physical Line("Ground") = {4, 5, 6}; Physical Surface("Field") = {1}; Physical Surface("Anode") = {2}; Physical Surface("Cathode") = {3}; """ for refinement in range(10): mesh = Gmsh2D(geo, background=monitor) charge = CellVariable(mesh=mesh, name=r"$\rho$", value=0.) charge.setValue(+1, where=mesh.physicalCells["Anode"]) charge.setValue(-1, where=mesh.physicalCells["Cathode"]) potential = CellVariable(mesh=mesh, name=r"$\psi$") potential.constrain(0., where=mesh.physicalFaces["Ground"]) eq = DiffusionTerm(coeff=1.) == -charge res0 = eq.sweep(var=potential) res = eq.justResidualVector(var=potential) res1 = numerix.L2norm(res) res1a = CellVariable(mesh=mesh, value=abs(res))
def __init__(self, var = None, name = ''): CellVariable.__init__(self, mesh = mesh.fineMesh, name = name) self.var = self._requires(var)
temp_yR = (2 * temp_i[:-2, 1:-1] - 2 * temp_i[1:-1, 1:-1]) / (dx**2) temp_yy = (temp_i[1:-1, 2:] - 2 * temp_i[1:-1, 1:-1] + temp_i[:2, 1:-1]) / (dy**2) temp_xL = (2 * temp_i[1:-1, 2:] - 2 * temp_i[1:-1, 1:-1]) / (dy**2) temp_xR = (2 * temp_i[1:-1, :-2] - 2 * temp_i[1:-1, 1:-1]) / (dy**2) temp_f[1:-1, 1:-1] = temp_xx + temp_yy temp_f[-1, 1:-1] = temp_xR temp_f[0, 1:-1] = temp_xL temp_f[1:-1, 0] = temp_yL return temp_f #%% Phase Field Derivative #%% Phase Field Variable phase = CellVariable(name=r'$\phi$', mesh=mesh, hasOld=True) D_temp = CellVariable(name=r'$\Delta T$', mesh=mesh, hasOld=True) CHANGE_TEMP = 2.25 #%% Heat Equation heatEQ = (TransientTerm() == DiffusionTerm(CHANGE_TEMP) + (phase - phase.old) / D_time) #%% Parameter Setup # ALPHA = 0.015 # Alpha CONSTANT C_ani = 0.02 # Component of (D) the anisotropic diffusion tensor in 2D N = 6. # Symmetry THETA = np.pi / 8 # Orientation psi = THETA + np.arctan2(phase.faceGrad[1], phase.faceGrad[0]) PHI = np.tan(N * psi / 2)
valueLeft = 0. valueRight = 1. meshList = [] RMSNonOrthoList = [] RMSErrorList = [] for i in range(1, 501): meshList = meshList + [ SkewedGrid2D(dx=1.0, dy=1.0, nx=20, ny=20, rand=(0.001 * i)) ] for mesh in meshList: var = CellVariable(name="solution variable", mesh=mesh, value=valueLeft) var.constrain(valueLeft, mesh.facesLeft) var.constrain(valueRight, mesh.facesRight) DiffusionTerm().solve(var) varArray = numerix.array(var) x = mesh.cellCenters[0] analyticalArray = valueLeft + (valueRight - valueLeft) * x / 20 errorArray = varArray - analyticalArray nonOrthoArray = mesh._nonOrthogonality RMSError = (numerix.add.reduce(errorArray * errorArray) / len(errorArray))**0.5 RMSNonOrtho = (numerix.add.reduce(nonOrthoArray * nonOrthoArray) /
from examples.chemotaxis.parameters import parameters from fipy import CellVariable, Grid1D, TransientTerm, DiffusionTerm, ImplicitSourceTerm, Viewer params = parameters['case 2'] nx = 50 dx = 1. L = nx * dx mesh = Grid1D(nx=nx, dx=dx) shift = 1. KMVar = CellVariable(mesh=mesh, value=params['KM'] * shift, hasOld=1) KCVar = CellVariable(mesh=mesh, value=params['KC'] * shift, hasOld=1) TMVar = CellVariable(mesh=mesh, value=params['TM'] * shift, hasOld=1) TCVar = CellVariable(mesh=mesh, value=params['TC'] * shift, hasOld=1) P3Var = CellVariable(mesh=mesh, value=params['P3'] * shift, hasOld=1) P2Var = CellVariable(mesh=mesh, value=params['P2'] * shift, hasOld=1) RVar = CellVariable(mesh=mesh, value=params['R'], hasOld=1) PN = P3Var + P2Var KMscCoeff = params['chiK'] * (RVar + 1) * (1 - KCVar - KMVar.cellVolumeAverage) KMspCoeff = params['lambdaK'] / (1 + PN / params['kappaK']) KMEq = TransientTerm() - KMscCoeff + ImplicitSourceTerm(KMspCoeff) TMscCoeff = params['chiT'] * (1 - TCVar - TMVar.cellVolumeAverage) TMspCoeff = params['lambdaT'] * (KMVar + params['zetaT'])
>>> print var.allclose(analyticalArray) 1 """ __docformat__ = 'restructuredtext' from fipy import Tri2D, CellVariable, DiffusionTerm, Viewer nx = 50 dx = 1. mesh = Tri2D(dx = dx, nx = nx) valueLeft = 0. valueRight = 1. var = CellVariable(name = "solution-variable", mesh = mesh, value = valueLeft) var.constrain(valueLeft, mesh.facesLeft) var.constrain(valueRight, mesh.facesRight) if __name__ == '__main__': DiffusionTerm().solve(var) viewer = Viewer(vars=var) viewer.plot() x = mesh.cellCenters[0] Lx = nx * dx analyticalArray = valueLeft + (valueRight - valueLeft) * x / Lx print var.allclose(analyticalArray) raw_input("finished")
""" from __future__ import print_function from __future__ import division from __future__ import unicode_literals from builtins import input __docformat__ = 'restructuredtext' from fipy import CellVariable, Grid1D, LinearLUSolver, NthOrderBoundaryCondition, DiffusionTerm, Viewer Lx = 1. nx = 100000 dx = Lx / nx mesh = Grid1D(dx = dx, nx = nx) var = CellVariable(mesh = mesh) eq = DiffusionTerm((1.0, 1.0)) BCs = (NthOrderBoundaryCondition(mesh.facesLeft, 0., 0), NthOrderBoundaryCondition(mesh.facesRight, Lx, 0), NthOrderBoundaryCondition(mesh.facesLeft, 0., 2), NthOrderBoundaryCondition(mesh.facesRight, 0., 2)) solver = LinearLUSolver(iterations=10) if __name__ == '__main__': eq.solve(var, boundaryConditions = BCs, solver = solver)
class Yarn1DModel(object): """ Yarn1DModel is a special diffusion model for a single yarn which is composed by a certain amount of fibers. A cross-section of a fiber is generated. The domain is a line from the center of the yarn to the surface. On this line there are some fibers distributed as in the module yarn1Dgrid. Only diffusion processes in a single fiber and yarn are considered. ODE of scipy solve the diffusion process in the layers of DEET and permithrine which are on the fiber An overlap region outside of the yarn can be added for multiscale simulations Fipy solve the transient diffusion problem in the whole domain """ def __init__(self, config): """ a config class must be passed in that contains the required settings """ self.cfg = config self.verbose = self.cfg.get('general.verbose') self.time_period = self.cfg.get('time.time_period') self.delta_t = self.cfg.get('time.dt') self.steps = int((self.time_period*(1.+self.delta_t*1e-6)) // self.delta_t) #set correct delta_t self.delta_t = self.time_period / self.steps if self.verbose: print "Timestep used in yarn1d model:", self.delta_t self.diff_coef = self.cfg.get('diffusion.diffusion_coeff') self.init_conc_func = eval(self.cfg.get('initial.init_conc1d')) self.number_fiber = self.cfg.get('fiber.number_fiber') self.blend = self.cfg.get('fiber.blend') self.blend = [x/100. for x in self.blend] self.nr_models = self.cfg.get('fiber.number_type') assert self.nr_models == len(self.blend) == len(self.cfg.get('fiber.fiber_config')) #Initialize the tortuosity self.tortuosity = self.cfg.get('yarn.tortuosity') #construct the config for the fibers self.cfg_fiber = [] for filename in self.cfg.get('fiber.fiber_config'): if not os.path.isabs(filename): filename = os.path.normpath(os.path.join( os.path.dirname(self.cfg.filename), filename)) self.cfg_fiber.append(FiberConfigManager.get_instance(filename)) #set values from the yarn on this inifile self.cfg_fiber[-1].set("time.time_period", self.time_period) if self.cfg_fiber[-1].get("time.dt") > self.cfg.get("time.dt"): self.cfg_fiber[-1].set("time.dt", self.cfg.get("time.dt")) #we need stepwize solution, we select cvode self.cfg_fiber[-1].set("general.method", 'FVM') self.cfg_fiber[-1].set("general.submethod", 'cvode_step') #we check that boundary is transfer or evaporation bty = self.cfg_fiber[-1].get("boundary.type_right") if bty not in ['evaporation', 'transfer']: raise ValueError, 'Boundary type for a fiber should be evaporation or transfer' if self.verbose: print 'NOTE: Fiber has boundary out of type %s' % bty #set data in case fiber with extension area is used self.cfg_fiber[-1].set("fiber.extenddiff", self.diff_coef/self.tortuosity) #some memory self.step_old_time = None self.step_old_sol = None #use the area function for calculating porosity self.prob_area = eval(self.cfg.get('fiber.prob_area')) # boundary data self.bound_type = conf.BOUND_TYPE[self.cfg.get('boundary.type_right')] self.boundary_conc_out = self.cfg.get('boundary.conc_out') self.boundary_D_out = self.cfg.get('boundary.D_out') self.boundary_dist = self.cfg.get('boundary.dist_conc_out') self.boundary_transf_right = self.cfg.get('boundary.transfer_coef') self.nr_fibers = self.cfg.get('fiber.number_fiber') self.plotevery = self.cfg.get("plot.plotevery") self.writeevery = self.cfg.get("plot.writeevery") #allow a multiscale model to work with a source in overlap zone self.source_overlap = 0. self.initialized = False self.fiberconc_center = 0 self.fiberconc_middle = 0 self.fiberconc_surface = 0 def times(self, timestep, end=None): """ Compute the time at one of our steps If end is given, all times between step timestep and step end are returned as a list, with end included """ if end is None: return timestep * self.delta_t else: begin = timestep * self.delta_t end = end * self.delta_t return np.linspace(begin, end, end-begin + 1) def create_mesh(self): """ Create a mesh for use in the model. We use an equidistant mesh! grid: the space position of each central point for every cell element (r-coordinate); """ self.end_point = self.cfg.get('domain.yarnradius') self.nr_edge = self.cfg.get('domain.n_edge') self.nr_cell = self.nr_edge - 1 self.use_extend = self.cfg.get("domain.useextension") self.fiberlayout_method = self.cfg.get('domain.fiberlayout_method') self.areaextend = 0. if self.use_extend: self.end_extend = self.end_point + \ self.cfg.get('domain.extensionfraction') * self.end_point self.nr_edge_extend = max(2, int(self.nr_edge*self.cfg.get('domain.extensionfraction'))) self.areaextend = np.pi * (self.end_extend**2 - self.end_point**2) else: self.end_extend = self.end_point self.nr_edge_extend = 1 #we now construct the full edge grid self.nr_edge_tot = self.nr_edge + self.nr_edge_extend - 1 self.nr_cell_tot = self.nr_edge_tot - 1 self.grid_edge = np.empty(self.nr_edge_tot, float) self.grid_edge[:self.nr_edge] = np.linspace(0., self.end_point, self.nr_edge) self.grid_edge[self.nr_edge:] = np.linspace(self.end_point, self.end_extend, self.nr_edge_extend)[1:] #construct cell centers from this self.grid = (self.grid_edge[:-1] + self.grid_edge[1:])/2. #obtain cell sizes self.delta_r = self.grid_edge[1:] - self.grid_edge[:-1] grid_square = np.power(self.grid_edge, 2) self.delta_rsquare = grid_square[1:] - grid_square[:-1] #create fiber models as needed: one per fibertype and per cell in the yarn model self.fiber_models = [0] * (self.nr_edge - 1) self.fiber_mass = np.empty((self.nr_edge - 1, self.nr_models), float) self.source_mass = np.empty((self.nr_edge - 1, self.nr_models), float) self.source_conc = np.empty((self.nr_edge - 1, self.nr_models), float) self.source = np.zeros(self.nr_edge_tot - 1, float) for ind in range(self.nr_edge-1): self.fiber_models[ind] = [] for cfg in self.cfg_fiber: cfg.set("fiber.extendinit_conc", self.init_conc_func(self.grid[ind])) self.fiber_models[ind].append(FiberModel(cfg)) #calculate the porosity as n=(pi Ry^2-nr_fibers pi Rf^2) / pi Ry^2 #porosity in the yarn self.porosity = np.ones(self.nr_cell_tot, float) self.volfracfib = [] # volume fraction of the fiber types if self.fiberlayout_method == 'virtlocoverlap': value_from_areafunction = np.zeros(self.nr_cell, float) for i_porosity in range(len(self.prob_area)): function_area = self.prob_area[i_porosity] value_from_areafunction += function_area(self.grid[:]) #plot the value from the function and the porosity value to check #the calculation for porosity plt.figure() plt.plot(self.grid[:], 1 - value_from_areafunction, '-', color = 'red') plt.plot(self.grid[:], 1 - function_area(self.grid[:]), '*') plt.xlabel('Yarn domain') plt.ylabel('value') plt.ylim(0., 1.0) plt.show() self.porosity[:self.nr_cell] = 1. - value_from_areafunction[:self.nr_cell] else: for blend, model in zip(self.blend, self.fiber_models[0]): print 'fiberradius', model.radius(), 'yarnradius', self.end_point self.volfracfib.append( blend * self.nr_fibers * np.power(model.radius(), 2) / np.power(self.end_point, 2) ) if np.sum(self.volfracfib) > 1: raise ValueError, 'porosity negative, unrealistic number of fibers in yarn cross section, %f fibers per yarn * Rf^2/Ry^2 = %f' % (self.nr_fibers,np.sum(self.volfracfib)) raw_input() self.porosity[:self.nr_cell] = 1 - np.sum(self.volfracfib) #print 'porosity in yarn', self.porosity[:self.nr_cell], #nrf is number of fibers in the shell at that grid position # per radial if not self.fiberlayout_method == 'virtlocoverlap': self.nrf_shell = (self.delta_rsquare[:self.nr_cell] / (self.end_point**2) * self.nr_fibers) else: raise NotImplementedError, 'nrfibers per shell still to determine' #we now have porosity and fiber models, we can calculate area extend available for ind in range(self.nr_edge-1): for fibmod in self.fiber_models[ind]: area_extend = np.pi * self.delta_rsquare[ind] \ * self.porosity[ind] / self.nrf_shell[ind] fibmod.set_areaextend(area_extend) # set fiber.extendarea in the fiber model! #create cylindrical 1D grid over domain for using fipy to view. if self.plotevery: self.mesh_yarn = CylindricalGrid1D(dr=tuple(self.delta_r[:self.nr_cell])) self.mesh_yarn.periodicBC = False self.mesh_yarn = self.mesh_yarn #print 'mesh yarn', self.grid_edge, ', delta_r yarn', self.delta_r def initial_yarn1d(self): """ initial concentration over the domain""" self.init_conc = np.empty(self.nr_cell_tot, float) #zero to the outside for ind, r in enumerate(self.grid[:self.nr_cell]): self.init_conc[ind] = self.init_conc_func(r) self.init_conc[self.nr_cell:] = self.boundary_conc_out def get_data(self, cellnr): index = cellnr return index def out_conc(self, data, t): """ return the concentration of compound in the void zone of cell cellnr at time t """ timenowyarn = self.step_old_time if t >= timenowyarn: #return data return self.step_old_sol[data] raise ValueError, 'out concentration should only be requested at a later time' def solve_fiber_init(self): """ Solve the diffusion process for a repellent on the fiber at radial position r in the yarn. &C/&t = 1/r * &(Dr&C/&r) / &r The diffusion coefficient is constant. The finite volume method is used to discretize the right side of equation. The mesh in this 1-D condition is uniform. """ for ind, models in enumerate(self.fiber_models): for type, model in enumerate(models): model.run_init() model.solve_init() #rebind the out_conc method to a call to yarn1d if model.use_extend: model.set_outconc(self.out_conc(ind, 0)) else: model.set_userdata(self.get_data(ind)) model.out_conc = lambda t, data: self.out_conc(data, t) self.fiber_mass[ind, type] = model.calc_mass(model.initial_c1) self.fiberconc_center = model.initial_c1[0] n = int((model.tot_edges_no_extend-2)/2) self.fiberconc_middle = model.initial_c1[n] self.fiberconc_surface = model.initial_c1[model.tot_edges_no_extend-2] def do_fiber_step(self, stoptime): """ Solve the diffusion process on the fiber up to stoptime, starting from where we where last. The flux is the BC: S*h(C_equi - C_yarn(t))*H(C-C_b,C_equi-C_yarn(t)) """ for ind, models in enumerate(self.fiber_models): for type, model in enumerate(models): if model.use_extend: model.set_outconc(self.out_conc(ind, self.step_old_time)) time, result = model.do_step(stoptime, needreinit=True) self.fiberconc_center = result[0] n = int((model.tot_edges_no_extend-2)/2) self.fiberconc_middle = result[n] self.fiberconc_surface = result[model.tot_edges_no_extend-2] #filedata= open(utils.OUTPUTDIR + os.sep + "fiberconc_%05d" %stoptime + ".txt",'w') #filedata.write("conc on %.10f is %s" % (stoptime,result)) #filedata.close() tmp = model.calc_mass(result) self.source_mass[ind, type] = self.fiber_mass[ind, type] - tmp self.fiber_mass[ind, type] = tmp def get_fiber_conc(self): """ method for reading fiberconcentrations from roommodel for last computed fiber in the yarn """ return self.fiberconc_center, self.fiberconc_middle, self.fiberconc_surface def _set_bound_flux(self, flux_edge, conc_r): """ Method that takes BC into account to set flux on edge flux here is the flux per radial Data is written to flux_edge, conc_r contains solution in the cell centers """ flux_edge[0] = 0. #avergage porosity on edge: if self.use_extend: porright = (self.porosity[self.nr_cell-1] + self.porosity[self.nr_cell]) / 2 else: porright = (self.porosity[self.nr_cell-1] + 1.) / 2 diffright = (self.diff_coef/self.tortuosity + self.boundary_D_out)/ 2 if self.bound_type == conf.TRANSFER: # tranfer flux, in x: flux_x = tf * C, so radially per radial a flux # flux_radial = 2 Pi * radius * flux_x / 2 * Pi flux_edge[self.nr_edge-1] = self.boundary_transf_right * porright \ * conc_r[self.nr_cell-1] * self.grid_edge[self.nr_edge-1] elif self.bound_type == conf.DIFF_FLUX: # diffusive flux with the outside # flux radial = - D_out * (conc_out - yarn_edge_conc)/dist_conc_out * radius if self.use_extend: conright = conc_r[self.nr_cell] bcdist = (self.delta_r[self.nr_cell-1] + self.delta_r[self.nr_cell]) / 2. else: conright = self.boundary_conc_out bcdist = self.boundary_dist flux_edge[self.nr_edge-1] = -(diffright * porright * (conright - conc_r[self.nr_cell-1]) / bcdist * self.grid_edge[self.nr_edge-1]) if self.use_extend: #zero flux at right boundary flux_edge[-1] = 0. def calc_mass(self, conc): """ calculate current amount of mass of volatile based on data currently stored """ #first we calculate the mass in the void space: mass = np.sum(conc[:self.nr_cell] * (np.power(self.grid_edge[1:self.nr_edge], 2) - np.power(self.grid_edge[:self.nr_edge-1], 2)) * self.porosity[:self.nr_cell] ) * np.pi ## print "mass in void space yarn", mass #now we add the mass in the fibers for ind, pos in enumerate(self.grid[:self.nr_cell]): for type, blend in enumerate(self.blend): #nrf_shell is number of fibers of blend in the shell at that grid position massfib = (self.fiber_mass[ind, type] * self.nrf_shell[ind] * blend) #print 'mass fiber', self.fiber_mass[ind,type], 'nr fibers per shell', self.nrf_shell[ind] mass += massfib #print 'yarn conc', conc #print 'yarn totalmass', mass, 'microgram' return mass def calc_mass_overlap(self, conc): """ calculate current amount of mass of volatile in the overlap region based on data currently stored. From mol/microm^2 to mol """ #we calculate the mass in the void space: mass = np.sum(conc[self.nr_cell:] * (np.power(self.grid_edge[self.nr_edge:], 2) - np.power(self.grid_edge[self.nr_edge-1:-1], 2)) * self.porosity[self.nr_cell:] ) * np.pi #print "porosity",self.porosity ##print 'yarn mass overlap', conc[self.nr_cell:], mass return mass def set_source(self, timestep): ## we calculated the source_mass from a fiber, using upscaling via volume averaging technique: ## source conc = nrf_shell * conc_r * \int_{r_{i+1}}^r_i rdr / V = nrf_shell * conc_r / (2*\pi) ## source mass = source conc * V = nrf_shell * conc_r * (r_{i+1}^2-r_i^2)*pi / 2*pi """ Method to calculate the radial source term Per radial we have the global equation \partial_t (n r C) = \partial_r (D/tau) r \partial_r (n C) + r Source where Source is mass per time per volume released/absorbed by the fibers So Source = nrf_shell * mass_source_fiber / (V \Delta t) This equation is integrated over a shell and devided by n (the porosity), and we determine n d_t w, with w = rC, where the sourceterm is \int_{r_i}^{r_{i+1}} r Source dr and is the term here calculated and stored in self.source As we assume Source constant over a shell by averaging out the mass over the area of void space of a shell (nV), we have Source = nrf_shell * mass_source_fiber/(nV \Delta t) * \Delta r_i^2 / 2 self.source_mass contains per shell how much mass was released in previous step by one fiber. Suppose this mass is M. We determine how many fibers there are radially, multiply this with M and divide by volume V * porosity n \delta t to obtain Source-concentration, since concentration is mass/volume time. Afterwards we multiply this Source with \Delta r_i^2 / (2 n \Delta r) coming from the integration (int n d_t w gives the term n \Delta r). """ for ind, pos in enumerate(self.grid_edge[:self.nr_cell]): self.source[ind] = 0. #V is the area of the shell V = np.pi*((pos+self.delta_r[ind])**2-pos**2) for type, blend in enumerate(self.blend): #nrf_shell is number of fibers of blend in the shell at that grid position # per radial # self.source_mass is the mass coming out of one fiber, we need a concentration # so,nrf_shell * self.source_conc = (nrf_shell * self.source_mass) / (porosity*V_shell) self.source_conc[ind,type] = (self.source_mass[ind, type] / (self.porosity[ind]*V) ) self.source[ind] += (self.source_conc[ind, type] * self.nrf_shell[ind] * blend) self.source[ind] /= timestep ## Note: source must be per second, so divided by the timestep def f_conc1_ode(self, t, conc_r, diff_u_t): """ Solving the radial yarn 1D diffusion equation: n \partial_t (rC) = \partial_r (D/tau r n \partial_r C) + Source * r with Source the conc amount per time unit added at r. Solution is obtained by integration over a cell, so n \delta r d_t (r C) = flux_right - flux_left + Source (\delta r^2 /2) so n d_t C = 1 / (r \delta r) * (flux_right - flux_left + Source (\delta r^2 /2) ) """ grid = self.grid #Initialize the flux rate on the edges flux_edge = self.__tmp_flux_edge #set flux on edge 0, self.nr_edge-1 and -1 self._set_bound_flux(flux_edge, conc_r) #calculate flux rate in each edge of the domain flux_edge[1:self.nr_edge-1] = -(2 * (self.diff_coef/self.tortuosity) * self.grid_edge[1:self.nr_edge-1] * (conc_r[1:self.nr_cell]-conc_r[:self.nr_cell-1]) /(self.delta_r[:self.nr_cell-1]+self.delta_r[1:self.nr_cell]) * (self.porosity[:self.nr_cell-1] + self.porosity[1:self.nr_cell])/2 ) if self.use_extend: # diffusion in the outside region flux_edge[self.nr_edge:-1] = -(2 * self.boundary_D_out * self.grid_edge[self.nr_edge:-1] * (conc_r[self.nr_cell+1:]-conc_r[self.nr_cell:-1]) /(self.delta_r[self.nr_cell:-1]+self.delta_r[self.nr_cell+1:]) * (self.porosity[self.nr_cell:-1] + self.porosity[self.nr_cell+1:])/2 ) diff_u_t[:] = ((flux_edge[:-1]-flux_edge[1:]) / self.delta_r[:]/ self.porosity[:] + self.source[:] * self.delta_rsquare / 2 / self.delta_r ) if self.use_extend and self.source_overlap: #porosity assumed 1 in extend! diff_u_t[self.nr_cell:] += (self.source_overlap * self.delta_rsquare[self.nr_cell:] / 2 / self.delta_r[self.nr_cell:]) diff_u_t[:] = diff_u_t[:] / self.grid[:] # still division by r to move from w to C def solve_ode_init(self): """ Initialize the ode solver """ self.initial_t = 0. self.step_old_time = self.initial_t n_cells = len(self.init_conc) self.ret_y = np.empty(n_cells, float) self.__tmp_flux_edge = np.empty(n_cells+1, float) self.tstep = 0 self.step_old_sol = np.empty(n_cells, float) self.step_old_sol[:] = self.init_conc[:] self.solver = sc_ode('cvode', self.f_conc1_ode, min_step_size=1e-9, first_step_size=1e-16, rtol=1e-6, atol=1e-10, max_steps=50000, lband=1, uband=1) self.solver.init_step(self.step_old_time, self.init_conc) self.initialized = True def do_ode_step(self, stoptime): """Solve the yarnmodel up to stoptime, continuing from the present state, return the time, concentration after step """ #fix where next to stop self.solver.set_options(tstop=stoptime) if not self.initialized: raise Exception, 'Solver ode not initialized' #solve the problem flag, realtime = self.solver.step(stoptime, self.ret_y) if flag < 0: raise Exception, 'could not find solution, flag %d' % flag assert np.allclose(realtime, stoptime), "%f %f" % (realtime, stoptime) return stoptime, self.ret_y def do_yarn_init(self): """ generic initialization needed before yarn can be solved """ self.create_mesh() self.initial_yarn1d() if not self.initialized: self.solve_ode_init() self.solve_fiber_init() def do_yarn_step(self, stoptime): """ Solve yarn up to time t. This does: 1. solve the fiber up to t 2. set correct source term for the yarn 3. solve the yarn up to t """ compute = True #even is step is large, we don't compute for a longer time than delta_t t = self.step_old_time while compute: t += self.delta_t if t >= stoptime - self.delta_t/100.: t = stoptime compute = False self.do_fiber_step(t) self.set_source(t-self.step_old_time) #we need to reinit as rhs changed if REINIT_ALWAYS: self.solver.init_step(self.step_old_time, self.step_old_sol) realtime, self.step_old_sol = self.do_ode_step(t) self.tstep += 1 self.step_old_time = t # filedata= open(utils.OUTPUTDIR + os.sep + "fiberconccenter" + ".txt",'w') # for i in range(0,len(self.fiberconc_center)): # filedata.write("%.5f %.5f\n" % (self.fiberconc_center[i,0],self.fiberconc_center[i,1])) # filedata.close() # filedata= open(utils.OUTPUTDIR + os.sep + "fiberconcmiddle" + ".txt",'w') # for i in range(0,len(self.fiberconc_middle)): # filedata.write("%.5f %.5f\n" % (self.fiberconc_middle[i,0],self.fiberconc_middle[i,1])) # filedata.close() # filedata= open(utils.OUTPUTDIR + os.sep + "fiberconcsurface" + ".txt",'w') # for i in range(0,len(self.fiberconc_surface)): # filedata.write("%.5f %.5f\n" % (self.fiberconc_surface[i,0],self.fiberconc_surface[i,1])) # filedata.close() return realtime, self.step_old_sol def view_sol(self, times, conc): """ Show the solution in conc with times. conc[i][:] contains solution at time times[i] """ if self.plotevery: self.solution_view = CellVariable(name="Yarn radial concentration", mesh=self.mesh_yarn, value=conc[0][:self.nr_cell]) if isinstance(conc, np.ndarray): maxv = conc.max() * 1.2 minv = conc.min() * 0.9 else: maxv = np.max(conc) * 1.2 minv = np.min(conc) * 0.9 self.viewer = Matplotlib1DViewer(vars=self.solution_view, datamin=minv, datamax=maxv) self.viewerplotcount = 0 self.viewerwritecount = 0 for time, con in zip(times, conc): self.solution_view.setValue(con[:self.nr_cell]) if self.viewerplotcount == 0 or (self.writeevery and self.viewerwritecount == 0): self.viewer.axes.set_title('time %s' %str(time)) if self.writeevery and self.viewerwritecount == 0: #plot and savefig self.viewer.plot(filename=utils.OUTPUTDIR + os.sep \ + 'yarnconc%08.4f.png' % time) else: #only plot self.viewer.plot() self.viewerplotcount += 1 self.viewerplotcount = self.viewerplotcount % self.plotevery if self.writeevery: self.viewerwritecount += 1 self.viewerwritecount = self.viewerwritecount % self.writeevery def run(self, wait=False): self.do_yarn_init() #data storage, will lead to memerror if many times ! n_cells = len(self.init_conc) self.conc1 = np.empty((self.steps+1, n_cells), float) self.conc1[0][:] = self.init_conc[:] print 'Start mass of DEET per grid cell per fiber type' for ind, masses in enumerate(self.fiber_mass): print 'cell', ind, for mass in masses: print mass, ' - ', print ' ' mc1 = self.calc_mass(self.step_old_sol) mc2 = self.calc_mass_overlap(self.step_old_sol) print 'Total mass in yarn', mc1, ', mass in overlap zone:', mc2, \ 'Sum', mc1 + mc2 tstep = 0 t = self.times(tstep+1) while t <= self.time_period+self.delta_t/10: tstep += 1 #print 'solving t', t rt, rety = self.do_yarn_step(t) self.conc1[self.tstep][:] = self.ret_y[:] t = self.times(tstep+1) print 'Final mass of DEET per grid cell per fiber type' for ind, masses in enumerate(self.fiber_mass): print 'cell', ind, for mass in masses: print mass, ' - ', print ' ' mc1 = self.calc_mass(self.step_old_sol) mc2 = self.calc_mass_overlap(self.step_old_sol) print 'Total mass in yarn', mc1, ', mass in overlap zone:', mc2, \ 'Sum', mc1 + mc2 self.view_sol(self.times(0,self.steps), self.conc1) if wait: raw_input("Finished yarn1d run")
def runSimpleTrenchSystem(faradaysConstant=9.6e4, gasConstant=8.314, transferCoefficient=0.5, rateConstant0=1.76, rateConstant3=-245e-6, catalystDiffusion=1e-9, siteDensity=9.8e-6, molarVolume=7.1e-6, charge=2, metalDiffusion=5.6e-10, temperature=298., overpotential=-0.3, metalConcentration=250., catalystConcentration=5e-3, catalystCoverage=0., currentDensity0=0.26, currentDensity1=45., cellSize=0.1e-7, trenchDepth=0.5e-6, aspectRatio=2., trenchSpacing=0.6e-6, boundaryLayerDepth=0.3e-6, numberOfSteps=5, displayViewers=True): cflNumber = 0.2 numberOfCellsInNarrowBand = 10 cellsBelowTrench = 10 yCells = cellsBelowTrench \ + int((trenchDepth + boundaryLayerDepth) / cellSize) xCells = int(trenchSpacing / 2 / cellSize) from fipy.tools import serialComm mesh = Grid2D(dx=cellSize, dy=cellSize, nx=xCells, ny=yCells, communicator=serialComm) narrowBandWidth = numberOfCellsInNarrowBand * cellSize distanceVar = DistanceVariable(name='distance variable', mesh=mesh, value=-1., hasOld=1) bottomHeight = cellsBelowTrench * cellSize trenchHeight = bottomHeight + trenchDepth trenchWidth = trenchDepth / aspectRatio sideWidth = (trenchSpacing - trenchWidth) / 2 x, y = mesh.cellCenters distanceVar.setValue(1., where=(y > trenchHeight) | ((y > bottomHeight) & (x < xCells * cellSize - sideWidth))) distanceVar.calcDistanceFunction(order=2) catalystVar = SurfactantVariable(name="catalyst variable", value=catalystCoverage, distanceVar=distanceVar) bulkCatalystVar = CellVariable(name='bulk catalyst variable', mesh=mesh, value=catalystConcentration) metalVar = CellVariable(name='metal variable', mesh=mesh, value=metalConcentration) expoConstant = -transferCoefficient * faradaysConstant \ / (gasConstant * temperature) tmp = currentDensity1 * catalystVar.interfaceVar exchangeCurrentDensity = currentDensity0 + tmp expo = numerix.exp(expoConstant * overpotential) currentDensity = expo * exchangeCurrentDensity * metalVar \ / metalConcentration depositionRateVariable = currentDensity * molarVolume \ / (charge * faradaysConstant) extensionVelocityVariable = CellVariable(name='extension velocity', mesh=mesh, value=depositionRateVariable) surfactantEquation = AdsorbingSurfactantEquation( surfactantVar=catalystVar, distanceVar=distanceVar, bulkVar=bulkCatalystVar, rateConstant=rateConstant0 + rateConstant3 * overpotential**3) advectionEquation = TransientTerm() + AdvectionTerm( extensionVelocityVariable) metalEquation = buildMetalIonDiffusionEquation( ionVar=metalVar, distanceVar=distanceVar, depositionRate=depositionRateVariable, diffusionCoeff=metalDiffusion, metalIonMolarVolume=molarVolume, ) metalVar.constrain(metalConcentration, mesh.facesTop) from surfactantBulkDiffusionEquation import buildSurfactantBulkDiffusionEquation bulkCatalystEquation = buildSurfactantBulkDiffusionEquation( bulkVar=bulkCatalystVar, distanceVar=distanceVar, surfactantVar=catalystVar, diffusionCoeff=catalystDiffusion, rateConstant=rateConstant0 * siteDensity) bulkCatalystVar.constrain(catalystConcentration, mesh.facesTop) if displayViewers: try: from mayaviSurfactantViewer import MayaviSurfactantViewer viewer = MayaviSurfactantViewer(distanceVar, catalystVar.interfaceVar, zoomFactor=1e6, datamax=0.5, datamin=0.0, smooth=1, title='catalyst coverage') except: viewer = MultiViewer( viewers=(Viewer(distanceVar, datamin=-1e-9, datamax=1e-9), Viewer(catalystVar.interfaceVar))) else: viewer = None levelSetUpdateFrequency = int(0.8 * narrowBandWidth \ / (cellSize * cflNumber * 2)) for step in range(numberOfSteps): if step > 5 and step % 5 == 0 and viewer is not None: viewer.plot() if step % levelSetUpdateFrequency == 0: distanceVar.calcDistanceFunction(order=2) extensionVelocityVariable.setValue(depositionRateVariable()) distanceVar.updateOld() distanceVar.extendVariable(extensionVelocityVariable, order=2) dt = cflNumber * cellSize / extensionVelocityVariable.max() advectionEquation.solve(distanceVar, dt=dt) surfactantEquation.solve(catalystVar, dt=dt) metalEquation.solve(metalVar, dt=dt) bulkCatalystEquation.solve(bulkCatalystVar, dt=dt, solver=GeneralSolver(tolerance=1e-15, iterations=2000)) try: import os filepath = os.path.splitext(__file__)[0] + '.gz' print catalystVar.allclose(numerix.loadtxt(filepath), rtol=1e-4) except: return 0
from fipy.solvers import * from parameters import * # ----------------- Solver -------------------------------- mySolver = LinearGMRESSolver(iterations=1000, tolerance=1.0e-6) # ----------------- Mesh Generation ----------------------- nx = 100 L = 5.0 mesh = Grid1D(nx=nx, Lx=L) x = mesh.cellCenters[0] # Cell position # ----------------- Variable Declarations ----------------- density = CellVariable(name=r"$n$", mesh=mesh, hasOld=True) temperature = CellVariable(name=r"$T$", mesh=mesh, hasOld=True) Z = CellVariable(name=r"$Z$", mesh=mesh, hasOld=True) Diffusivity = CellVariable(name=r"$D$", mesh=mesh, hasOld=True) # ----------- Initial Conditions of Z --------------------- Z0L = 1.0 # L--mode Z0H = Z_S * (1.0 - numerix.tanh((L * x - L) / 2.0)) # H--mode Z.setValue(Z0H) # ----------------- Diffusivities ------------------------- # Itohs'/Zohm's model D_Zohm = (D_max + D_min) / 2.0 + ((D_max - D_min) * numerix.tanh(Z)) / 2.0 # Stap's Model alpha_sup = 0.5
valueRight = 0. L = 10. nx = 400 dx = L / nx cfl = 0.01 velocity = 1. timeStepDuration = cfl * dx / abs(velocity) steps = 1000 mesh = Grid1D(dx = dx, nx = nx) startingArray = numerix.zeros(nx, 'd') startingArray[50:90] = 1. var = CellVariable( name = "advection variable", mesh = mesh, value = startingArray) var.constrain(valueLeft, mesh.facesLeft) var.constrain(valueRight, mesh.facesRight) eq = TransientTerm() - PowerLawConvectionTerm(coeff = (velocity,)) if __name__ == '__main__': viewer = Viewer(vars=(var,)) viewer.plot() raw_input("press key to continue") for step in range(steps): eq.solve(var, dt = timeStepDuration,
del logLevel, logFilename R_outer = 2e-6 #SETTING must match what is in .geo file R_inner = 1e-6 #SETTING must match what is in .geo file cellSize = .05 * (R_outer - R_inner) #SETTING must match what is in .geo file #run gmsh gui to produce this mesh (.msh file) #gmsh infiniteCylinder01.geo filename = 'infiniteCylinder01.msh' mesh = Gmsh2D(filename, communicator=serialComm) del filename T_initial = 425.08 #deg K T_infinity = 293.15 #deg K #SETTING var = CellVariable(mesh=mesh, value=T_initial - T_infinity) #let var be T-T_infinity rho = 6980. #kg/m^3 cp = 227. #J/kg/K k = 59.6 #W/m/K D_thermal = k / rho / cp X_faces, Y_faces = mesh.faceCenters surfaceFaces = (Y_faces > 0) & ( (X_faces**2 + Y_faces**2)**.5 > R_outer - cellSize / 10.) #convectionCoeff=200. #W/m^2/K Bi_desired = 10. #SETTING convectionCoeff = Bi_desired * k / R_inner logging.info('convection coefficient is %.2E' % convectionCoeff)
def runSimpleTrenchSystem(faradaysConstant=9.6e4, gasConstant=8.314, transferCoefficient=0.5, rateConstant0=1.76, rateConstant3=-245e-6, catalystDiffusion=1e-9, siteDensity=9.8e-6, molarVolume=7.1e-6, charge=2, metalDiffusion=5.6e-10, temperature=298., overpotential=-0.3, metalConcentration=250., catalystConcentration=5e-3, catalystCoverage=0., currentDensity0=0.26, currentDensity1=45., cellSize=0.1e-7, trenchDepth=0.5e-6, aspectRatio=2., trenchSpacing=0.6e-6, boundaryLayerDepth=0.3e-6, numberOfSteps=5, displayViewers=True): cflNumber = 0.2 numberOfCellsInNarrowBand = 10 cellsBelowTrench = 10 yCells = cellsBelowTrench \ + int((trenchDepth + boundaryLayerDepth) / cellSize) xCells = int(trenchSpacing / 2 / cellSize) from fipy.tools import serialComm mesh = Grid2D(dx = cellSize, dy = cellSize, nx = xCells, ny = yCells, communicator=serialComm) narrowBandWidth = numberOfCellsInNarrowBand * cellSize distanceVar = DistanceVariable( name = 'distance variable', mesh = mesh, value = -1., hasOld = 1) bottomHeight = cellsBelowTrench * cellSize trenchHeight = bottomHeight + trenchDepth trenchWidth = trenchDepth / aspectRatio sideWidth = (trenchSpacing - trenchWidth) / 2 x, y = mesh.cellCenters distanceVar.setValue(1., where=(y > trenchHeight) | ((y > bottomHeight) & (x < xCells * cellSize - sideWidth))) distanceVar.calcDistanceFunction(order=2) catalystVar = SurfactantVariable( name = "catalyst variable", value = catalystCoverage, distanceVar = distanceVar) bulkCatalystVar = CellVariable( name = 'bulk catalyst variable', mesh = mesh, value = catalystConcentration) metalVar = CellVariable( name = 'metal variable', mesh = mesh, value = metalConcentration) expoConstant = -transferCoefficient * faradaysConstant / (gasConstant * temperature) tmp = currentDensity1 * catalystVar.interfaceVar exchangeCurrentDensity = currentDensity0 + tmp expo = numerix.exp(expoConstant * overpotential) currentDensity = expo * exchangeCurrentDensity * metalVar / metalConcentration depositionRateVariable = currentDensity * molarVolume / (charge * faradaysConstant) extensionVelocityVariable = CellVariable( name = 'extension velocity', mesh = mesh, value = depositionRateVariable) surfactantEquation = AdsorbingSurfactantEquation( surfactantVar = catalystVar, distanceVar = distanceVar, bulkVar = bulkCatalystVar, rateConstant = rateConstant0 + rateConstant3 * overpotential**3) advectionEquation = TransientTerm() + AdvectionTerm(extensionVelocityVariable) metalEquation = buildMetalIonDiffusionEquation( ionVar = metalVar, distanceVar = distanceVar, depositionRate = depositionRateVariable, diffusionCoeff = metalDiffusion, metalIonMolarVolume = molarVolume, ) metalVar.constrain(metalConcentration, mesh.facesTop) from .surfactantBulkDiffusionEquation import buildSurfactantBulkDiffusionEquation bulkCatalystEquation = buildSurfactantBulkDiffusionEquation( bulkVar = bulkCatalystVar, distanceVar = distanceVar, surfactantVar = catalystVar, diffusionCoeff = catalystDiffusion, rateConstant = rateConstant0 * siteDensity ) bulkCatalystVar.constrain(catalystConcentration, mesh.facesTop) if displayViewers: try: from .mayaviSurfactantViewer import MayaviSurfactantViewer viewer = MayaviSurfactantViewer(distanceVar, catalystVar.interfaceVar, zoomFactor = 1e6, datamax=0.5, datamin=0.0, smooth = 1, title = 'catalyst coverage') except: viewer = MultiViewer(viewers=( Viewer(distanceVar, datamin=-1e-9, datamax=1e-9), Viewer(catalystVar.interfaceVar))) else: viewer = None levelSetUpdateFrequency = int(0.8 * narrowBandWidth \ / (cellSize * cflNumber * 2)) for step in range(numberOfSteps): if step>5 and step % 5 == 0 and viewer is not None: viewer.plot() if step % levelSetUpdateFrequency == 0: distanceVar.calcDistanceFunction(order=2) extensionVelocityVariable.setValue(depositionRateVariable()) distanceVar.updateOld() distanceVar.extendVariable(extensionVelocityVariable, order=2) dt = cflNumber * cellSize / extensionVelocityVariable.max() advectionEquation.solve(distanceVar, dt = dt) surfactantEquation.solve(catalystVar, dt = dt) metalEquation.solve(metalVar, dt = dt) bulkCatalystEquation.solve(bulkCatalystVar, dt = dt, solver=GeneralSolver(tolerance=1e-15, iterations=2000)) try: import os filepath = os.path.splitext(__file__)[0] + '.gz' print(catalystVar.allclose(numerix.loadtxt(filepath), rtol = 1e-4)) except: return 0
"""Initial potential""" return a1 * np.sin(b1 * x + c1) + a2 * np.sin(b2 * x + c2) mesh = Grid1D(dx=dx, nx=nx) #Establish mesh in how many dimensions necessary ############################################################################## #################''' SETUP CELLVARIABLES AND EQUATIONS '''#################### ############################################################################## #CellVariable - defines the variables that you want to solve for: '''Initial value can be established when defining the variable, or later using 'var.value =' Value defaults to zero if not defined''' Pion = CellVariable(mesh=mesh, name='Positive ion Charge Density', value=y01(x)) Nion = CellVariable(mesh=mesh, name='Negative ion Charge Density', value=y02(x)) potential = CellVariable(mesh=mesh, name='Potential', value=y03(x)) #EQUATION SETUP BASIC DESCRIPTION '''Equations to solve for each varible must be defined: -TransientTerm = dvar/dt -ConvectionTerm = dvar/dx -DiffusionTerm = d^2var/dx^2 -Source terms can be described as they would appear mathematically Notes: coeff = terms that are multiplied by the Term.. must be rank-1 FaceVariable for ConvectionTerm
from builtins import input if __name__ == '__main__': import sys import os from fipy import SkewedGrid2D, CellVariable, Viewer, DiffusionTerm from fipy.tools import numerix valueLeft = 0. valueRight = 1. mesh = SkewedGrid2D(dx = 1.0, dy = 1.0, nx = 20, ny = 20, rand = 0.1) var = CellVariable(name = "solution variable", mesh = mesh, value = valueLeft) viewer = Viewer(vars = var) var.constrain(valueLeft, mesh.facesLeft) var.constrain(valueRight, mesh.facesRight) DiffusionTerm().solve(var) varArray = numerix.array(var) x = mesh.cellCenters[0] analyticalArray = valueLeft + (valueRight - valueLeft) * x / 20 errorArray = varArray - analyticalArray errorVar = CellVariable(name = 'absolute error', mesh = mesh,
def runGold(faradaysConstant=9.6e4, consumptionRateConstant=2.6e+6, molarVolume=10.21e-6, charge=1.0, metalDiffusion=1.7e-9, metalConcentration=20.0, catalystCoverage=0.15, currentDensity0=3e-2 * 16, currentDensity1=6.5e-1 * 16, cellSize=0.1e-7, trenchDepth=0.2e-6, aspectRatio=1.47, trenchSpacing=0.5e-6, boundaryLayerDepth=90.0e-6, numberOfSteps=10, taperAngle=6.0, displayViewers=True): cflNumber = 0.2 numberOfCellsInNarrowBand = 20 mesh = TrenchMesh(cellSize = cellSize, trenchSpacing = trenchSpacing, trenchDepth = trenchDepth, boundaryLayerDepth = boundaryLayerDepth, aspectRatio = aspectRatio, angle = numerix.pi * taperAngle / 180.) narrowBandWidth = numberOfCellsInNarrowBand * cellSize distanceVar = GapFillDistanceVariable( name = 'distance variable', mesh = mesh, value = -1.) distanceVar.setValue(1., where=mesh.electrolyteMask) distanceVar.calcDistanceFunction() catalystVar = SurfactantVariable( name = "catalyst variable", value = catalystCoverage, distanceVar = distanceVar) metalVar = CellVariable( name = 'metal variable', mesh = mesh, value = metalConcentration) exchangeCurrentDensity = currentDensity0 + currentDensity1 * catalystVar.interfaceVar currentDensity = metalVar / metalConcentration * exchangeCurrentDensity depositionRateVariable = currentDensity * molarVolume / charge / faradaysConstant extensionVelocityVariable = CellVariable( name = 'extension velocity', mesh = mesh, value = depositionRateVariable) catalystSurfactantEquation = AdsorbingSurfactantEquation( catalystVar, distanceVar = distanceVar, bulkVar = 0, rateConstant = 0, consumptionCoeff = consumptionRateConstant * extensionVelocityVariable) advectionEquation = TransientTerm() + FirstOrderAdvectionTerm(extensionVelocityVariable) metalEquation = buildMetalIonDiffusionEquation( ionVar = metalVar, distanceVar = distanceVar, depositionRate = depositionRateVariable, diffusionCoeff = metalDiffusion, metalIonMolarVolume = molarVolume) metalVar.constrain(metalConcentration, mesh.facesTop) if displayViewers: try: from .mayaviSurfactantViewer import MayaviSurfactantViewer viewer = MayaviSurfactantViewer(distanceVar, catalystVar.interfaceVar, zoomFactor = 1e6, datamax=1.0, datamin=0.0, smooth = 1, title = 'catalyst coverage', animate=True) except: class PlotVariable(CellVariable): def __init__(self, var = None, name = ''): CellVariable.__init__(self, mesh = mesh.fineMesh, name = name) self.var = self._requires(var) def _calcValue(self): return numerix.array(self.var(self.mesh.cellCenters)) viewer = MultiViewer(viewers=( Viewer(PlotVariable(var = distanceVar), datamax=1e-9, datamin=-1e-9), Viewer(PlotVariable(var = catalystVar.interfaceVar)))) else: viewer = None levelSetUpdateFrequency = int(0.7 * narrowBandWidth / cellSize / cflNumber / 2) step = 0 while step < numberOfSteps: if step % 10 == 0 and viewer is not None: viewer.plot() if step % levelSetUpdateFrequency == 0: distanceVar.calcDistanceFunction() extensionVelocityVariable.setValue(numerix.array(depositionRateVariable)) dt = cflNumber * cellSize / max(extensionVelocityVariable.globalValue) distanceVar.extendVariable(extensionVelocityVariable) advectionEquation.solve(distanceVar, dt = dt) catalystSurfactantEquation.solve(catalystVar, dt = dt) metalEquation.solve(metalVar, dt = dt) step += 1 point = ((5e-09,), (1.15e-07,)) value = 1.45346701e-09 return abs(float(distanceVar(point, order=1)) - value) < cellSize / 10.0
dx = dy = 1.0 # mesh resolution dt = Variable(0.1) # initial timestep # Physical parameters mm = 4. # anisotropic symmetry epsilon_m = 0.025 # degree of anisotropy theta_0 = 0.0 # tilt w.r.t. x-axis tau_0 = 1. # numerical mobility DD = 10. # thermal diffusivity W_0 = 1. # isotropic well height lamda = DD * tau_0 / 0.6267 / W_0**2 delta = 0.05 # undercooling # Mesh and field variables mesh = Grid2D(nx=nx, ny=ny, dx=dx, dy=dy) phase = CellVariable(mesh=mesh, hasOld=True) uu = CellVariable(mesh=mesh, hasOld=True) uu.constrain(-delta, mesh.exteriorFaces) def initialize(): phase[:] = -1.0 x, y = mesh.cellCenters radius = 2.0 # Initial r=1 collapses due to Gibbs-Thomson, r=2 slumps to phi=0.6, r=4 seems OK. center = (nx * dx / 2., ny * dy / 2.) mask = (x - center[0])**2 + (y - center[1])**2 < radius**2 phase.setValue(1., where=mask) uu[:] = -delta initialize()
Extrude {{ {{1, 0, 0}}, {{0, 0, 0}}, Pi/2}} {{ Surface{{16}}; }} Physical Surface("inner") = {{30, 26}}; Physical Surface("outer") = {{37}}; Physical Volume("volume") = {{1}}; '''.format(cellSize, l1, l2, math.tan(beta) * (l2 - l1), l3, math.tan(alpha) * l3) mesh = Gmsh3D(geometryTemplate) # Enthaelt die Temperatur phi = CellVariable(name="Temperature", mesh=mesh, value=T0) # boundry conditions ---------------------------------------------------------- phi.constrain(Ti, where=mesh.physicalFaces["inner"]) phi.constrain(Te, where=mesh.physicalFaces["outer"]) # calculation ----------------------------------------------------------------- viewer = Viewer(vars=phi, datamin=200., datamax=Te * 1.01) print "Calculation started" started = time.clock() # Loest die Stationaere Waermegleichung DiffusionTerm(coeff=D).solve(var=phi)
from __future__ import division from __future__ import unicode_literals from builtins import input __docformat__ = 'restructuredtext' from fipy import Tri2D, CellVariable, DiffusionTerm, Viewer nx = 50 dx = 1. mesh = Tri2D(dx = dx, nx = nx) valueLeft = 0. valueRight = 1. var = CellVariable(name = "solution-variable", mesh = mesh, value = valueLeft) var.constrain(valueLeft, mesh.facesLeft) var.constrain(valueRight, mesh.facesRight) if __name__ == '__main__': DiffusionTerm().solve(var) viewer = Viewer(vars=var) viewer.plot() x = mesh.cellCenters[0] Lx = nx * dx analyticalArray = valueLeft + (valueRight - valueLeft) * x / Lx print(var.allclose(analyticalArray)) input("finished")
import time from fipy import PeriodicGrid2D from params_fipy import (A_RAW, NOISE_MAGNITUDE, TIME_MAX, DT, N_CELLS, DOMAIN_LENGTH, TIME_STRIDE, chi_AB, N_A, N_B, GIBBS, DESIRED_RESIDUAL) print("Yay") # # Define mesh # mesh = Grid2D(dx=dx, dy=dx, nx=N_CELLS, ny=N_CELLS) mesh = PeriodicGrid2D(nx=50.0, ny=50.0, dx=0.1, dy=0.1) print("mesh loaded") # We need to define the relevant variables: x_a = CellVariable(name=r"x_a", mesh=mesh, hasOld=1) mu_AB = CellVariable(name=r"mu_AB", mesh=mesh, hasOld=1) # We need to introduce the noise noise = GaussianNoiseVariable(mesh=mesh, mean=A_RAW, variance=NOISE_MAGNITUDE).value x_a[:] = noise # x_a.setValue(GaussianNoiseVariable(mesh=mesh, # mean=A_RAW, # variance=NOISE_MAGNITUDE) # ) # def g(x_a): # if GIBBS == "FH":
class Circumbinary(object): def __init__(self, rmax=1.0e4, ncell=300, dt=1.0e-6, delta=1.0e-100, fudge=1.0e-3, q=1.0, gamma=100, mdisk=0.1, odir='output', bellLin=True, emptydt=0.001, **kargs): self.rmax = rmax self.ncell = ncell self.dt = dt self.delta = delta self.mDisk = mdisk Omega0 = (G*M/(gamma*a)**3)**0.5 nu0 = alpha*cs**2/Omega0 self.chi = 2*fudge*q**2*np.sqrt(G*M)/nu0/a*(gamma*a)**1.5 self.T0 = mu*Omega0/alpha/k*nu0 self.gamma = gamma self.fudge = fudge self.q = q self.nu0 = nu0 self.t = 0.0 self.odir = odir self.bellLin = bellLin self.emptydt = emptydt self._genGrid() self.r = self.mesh.cellCenters.value[0] self.rF = self.mesh.faceCenters.value[0] if self.q > 0.0: self.gap = np.where(self.rF < 1.7/gamma) else: self.gap = np.where(self.rF < 1.0/gamma) self._genSigma() self._genTorque() self._genT(bellLin=self.bellLin, **kargs) self._genVr() self._buildEq() def _genGrid(self, inB=1.0): """Generate a logarithmically spaced grid""" logFaces = np.linspace(-np.log(self.gamma/inB), np.log(self.rmax), num=self.ncell+1) logFacesLeft = logFaces[:-1] logFacesRight = logFaces[1:] dr = tuple(np.exp(logFacesRight) - np.exp(logFacesLeft)) self.mesh = CylindricalGrid1D(dr=dr, origin=(inB/self.gamma,)) def _genSigma(self, width=0.1): """Create dependent variable Sigma""" # Gaussian initial condition value = self.mDisk*M/np.sqrt(2*np.pi)/(self.gamma*a*width)*\ np.exp(-0.5*np.square(self.r-1.0)/width**2)/(2*np.pi*self.gamma*self.r*a) # Make it dimensionless value /= self.mDisk*M/(self.gamma*a)**2 idxs = np.where(self.r < 0.1) value[idxs] = 0.0 value = tuple(value) # Create the dependent variable and set the boundary conditions # to zero self.Sigma = CellVariable(name='Surface density', mesh=self.mesh, hasOld=True, value=value) #self.Sigma.constrain(0, self.mesh.facesLeft) #self.Sigma.constrain(0, self.mesh.facesRight) def _genTorque(self): """Generate Torque""" self.Lambda = FaceVariable(name='Torque at cell faces', mesh=self.mesh, rank=1) self.LambdaCell = CellVariable(name='Torque at cell centers', mesh=self.mesh) LambdaArr = np.zeros(self.rF.shape) LambdaArr[1:] = self.chi*np.power(1.0/(self.rF[1:]*self.gamma-1.0), 4) #LambdaArr[self.gap] = 0.0; LambdaArr[self.gap] = LambdaArr.max() self.Lambda.setValue(LambdaArr) self.LambdaCell.setValue(self.chi*np.power(1.0/(self.r*self.gamma-1.0), 4)) self.LambdaCell[np.where(self.LambdaCell > LambdaArr.max())] = LambdaArr.max() def _interpT(self): """ Get an initial guess for T using an interpolation of the solutions for T in the various thermodynamic limits. """ Lambda = self.Lambda/self.chi*self.fudge*self.q**2*G*M/a LambdaCell = self.LambdaCell/self.chi*self.fudge*self.q**2*G*M/a Sigma = self.Sigma*M/(self.gamma*a)**2 r = self.r*a*self.gamma #In physical units (cgs) self.Omega = np.sqrt(G*M/r**3) self.TvThin = np.power(9.0/4*alpha*k/sigma/mu/kappa0*self.Omega, 1.0/(3.0+beta)) self.TtiThin = np.power(1/sigma/kappa0*(OmegaIn-self.Omega)*LambdaCell, 1.0/(4.0+beta)) self.Ti = np.power(np.square(eta/7*L/4/np.pi/sigma)*k/mu/G/M*r**(-3), 1.0/7) self.TvThick = np.power(27.0/64*kappa0*alpha*k/sigma/mu*self.Omega*Sigma**2, 1.0/(3.0-beta)) self.TtiThick = np.power(3*kappa0/16/sigma*Sigma**2*(OmegaIn-self.Omega)*LambdaCell, 1.0/(4.0-beta)) #return np.power(self.TvThin**4 + self.TvThick**4 + self.TtiThin**4 + self.TtiThick**4 + self.Ti**4, 1.0/4)/self.T0 return np.power(self.TvThin**4 + self.TvThick**4 + self.Ti**4, 1.0/4)/self.T0 def _genT(self, bellLin=True, **kargs): """Create a cell variable for temperature""" if bellLin: @pickle_results(os.path.join(self.odir, "interpolator.pkl")) def buildInterpolator(r, gamma, q, fudge, mDisk, **kargs): # Keep in mind that buildTemopTable() returns the log10's of the values rGrid, SigmaGrid, temp = thermopy.buildTempTable(r*a*gamma, q=q, f=fudge, **kargs) # Go back to dimensionless units rGrid -= np.log10(a*gamma) SigmaGrid -= np.log10(mDisk*M/gamma**2/a**2) # Get the range of values for Sigma in the table rangeSigma = (np.power(10.0, SigmaGrid.min()), np.power(10.0, SigmaGrid.max())) # Interpolate in the log of dimensionless units return rangeSigma, RectBivariateSpline(rGrid, SigmaGrid, temp) # Pass the radial grid in phsyical units # Get back interpolator in logarithmic space rangeSigma, log10Interp = buildInterpolator(self.r, self.gamma, self.q, self.fudge, self.mDisk, **kargs) rGrid = np.log10(self.r) SigmaMin = np.ones(rGrid.shape)*rangeSigma[0] SigmaMax = np.ones(rGrid.shape)*rangeSigma[1] r = self.r*a*self.gamma #In physical units (cgs) self.Omega = np.sqrt(G*M/r**3) Ti = np.power(np.square(eta/7*L/4/np.pi/sigma)*k/mu/G/M*r**(-3), 1.0/7) T = np.zeros(Ti.shape) # Define wrapper function that uses the interpolator and stores the results # in an array given as a second argument. It can handle zero or negative # Sigma values. def func(Sigma): good = np.logical_and(Sigma > rangeSigma[0], Sigma < rangeSigma[1]) badMin = np.logical_and(True, Sigma < rangeSigma[0]) badMax = np.logical_and(True, Sigma > rangeSigma[1]) if np.sum(good) > 0: T[good] = np.power(10.0, log10Interp.ev(rGrid[good], np.log10(Sigma[good]))) if np.sum(badMin) > 0: T[badMin] = np.power(10.0, log10Interp.ev(rGrid[badMin], np.log10(SigmaMin[badMin]))) if np.sum(badMax) > 0: raise ValueError("Extrapolation to large values of Sigma is not allowed, build a table with a larger Sigmax") T[badMax] = np.power(10.0, log10Interp.ev(rGrid[badMax], np.log10(SigmaMax[badMax]))) return T # Store interpolator as an instance method self._bellLinT = func # Save the temperature as an operator variable self.T = self.Sigma._UnaryOperatorVariable(lambda x: self._bellLinT(x)) # Initialize T with the interpolation of the various thermodynamic limits else: self.T = self._interpT() def _genVr(self): """Generate the face variable that stores the velocity values""" r = self.r #In dimensionless units (cgs) # viscosity at cell centers in cgs self.nu = alpha*k/mu/self.Omega/self.nu0*self.T self.visc = r**0.5*self.nu*self.Sigma #self.visc.grad.constrain([self.visc/2/self.r[0]], self.mesh.facesLeft) #self.Sigma.constrain(self.visc.grad/self.nu*2*self.r**0.5, where=self.mesh.facesLeft) # I add the delta to avoid divisions by zero self.vrVisc = -3/self.rF**(0.5)/(self.Sigma.faceValue + self.delta)*self.visc.faceGrad if self.q > 0.0: self.vrTid = self.Lambda*np.sqrt(self.rF) def _buildEq(self): """ Build the equation to solve, we can change this method to impelement other schemes, e.g. Crank-Nicholson. """ # The current scheme is an implicit-upwind if self.q > 0.0: self.vr = self.vrVisc + self.vrTid self.eq = TransientTerm(var=self.Sigma) == - ExplicitUpwindConvectionTerm(coeff=self.vr, var=self.Sigma) else: self.vr = self.vrVisc mask_coeff = (self.mesh.facesLeft * self.mesh.faceNormals).getDivergence() self.eq = TransientTerm(var=self.Sigma) == - ExplicitUpwindConvectionTerm(coeff=self.vr, var=self.Sigma)\ - mask_coeff*3.0/2*self.nu/self.mesh.x*self.Sigma.old def dimensionalSigma(self): """ Return Sigma in dimensional form (cgs) """ return self.Sigma.value*self.mDisk*M/(self.gamma*a)**2 def dimensionalFJ(self): """ Return the viscous torque in dimensional units (cgs) """ return 3*np.pi*self.nu.value*self.nu0*self.dimensionalSigma()*np.sqrt(G*M*self.r*a*self.gamma) def dimensionalTime(self, t=None, mode='yr'): """ Return current time in dimensional units (years or seconds) """ if t == None: t = self.t if mode == 'yr' or mode == 'years' or mode == 'year': return t*(a*self.gamma)**2/self.nu0/(365*24*60*60) else: return t*(a*self.gamma)**2/self.nu0 def dimensionlessTime(self, t, mode='yr'): """ Returns the dimensionless value of the time given as an argument """ if mode == 'yr' or mode == 'years' or mode == 'year': return t/(a*self.gamma)**2*self.nu0*(365*24*60*60) else: return t/(a*self.gamma)**2*self.nu0 def singleTimestep(self, dtMax=0.001, dt=None, update=True, emptyDt=False): """ Evolve the system for a single timestep of size `dt` """ if dt: self.dt = dt if emptyDt: vr = self.vr.value[0] if self.q == 0.0: vr[0] = -3.0/2*self.nu.faceValue.value[0]/self.rF[0] #vr[np.where(self.Sigma.value)] = self.delta self.flux = self.rF[1:]*vr[1:]-self.rF[:-1]*vr[:-1] self.flux = np.maximum(self.flux, self.delta) self.dts = self.mesh.cellVolumes/(self.flux) self.dts[np.where(self.Sigma.value == 0.0)] = np.inf self.dts[self.gap] = np.inf self.dt = self.emptydt*np.amin(self.dts) self.dt = min(dtMax, self.dt) try: self.eq.sweep(dt=self.dt) if np.any(self.Sigma.value < 0.0): self.singleTimestep(dt=self.dt/2) if update: self.Sigma.updateOld() self.t += self.dt except FloatingPointError: import ipdb; ipdb.set_trace() def evolve(self, deltaTime, **kargs): """ Evolve the system using the singleTimestep method """ tEv = self.t + deltaTime while self.t < tEv: dtMax = tEv - self.t self.singleTimestep(dtMax=dtMax, **kargs) def revert(self): """ Revert evolve method if update=False was used, otherwise it has no effect. """ self.Sigma.setValue(self.Sigma.old.value) def writeToFile(self): fName = self.odir + '/t{0}.pkl'.format(self.t) with open(fName, 'wb') as f: pickle.dump((self.t, self.Sigma.getValue()), f) def readFromFile(self, fName): with open(fName, 'rb') as f: t, Sigma = pickle.load(f) self.t = t self.Sigma.setValue(Sigma) def loadTimesList(self): path = self.odir files = os.listdir(path) if '.DS_Store' in files: files.remove('.DS_Store') if 'interpolator.pkl' in files: files.remove('interpolator.pkl') if 'init.pkl' in files: files.remove('init.pkl') self.times = np.zeros((len(files),)) for i, f in enumerate(files): match = re.match(r"^t((\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)\.pkl", f) if match == None: print "WARNING: File {0} has an unexepected name".format(f) files.remove(f) continue self.times[i] = float(match.group(1)) self.times.sort() self.files = files def loadTime(self, t): """ Load the file with the time closest to `t` """ idx = (np.abs(self.times-t)).argmin() fName = self.odir + '/t'+str(self.times[idx]) + '.pkl' self.readFromFile(fName)
def y02(x): """"Initial negative ion charge density""" return nini * ((special.gamma(k2 + p2)) / (special.gamma(k2) * special.gamma(p2)) * ((x / l)**(k2 - 1)) * (1 - (x / l))**(p2 - 1)) / 7.3572 def y03(x): """Initial potential""" return a1 * np.sin(b1 * x + c1) + a2 * np.sin(b2 * x + c2) mesh = Grid1D(dx=dx, nx=nx) Pion = CellVariable(mesh=mesh, name='Positive ion Charge Density', value=y01(mesh.x)) Nion = CellVariable(mesh=mesh, name='Negative ion Charge Density', value=y02(mesh.x)) potential = CellVariable(mesh=mesh, name='Potential', value=y03(mesh.x)) Jp = CellVariable(mesh=mesh, name='Positive ion Current Density', value=0.) Jn = CellVariable(mesh=mesh, name='Negative ion Current Density', value=0.) Jp.value = -mu_p * Pion * potential.arithmeticFaceValue.divergence + Dn * Pion.arithmeticFaceValue.divergence Jn.value = -mu_n * Nion * potential.arithmeticFaceValue.divergence + Dn * Pion.arithmeticFaceValue.divergence Pion.equation = TransientTerm( coeff=1, var=Pion) == -k_rec * Pion * Nion + (Jp.arithmeticFaceValue).divergence Nion.equation = TransientTerm(
#!/usr/bin/env python from fipy import Grid1D, CellVariable, TransientTerm, DiffusionTerm, Viewer # m = Grid1D(nx=100, Lx=1.) # v0 = CellVariable(mesh=m, hasOld=True, value=0.5) v1 = CellVariable(mesh=m, hasOld=True, value=0.5) # v0.constrain(0, m.facesLeft) v0.constrain(1, m.facesRight) # v1.constrain(1, m.facesLeft) v1.constrain(0, m.facesRight) # eq0 = TransientTerm() == DiffusionTerm(coeff=0.01) - v1.faceGrad.divergence eq1 = TransientTerm() == v0.faceGrad.divergence + DiffusionTerm(coeff=0.01) # vi = Viewer((v0, v1)) # for t in range(100): v0.updateOld() v1.updateOld() res0 = res1 = 1e100 while max(res0, res1) > 0.1: res0 = eq0.sweep(var=v0, dt=1e-5) res1 = eq1.sweep(var=v1, dt=1e-5) if t % 10 == 0: vi.plot()
steps = 100 timeStepDuration = 0.02 L = 1.5 nx = 100 temperature = 1. phaseTransientCoeff = 0.1 epsilon = 0.008 s = 0.01 alpha = 0.015 temperature = 1. dx = L / nx mesh = Grid1D(dx=dx, nx=nx) phase = CellVariable(name='PhaseField', mesh=mesh, value=1.) theta = ModularVariable(name='Theta', mesh=mesh, value=1.) theta.setValue(0., where=mesh.cellCenters[0] > L / 2.) mPhiVar = phase - 0.5 + temperature * phase * (1 - phase) thetaMag = theta.old.grad.mag implicitSource = mPhiVar * (phase - (mPhiVar < 0)) implicitSource += (2 * s + epsilon**2 * thetaMag) * thetaMag phaseEq = TransientTerm(phaseTransientCoeff) == \ ExplicitDiffusionTerm(alpha**2) \ - ImplicitSourceTerm(implicitSource) \ + (mPhiVar > 0) * mPhiVar * phase if __name__ == '__main__':
nx = 8 # FIXME: downsized temporarily from 10 due to https://github.com/usnistgov/fipy/issues/622 ny = 5 nz = 3 dx = 1. dy = 1. dz = 1. valueBottomTop = 0. valueLeftRight = 1. mesh = Grid3D(dx = dx, dy = dy, dz = dz, nx = nx, ny = ny, nz = nz) var = CellVariable(name = "solution variable", mesh = mesh, value = valueBottomTop) var.constrain(valueLeftRight, mesh.facesLeft) var.constrain(valueLeftRight, mesh.facesRight) var.constrain(valueBottomTop, mesh.facesTop) var.constrain(valueBottomTop, mesh.facesBottom) #do the 2D problem for comparison nx = 8 # FIXME: downsized temporarily from 10 due to https://github.com/usnistgov/fipy/issues/622 ny = 5 dx = 1. dy = 1.
nx = 40 dx = L / nx cfl = 0.5 velocity = 1.0 dt = cfl * dx / velocity steps = int(L / 4. / dt / velocity) mesh = Grid1D(dx=dx, nx=nx) periodicMesh = PeriodicGrid1D(dx=dx, nx=nx // 2) startingArray = numerix.zeros(nx, 'd') startingArray[2 * nx // 10:3 * nx // 10] = 1. var1 = CellVariable(name="non-periodic", mesh=mesh, value=startingArray) var2 = CellVariable(name="periodic", mesh=periodicMesh, value=startingArray[:nx // 2]) eq1 = TransientTerm() - VanLeerConvectionTerm(coeff=(-velocity, )) eq2 = TransientTerm() - VanLeerConvectionTerm(coeff=(-velocity, )) if __name__ == '__main__': viewer1 = Viewer(vars=var1) viewer2 = Viewer(vars=var2) viewer1.plot() viewer2.plot()
def runLeveler(kLeveler=0.018, bulkLevelerConcentration=0.02, cellSize=0.1e-7, rateConstant=0.00026, initialAcceleratorCoverage=0.0, levelerDiffusionCoefficient=5e-10, numberOfSteps=400, displayRate=10, displayViewers=True): kLevelerConsumption = 0.0005 aspectRatio = 1.5 faradaysConstant = 9.6485e4 gasConstant = 8.314 acceleratorDiffusionCoefficient = 4e-10 siteDensity = 6.35e-6 atomicVolume = 7.1e-6 charge = 2 metalDiffusionCoefficient = 4e-10 temperature = 298. overpotential = -0.25 bulkMetalConcentration = 250. bulkAcceleratorConcentration = 50.0e-3 initialLevelerCoverage = 0. cflNumber = 0.2 cellsBelowTrench = 10 trenchDepth = 0.4e-6 trenchSpacing = 0.6e-6 boundaryLayerDepth = 98.7e-6 i0Suppressor = 0.3 i0Accelerator = 22.5 alphaSuppressor = 0.5 alphaAccelerator = 0.4 alphaAdsorption = 0.62 m = 4 b = 2.65 A = 0.3 Ba = -40 Bb = 60 Vd = 0.098 Bd = 0.0008 etaPrime = faradaysConstant * overpotential / gasConstant / temperature mesh = TrenchMesh(cellSize = cellSize, trenchSpacing = trenchSpacing, trenchDepth = trenchDepth, boundaryLayerDepth = boundaryLayerDepth, aspectRatio = aspectRatio, angle = numerix.pi * 4. / 180.) distanceVar = GapFillDistanceVariable( name = 'distance variable', mesh = mesh, value = -1.) distanceVar.setValue(1., where=mesh.electrolyteMask) distanceVar.calcDistanceFunction() levelerVar = SurfactantVariable( name = "leveler variable", value = initialLevelerCoverage, distanceVar = distanceVar) acceleratorVar = SurfactantVariable( name = "accelerator variable", value = initialAcceleratorCoverage, distanceVar = distanceVar) bulkAcceleratorVar = CellVariable(name = 'bulk accelerator variable', mesh = mesh, value = bulkAcceleratorConcentration) bulkLevelerVar = CellVariable( name = 'bulk leveler variable', mesh = mesh, value = bulkLevelerConcentration) metalVar = CellVariable( name = 'metal variable', mesh = mesh, value = bulkMetalConcentration) def depositionCoeff(alpha, i0): expo = numerix.exp(-alpha * etaPrime) return 2 * i0 * (expo - expo * numerix.exp(etaPrime)) coeffSuppressor = depositionCoeff(alphaSuppressor, i0Suppressor) coeffAccelerator = depositionCoeff(alphaAccelerator, i0Accelerator) exchangeCurrentDensity = acceleratorVar.interfaceVar * (coeffAccelerator - coeffSuppressor) + coeffSuppressor currentDensity = metalVar / bulkMetalConcentration * exchangeCurrentDensity depositionRateVariable = currentDensity * atomicVolume / charge / faradaysConstant extensionVelocityVariable = CellVariable( name = 'extension velocity', mesh = mesh, value = depositionRateVariable) kAccelerator = rateConstant * numerix.exp(-alphaAdsorption * etaPrime) kAcceleratorConsumption = Bd + A / (numerix.exp(Ba * (overpotential + Vd)) + numerix.exp(Bb * (overpotential + Vd))) q = m * overpotential + b levelerSurfactantEquation = AdsorbingSurfactantEquation( levelerVar, distanceVar = distanceVar, bulkVar = bulkLevelerVar, rateConstant = kLeveler, consumptionCoeff = kLevelerConsumption * depositionRateVariable) accVar1 = acceleratorVar.interfaceVar accVar2 = (accVar1 > 0) * accVar1 accConsumptionCoeff = kAcceleratorConsumption * (accVar2**(q - 1)) acceleratorSurfactantEquation = AdsorbingSurfactantEquation( acceleratorVar, distanceVar = distanceVar, bulkVar = bulkAcceleratorVar, rateConstant = kAccelerator, otherVar = levelerVar, otherBulkVar = bulkLevelerVar, otherRateConstant = kLeveler, consumptionCoeff = accConsumptionCoeff) advectionEquation = TransientTerm() + FirstOrderAdvectionTerm(extensionVelocityVariable) metalEquation = buildMetalIonDiffusionEquation( ionVar = metalVar, distanceVar = distanceVar, depositionRate = depositionRateVariable, diffusionCoeff = metalDiffusionCoefficient, metalIonMolarVolume = atomicVolume) metalVar.constrain(bulkMetalConcentration, mesh.facesTop) bulkAcceleratorEquation = buildSurfactantBulkDiffusionEquation( bulkVar = bulkAcceleratorVar, distanceVar = distanceVar, surfactantVar = acceleratorVar, otherSurfactantVar = levelerVar, diffusionCoeff = acceleratorDiffusionCoefficient, rateConstant = kAccelerator * siteDensity) bulkAcceleratorVar.constrain(bulkAcceleratorConcentration, mesh.facesTop) bulkLevelerEquation = buildSurfactantBulkDiffusionEquation( bulkVar = bulkLevelerVar, distanceVar = distanceVar, surfactantVar = levelerVar, diffusionCoeff = levelerDiffusionCoefficient, rateConstant = kLeveler * siteDensity) bulkLevelerVar.constrain(bulkLevelerConcentration, mesh.facesTop) eqnTuple = ( (advectionEquation, distanceVar, (), None), (levelerSurfactantEquation, levelerVar, (), None), (acceleratorSurfactantEquation, acceleratorVar, (), None), (metalEquation, metalVar, (), None), (bulkAcceleratorEquation, bulkAcceleratorVar, (), GeneralSolver()), (bulkLevelerEquation, bulkLevelerVar, (), GeneralSolver())) narrowBandWidth = 20 * cellSize levelSetUpdateFrequency = int(0.7 * narrowBandWidth / cellSize / cflNumber / 2) totalTime = 0.0 if displayViewers: try: raise Exception from .mayaviSurfactantViewer import MayaviSurfactantViewer viewers = ( MayaviSurfactantViewer(distanceVar, acceleratorVar.interfaceVar, zoomFactor = 1e6, datamax=0.5, datamin=0.0, smooth = 1, title = 'accelerator coverage'), MayaviSurfactantViewer(distanceVar, levelerVar.interfaceVar, zoomFactor = 1e6, datamax=0.5, datamin=0.0, smooth = 1, title = 'leveler coverage')) except: class PlotVariable(CellVariable): def __init__(self, var = None, name = ''): CellVariable.__init__(self, mesh = mesh.fineMesh, name = name) self.var = self._requires(var) def _calcValue(self): return numerix.array(self.var(self.mesh.cellCenters)) viewers = (Viewer(PlotVariable(var=acceleratorVar.interfaceVar)), Viewer(PlotVariable(var=levelerVar.interfaceVar))) for step in range(numberOfSteps): if displayViewers: if step % displayRate == 0: for viewer in viewers: viewer.plot() if step % levelSetUpdateFrequency == 0: distanceVar.calcDistanceFunction() extensionVelocityVariable.setValue(depositionRateVariable) extOnInt = numerix.where(distanceVar.globalValue > 0, numerix.where(distanceVar.globalValue < 2 * cellSize, extensionVelocityVariable.globalValue, 0), 0) dt = cflNumber * cellSize / extOnInt.max() distanceVar.extendVariable(extensionVelocityVariable) for eqn, var, BCs, solver in eqnTuple: eqn.solve(var, boundaryConditions = BCs, dt = dt, solver=solver) totalTime += dt point = ((1.25e-08,), (3.125e-07,)) value = 2.02815779e-08 return abs(float(distanceVar(point, order=1)) - value) < cellSize / 10.0