def __init__(self, layer, **kwargs): super(SimpleFiniteSolver,self).__init__(layer, **kwargs) try: layers = layer.layers if len(layers) == 1: self.layer = layers[0] else: arg = self.__class__.__name__+" can be initialized only from a single layer or a section containing only one layer." raise ArgumentError(arg) except AttributeError: self.layer = layer self.analytical_solver = HalfSpaceSolver(layer) self.material = self.layer.material self.depth = self.layer.thickness self.dy = self.layer.grid_spacing self.ny = int((self.depth/self.dy).into("dimensionless")) self.mesh = F.Grid1D(nx=self.ny, dx=self.dy.into("m")) t_min,t_max = (i.into("kelvin") for i in self.constraints) self.initial_values = N.empty(self.ny) self.initial_values.fill(t_max) self.var = F.CellVariable(name="Temperature", mesh=self.mesh, value=self.initial_values) self.var.constrain(t_min, self.mesh.facesLeft) self.var.constrain(t_max, self.mesh.facesRight) coefficient = self.material.diffusivity.into("m**2/s") self.equation = F.TransientTerm() == F.DiffusionTerm(coeff=coefficient)
def createLinearMesh(self, sections, Acs, As): """ Define mesh """ # Cross sectional area self.AcsMult = Acs # Area multiplier for face area self.areaMult = Acs # Surface area per unit length self.As = As # Create the meshes and emissivity calculator for each segment self.emissivityCalculator = sm.SectionCalculator() dx = [] i = 0 for sec in sections: numElements = np.ceil(sec['length'] / sec['meshSize']) self.emissivityCalculator.addSection( (i, i + numElements - 1), sm.Interpolator1D([sec['temperature1'], sec['temperature2']], [sec['emissivity1'], sec['emissivity2']]) ) dx += [sec['length'] / numElements] * numElements i += numElements self.radiation['emissivity'] = np.zeros((len(dx))) self.mesh = fp.Grid1D(dx = dx) self.meshType = 'Linear' # Define variable self.T = fp.CellVariable(name = "temperature", mesh = self.mesh, value = 0.) self.thermCond = fp.FaceVariable(mesh = self.mesh) self.emissivity = fp.CellVariable(mesh = self.mesh)
def test_linear_poison_solver(self): ''' Compare the result of the poison solution with analytical result. ''' # Electrical properties n_eff = 5e12 # Effective doping concentration rho = constants.elementary_charge * n_eff * ( 1e-4)**3 # Charge density in C / um3 epsilon = constant.epsilon_s * 1e-6 # Permitticity of silicon in F/um # External voltages in V V_read = -0 # Geometry dx = 0.01 # Grid spacing / resolution L = 200. # Length of simulation domain / width of sensor in um # Create mesh nx = L / dx # Number of space points mesh = fipy.Grid1D(dx=np.ones((int(nx), )) * dx, nx=nx) X = np.array(mesh.getFaceCenters()[0, :]) for V_bias in range(-10, -200, -20): # Get 1D potential with numerical solver potential = potential_1D.get_potential(mesh, rho, epsilon, L, V_read, V_bias) # Get correct analytical solution potential_a = fields.get_potential_planar_analytic_1D( X, V_bias=V_bias, V_readout=V_read, n_eff=n_eff, D=L) self.assertTrue(np.allclose(potential, potential_a[:-1], atol=1e-1))
def solve_fipy_with_given_N(N, params): s1 = params[0] s2 = params[1] t1 = params[2] t2 = params[3] dx = 100.0 / N dt = 1.0 NDAYS = 10 f_start_time = time.time() mesh = fp.Grid1D(nx=N, dx=dx) v_fp = fp.CellVariable(name="v_fp", mesh=mesh, value=INI_VALUE, hasOld=True) # BC v_fp.constrain(0, where=mesh.facesLeft) # left BC is always Dirichlet # v_fp.faceGrad.constrain(0. * mesh.faceNormals, where=mesh.facesRight) # right: flux=0 def dif_fp(u): b = -4. D = (numerix.exp(t1) / t2 * (numerix.power(s2 * numerix.exp(-s1) * u + numerix.exp(s2 * b), t2 / s2) - numerix.exp(t2 * b))) / ( s2 * u + numerix.exp(s1 + s2 * b)) return D # Boussinesq eq. for theta eq = fp.TransientTerm() == fp.DiffusionTerm(coeff=dif_fp(v_fp)) + SOURCE MAX_SWEEPS = 10000 for t in range(NDAYS): v_fp.updateOld() res = 0.0 for r in range(MAX_SWEEPS): # print(i, res) resOld = res # res = eq.sweep(var=v_fp, dt=dt, underRelaxation=0.1) res = eq.sweep(var=v_fp, dt=dt) if abs(res - resOld) < abs_tolerance: break # it has reached to the solution of the linear system time_spent = time.time() - f_start_time return v_fp.value, time_spent
def get_mesh(ncells, delta): """Make the mesh Args: ncells: number of cells delta: the domain size Returns: the mesh """ return fipy.Grid1D(nx=ncells, dx=delta / ncells)
def initialise_mesh(self): if self.has_obstacles(): self.mesh = make_porous_mesh(self.rc, self.Rc, self.dx, self.L) elif self.dim == 1: self.mesh = fipy.Grid1D(Lx=self.L[0], dx=self.dx[0], origin=(-self.L[0] / 2.0, )) elif self.dim == 2: self.mesh = fipy.Grid2D(Lx=self.L[0], Ly=self.L[1], dx=self.dx[0], dy=self.dx[1], origin=((-self.L[0] / 2.0, ), (-self.L[1] / 2.0, )))
def create_1D_planar_sensor(): # Electrical properties n_eff = 5e12 # Effective doping concentration rho = constants.elementary_charge * n_eff * \ (1e-4) ** 3 # Charge density in C / um3 epsilon = constant.epsilon_s * 1e-6 # Permitticity of silicon in F/um # External voltages in V V_bias = -200. V_read = -0 # Geometry dx = 0.01 # Grid spacing / resolution L = 200. # Length of simulation domain / width of sensor in um # Create mesh nx = L / dx # Number of space points mesh = fipy.Grid1D(dx=np.ones((int(nx), )) * dx, nx=nx) X = np.array(mesh.getFaceCenters()[0, :]) # Get 1D potential with numerical solver potential = get_potential(mesh, rho, epsilon, L, V_read, V_bias) # Get correct analytical solution potential_a = fields.get_potential_planar_analytic_1D( X, V_bias=V_bias, V_readout=V_read, n_eff=n_eff, D=L) # Get depletion width from empiric formular in um x_dep = silicon.get_depletion_depth( abs(V_bias), n_eff, temperature=300) * 1e6 # Plot result plt.plot(X, potential_a, '--', linewidth=2, label='Analytic solution') plt.plot(np.array(mesh.getFaceCenters()[0, :]), np.array( potential.arithmeticFaceValue()), '-', label='Numeric solution') plt.plot([x_dep, x_dep], plt.ylim(), '--', linewidth=2., label='Depletion zone') plt.plot(plt.xlim(), [V_bias, V_bias], '--', linewidth=2., label='Bias voltage') plt.xlabel('Position [um]') plt.ylabel('Potential [V]') plt.grid() plt.legend(loc=0) plt.title('Potential of a %sfully depleted planar silicon sensor\ with full fill factor' % ('not ' if x_dep < L else '')) plt.show()
def test_steady_state(): """ Test that we can create a steady-state model using FiPy that is similar to that given by our 'naive' solver """ continental_crust.heat_generation = u(1, 'mW/m^3') _ = continental_crust.to_layer(u(3000, 'm')) section = Section([_]) heatflow = u(15, 'mW/m^2') surface_temperature = u(0, 'degC') m = continental_crust q = heatflow k = m.conductivity a = m.heat_generation Cp = m.specific_heat rho = m.density def simple_heat_flow(x): # Density and heat capacity matter don't matter in steady-state T0 = surface_temperature return T0.to('K') + q * x / k + a / (2 * k) * x**2 p = simple_heat_flow(section.cell_centers) dx = section.cell_sizes[0] test_profile = p.into('degC') grad = (-q / k).into('K/m') # Divergence div = (-a / k).into('K/m^2') a_ = -N.gradient(N.gradient(test_profile, dx), dx) assert all(a_ < 0) assert N.allclose(a_.min(), div) res = steady_state(section, heatflow, surface_temperature) profile = res.profile.into('degC') assert N.allclose(test_profile, profile) solver = FiniteSolver(_) solver.constrain(surface_temperature, None) solver.constrain(heatflow, None) res2 = solver.steady_state() # Make sure it's nonlinear and has constant 2nd derivative arr = solver.var.faceGrad.divergence.value assert N.allclose(sum(arr - arr[0]), 0) assert N.allclose(arr.mean(), div) # Test simple finite element model mesh = F.Grid1D(nx=section.n_cells, dx=section.cell_sizes[0].into('m')) T = F.CellVariable(name="Temperature", mesh=mesh) T.constrain(surface_temperature.into("K"), mesh.facesLeft) A = F.FaceVariable(mesh=mesh, value=m.diffusivity.into("m**2/s")) rad_heat = F.CellVariable(mesh=mesh, value=(a / Cp / rho).into("K/s")) D = F.DiffusionTerm(coeff=A) + F.ImplicitSourceTerm(coeff=rad_heat) sol = D.solve(var=T) val = T.faceGrad.divergence.value arr = T.value - 273.15 #assert N.allclose(val.mean(), div) assert N.allclose(test_profile, arr) P = res2.profile.into('degC') assert N.allclose(test_profile, P)
def createLinearMesh(self, L, n=100): """ Creates 1D mesh """ dx = float(L) / n self.mesh = fp.Grid1D(nx=n, dx=dx) self.meshType = 'Linear'
def hydro_1d_fipy(theta_ini, nx, dx, dt, params, ndays, sensor_loc, boundary_values_left, boundary_values_right, precip, evapotra, ele_interp, peat_depth): def zeta_from_theta(x, b): return np.log(np.exp(s2 * b) + s2 * np.exp(-s1) * x) / s2 mesh = fp.Grid1D(nx=nx, dx=dx) ele = ele_interp(mesh.cellCenters.value[0]) b = peat_depth + ele.min() - ele s1 = params[0] s2 = params[1] t1 = params[2] t2 = params[3] source = precip[0] - evapotra[0] theta = fp.CellVariable(name="theta", mesh=mesh, value=theta_ini, hasOld=True) # Choice of parameterization # This is the underlying conductivity: K = exp(t1 + t2*zeta). The # transmissivity is derived from this and written in terms of theta # This is the underlying storage coeff: S = exp(s1 + s2*zeta) # S is hidden in change from theta to h # D = (numerix.exp(t1)/t2 * (numerix.power(s2 * numerix.exp(-s1) * theta + numerix.exp(s2*b), t2/s2) - numerix.exp(t2*b))) * np.power(s2 * (theta + numerix.exp(s1 + s2*b)/s2), -1) # Boussinesq eq. for theta eq = fp.TransientTerm() == fp.DiffusionTerm(coeff=( numerix.exp(t1) / t2 * (numerix.power(s2 * numerix.exp(-s1) * theta + numerix.exp(s2 * b), t2 / s2) - numerix.exp(t2 * b)) ) * np.power(s2 * (theta + numerix.exp(s1 + s2 * b) / s2), -1)) + source theta_sol_list = [] # returned quantity MAX_SWEEPS = 10000 for day in range(ndays): theta.updateOld() # BC and Source/sink update theta_left = boundary_values_left[day] # left BC is always Dirichlet theta.constrain(theta_left, where=mesh.facesLeft) if boundary_values_right == None: # Pxx sensors. Neuman BC on the right theta.faceGrad.constrain(0. * mesh.faceNormals, where=mesh.facesRight) else: theta_right = boundary_values_right[day] theta.constrain(theta_right, where=mesh.facesRight) source = precip[day] - evapotra[day] res = 0.0 for r in range(MAX_SWEEPS): resOld = res res = eq.sweep(var=theta, dt=dt) if abs(res - resOld) < 1e-7: break # it has reached the solution of the linear system if r == MAX_SWEEPS: raise ValueError( 'Solution not converging after maximum number of sweeps') # Append to list theta_sol = theta.value theta_sol_sensors = np.array( [theta_sol[sl] for sl in sensor_loc[1:]] ) # canal sensor is part of the model; cannot be part of the fitness estimation theta_sol_list.append(theta_sol_sensors[0]) b_sensors = np.array([b[sl] for sl in sensor_loc[1:]]) zeta_from_theta_sol_sensors = zeta_from_theta(np.array(theta_sol_list), b_sensors) # print(zeta_from_theta_sol_sensors) return zeta_from_theta_sol_sensors
# 5) Solve Boussinesq eq in both forms and compare results """ Choice: theta(h) = exp(s0 + s1*h) """ # 1) theta(h) = exp(a + b*h); T(h) = t0 + exp(t1*h**t2) # 2) S(h) = s1*exp(s0 + s1*h) # 3) S(theta) = s1*theta # 4) D(theta) = t0/s1 * exp(t1* ((ln(theta) -s0)/s1)**t2 - ln(theta)) nx = 300 dx = 1 dt = 10. mesh = fp.Grid1D(nx=nx, dx=dx) s0 = 0.1; s1 = 0.2 t0 = 1.; t1 = 0.01; t2 = 1.1 # IC hini = 1. h = fp.CellVariable(name="head", mesh=mesh, value=hini, hasOld=True) h_implicit_source = fp.CellVariable(name="head_implicit_source", mesh=mesh, value=hini, hasOld=True) h_convection = fp.CellVariable(name="head_convection", mesh=mesh, value=hini, hasOld=True) theta = fp.CellVariable(name="theta", mesh=mesh, value=numerix.exp(s0 + s1*h.value), hasOld=True) T = t0 * numerix.exp(t1 * h**t2) S = s1 * numerix.exp(s0 + s1 * h) # and S_theta = s1 * theta D = t0/s1 * numerix.exp(t1* ((numerix.log(theta) -s0)/s1)**t2)/ theta