Esempio n. 1
0
 def do_before_timesteps(self):
 
     if self.second_order_time_discretization:
         
         self.write_solution(self.solution_file, self.old_old_state)
     
     self.write_solution(self.solution_file, self.old_state)
     
     fenics.set_log_level(fenics.PROGRESS)
    def __init__(self,
                 mesh: fenics.Mesh,
                 metric_tensor: Optional[str] = None,
                 num_eigenpairs: Optional[int] = DEFAULT_NUM_EIGENPAIRS,
                 function_space: Optional[str] = DEFAULT_FUNCTION_SPACE,
                 degree: Optional[int] = DEFAULT_DEGREE,
                 boundary_conditions: Optional = None) -> None:
        """Compute eigenvalue decomposition of the Laplace-Beltrami operator.

        Parameters
        ----------
        mesh : fenics.Mesh
            Mesh defined over the domain of the Laplace-Beltrami operator
            (this is usually a chart).
        metric_tensor : str, optional
            A specification of the metric tensor (see also the documentation
            for the _make_metric_tensor method below).
        num_eigenpairs : int, optional
            Number of eigenvalue/eigenfunction pairs to (attempt to)
            obtain. The actual number will depend on the convergence of the
            eigensolver.
        function_space : str, optional
            Type of finite element functions.
        degree : int, optional
            Degree for the basis functions.
        boundary_conditions : list, optional
            Constraints on the mesh such as periodic boundary conditions.

        """
        fenics.set_log_level(logging.ERROR)

        self.mesh = mesh
        self.num_eigenpairs = num_eigenpairs
        self.degree = degree
        self.function_space = function_space
        self.boundary_conditions = boundary_conditions
        self.vector_space = fenics.FunctionSpace(
            mesh,
            function_space,
            degree,
            constrained_domain=boundary_conditions)
        self.metric_tensor = self._make_metric_tensor(metric_tensor)
        self.eigensolver = None
        self.eigenvalues, self.eigenfunctions = self.solve()
def main():

    d.set_log_level(1000)

    print("Initializing")

    ire = IREProblem()

    print("Loading")

    ire.load()

    print("Solving")

    ire.solve()

    #print("Plotting result")

    #ire.plot_result()

    print("Saving output lesion")

    ire.save_lesion()
Esempio n. 4
0
def main():

    d.set_log_level(1000)

    print("Initializing")

    ire = IREProblem()

    print("Loading")

    ire.load()

    print("Solving")

    ire.solve()

    #print("Plotting result")

    #ire.plot_result()

    print("Saving output lesion")

    ire.save_lesion()
Esempio n. 5
0
def main():
    fenics.set_log_level(30)
    np.random.seed(1226)

    # problem parameters
    dim = 1
    num_data = 25  # sample's dimension of data collected
    noise_sigma = 1.25  # since we generate the data we add some artificial noise
    true_z = 11.  # to avoid numerical instability, z should be between 9 and 17

    # distribution parameters
    def param_remapper(v):
        return np.exp(v)

    prior_mean = 14.
    prior_sigma = 1.
    proposal_sigma = .20  # we set up a RW chain on z[0] and an independent chain on z[1]
    inv_gamma_parameters = np.array([3., 2.])

    # MCMC parameters
    samples = 15000
    subchain_len = 1000
    upper_th = 1e-4
    error_th = 1e-2
    init_z = prior_mean
    init_variance = .05
    init_radius = .5
    rho = .85
    burn = 3000

    # surrogate parameters
    use_gpr = True
    quad_points = 20
    multi_fidelity_q = 20

    # definition of the forward model for data generation - finer grid to avoid inverse crimes
    eval_times = np.arange(.25, 1., .25)
    lx, ly, lz = 1., .1, .1
    data_gen_forward_model = HyperelasticBeam(eval_times=eval_times,
                                              lx=lx,
                                              ly=ly,
                                              lz=lz,
                                              param_remapper=param_remapper,
                                              time=1.25)

    # forward model for the MCMCs
    forward_model = HyperelasticBeam(eval_times=eval_times,
                                     lx=lx,
                                     ly=ly,
                                     lz=lz,
                                     param_remapper=param_remapper,
                                     n=5,
                                     timestep=.3,
                                     time=1.25)

    # generation of the dataset
    x = np.concatenate([
        lx * np.random.uniform(.2, .8, size=(1, num_data)),
        ly * np.random.uniform(0., 1., size=(1, num_data)),
        np.zeros(shape=(1, num_data))
    ])
    orig_data, last_sol = data_gen_forward_model(true_z,
                                                 x,
                                                 reshape=False,
                                                 retall=True)
    _, proxy_solution = forward_model(true_z, x, reshape=False, retall=True)
    true_data = orig_data.flatten()
    orig_noise = np.random.normal(0, noise_sigma, size=orig_data.shape)
    noise = orig_noise.flatten()
    data = true_data + noise

    # show reference points and their position in the deformed configuration
    fig, ax = plt.subplots(figsize=(15, 3))
    plt.scatter(x=x[0, :],
                y=x[2, :],
                marker='x',
                s=16,
                label='original points')
    scatter = plt.scatter(x=x[0, :],
                          y=x[2, :] -
                          np.exp(orig_data[:, -1] + orig_noise[:, -1]),
                          c=orig_noise[:, -1],
                          cmap='coolwarm',
                          label='final positions')
    fig.colorbar(scatter, shrink=.5)

    # compute the profile of the beam's central section
    disp_x = np.linspace(0., lx, 25)
    sup_profile = np.array(
        [lz + last_sol((x_, lz, ly / 2))[2] for x_ in disp_x])
    inf_profile = np.array([last_sol((x_, 0., ly / 2))[2] for x_ in disp_x])
    proxy_sup_profile = np.array(
        [lz + proxy_solution((x_, lz, ly / 2))[2] for x_ in disp_x])
    proxy_inf_profile = np.array(
        [proxy_solution((x_, 0., ly / 2))[2] for x_ in disp_x])

    # showing original and deformed configuration
    def plot_deformed(superior,
                      inferior,
                      xcoords,
                      xlen,
                      zlen,
                      label=None,
                      **style_params):
        plt.plot(xcoords, superior, label=label, **style_params)
        plt.plot(xcoords, inferior, **style_params)
        plt.plot((0., 0.), (0., zlen), **style_params)
        plt.plot((xlen, xlen), (0., zlen), **style_params)

    plot_deformed(sup_profile,
                  inf_profile,
                  disp_x,
                  lx,
                  lz,
                  color='teal',
                  linewidth=2,
                  label='finer grid')
    plot_deformed(proxy_sup_profile,
                  proxy_inf_profile,
                  disp_x,
                  lx,
                  lz,
                  color='mediumaquamarine',
                  linestyle='--',
                  linewidth=2,
                  label='coarser grid')
    rect = Rectangle((0, 0),
                     width=lx,
                     height=lz,
                     color='black',
                     linestyle=':',
                     linewidth=2.5,
                     alpha=.5,
                     fill=False,
                     label='original configuration')
    ax.add_patch(rect)
    ax.legend()
    ax.set_aspect('equal')
    show_or_save('deformation', save=True)

    # useful distributions and densities
    def log_prior(z_):
        return -(z_ - prior_mean)**2 / (2 * prior_sigma**2)

    prior = cpy.Normal(prior_mean, prior_sigma)
    log_err_density = GaussDensity(.1)
    proposal = GaussDensity(proposal_sigma)
    full_cnd_sigma2 = CondInvGamma(*inv_gamma_parameters)

    # models
    hfm = HighFidelityModel(forward_model, data, x, log_err_density, log_prior)
    lfm = PCESurrogate(data, log_err_density, prior, log_prior, 2,
                       multi_fidelity_q)
    gps = GPSurrogate(data,
                      log_err_density,
                      prior,
                      log_prior,
                      RBF(.6),
                      multi_fidelity_q,
                      n_restarts_optimizer=5,
                      alpha=1e-3)

    if use_gpr:
        low_fi_models = [lfm, gps]
        surrogate_types = ['PCE', 'GPR']
    else:
        low_fi_models = [lfm]
        surrogate_types = ['PCE']
    method_names = ['true model (MH)'] + \
        ['{} surrogate (MH)'.format(typ) for typ in surrogate_types] + \
        ['{} surr. (adap. MH)'.format(typ) for typ in surrogate_types]

    # running MCMCs
    fit_times, fit_calls, exec_times, exec_calls, mh_samples = run_and_track(
        hfm, low_fi_models, quad_points, proposal, full_cnd_sigma2, init_z,
        init_variance, samples, subchain_len, upper_th, error_th, init_radius,
        rho)

    # displaying results
    diagnostics_report(method_names,
                       exec_times, exec_calls, fit_times, fit_calls,
                       len(low_fi_models), samples, burn, mh_samples)
    visual_inspection(dim, method_names, mh_samples, samples, burn, save=True)
Esempio n. 6
0
	def __init__(self, state_forms, bcs_list, cost_functional_form, states, adjoints, config=None,
				 initial_guess=None, ksp_options=None, adjoint_ksp_options=None, desired_weights=None):
		r"""Initializes the optimization problem.

		Parameters
		----------
		state_forms : ufl.form.Form or list[ufl.form.Form]
			The weak form of the state equation (user implemented). Can be either
			a single UFL form, or a (ordered) list of UFL forms.
		bcs_list : list[dolfin.fem.dirichletbc.DirichletBC] or list[list[dolfin.fem.dirichletbc.DirichletBC]] or dolfin.fem.dirichletbc.DirichletBC or None
			The list of :py:class:`fenics.DirichletBC` objects describing Dirichlet (essential) boundary conditions.
			If this is ``None``, then no Dirichlet boundary conditions are imposed.
		cost_functional_form : ufl.form.Form or list[ufl.form.Form]
			UFL form of the cost functional. Can also be a list of individual terms of the cost functional,
			which are scaled according to desired_weights.
		states : dolfin.function.function.Function or list[dolfin.function.function.Function]
			The state variable(s), can either be a :py:class:`fenics.Function`, or a list of these.
		adjoints : dolfin.function.function.Function or list[dolfin.function.function.Function]
			The adjoint variable(s), can either be a :py:class:`fenics.Function`, or a (ordered) list of these.
		config : configparser.ConfigParser or None
			The config file for the problem, generated via :py:func:`cashocs.create_config`.
			Alternatively, this can also be ``None``, in which case the default configurations
			are used, except for the optimization algorithm. This has then to be specified
			in the :py:meth:`solve <cashocs.OptimalControlProblem.solve>` method. The
			default is ``None``.
		initial_guess : list[dolfin.function.function.Function], optional
			List of functions that act as initial guess for the state variables, should be valid input for :py:func:`fenics.assign`.
			Defaults to ``None``, which means a zero initial guess.
		ksp_options : list[list[str]] or list[list[list[str]]] or None, optional
			A list of strings corresponding to command line options for PETSc,
			used to solve the state systems. If this is ``None``, then the direct solver
			mumps is used (default is ``None``).
		adjoint_ksp_options : list[list[str]] or list[list[list[str]]] or None
			A list of strings corresponding to command line options for PETSc,
			used to solve the adjoint systems. If this is ``None``, then the same options
			as for the state systems are used (default is ``None``).
		desired_weights : list[int] or list[float] or None:
			A list which indicates the value of the associated term in the cost functional on
			the initial geometry in case a list of cost functions is supplied. If this is None,
			this defaults to multiplying all terms and adding them.

		Notes
		-----
		If one uses a single PDE constraint, the inputs can be the objects
		(UFL forms, functions, etc.) directly. In case multiple PDE constraints
		are present the inputs have to be put into (ordered) lists. The order of
		the objects depends on the order of the state variables, so that
		``state_forms[i]`` is the weak form of the PDE for ``states[i]`` with boundary
		conditions ``bcs_list[i]`` and corresponding adjoint state ``adjoints[i]``.

		See Also
		--------
		cashocs.OptimalControlProblem : Represents an optimal control problem.
		cashocs.ShapeOptimizationProblem : Represents a shape optimization problem.
		"""

		### Overloading, so that we do not have to use lists for a single state and a single control
		### state_forms
		try:
			if type(state_forms) == list and len(state_forms) > 0:
				for i in range(len(state_forms)):
					if state_forms[i].__module__=='ufl.form' and type(state_forms[i]).__name__=='Form':
						pass
					else:
						raise InputError('cashocs.optimization_problem.OptimizationProblem', 'state_forms', 'state_forms have to be ufl forms')
				self.state_forms = state_forms
			elif state_forms.__module__ == 'ufl.form' and type(state_forms).__name__ == 'Form':
				self.state_forms = [state_forms]
			else:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'state_forms', 'state_forms have to be ufl forms')
		except:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'state_forms', 'state_forms have to be ufl forms')
		self.state_dim = len(self.state_forms)

		### bcs_list
		try:
			if bcs_list == [] or bcs_list is None:
				self.bcs_list = []
				for i in range(self.state_dim):
					self.bcs_list.append([])
			elif type(bcs_list) == list and len(bcs_list) > 0:
				if type(bcs_list[0]) == list:
					for i in range(len(bcs_list)):
						if type(bcs_list[i]) == list:
							pass
						else:
							raise InputError('cashocs.optimization_problem.OptimizationProblem', 'bcs_list', 'bcs_list has inconsistent types.')
					self.bcs_list = bcs_list

				elif bcs_list[0].__module__ == 'dolfin.fem.dirichletbc' and type(bcs_list[0]).__name__ == 'DirichletBC':
					for i in range(len(bcs_list)):
						if bcs_list[i].__module__=='dolfin.fem.dirichletbc' and type(bcs_list[i]).__name__=='DirichletBC':
							pass
						else:
							raise InputError('cashocs.optimization_problem.OptimizationProblem', 'bcs_list', 'bcs_list has inconsistent types.')
					self.bcs_list = [bcs_list]
			elif bcs_list.__module__ == 'dolfin.fem.dirichletbc' and type(bcs_list).__name__ == 'DirichletBC':
				self.bcs_list = [[bcs_list]]
			else:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'bcs_list', 'Type of bcs_list is wrong.')
		except:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'bcs_list', 'Type of bcs_list is wrong.')

		### cost_functional_form
		self.use_cost_functional_list = False
		try:
			if type(cost_functional_form) == list:
				for term in cost_functional_form:
					if not term.__module__ == 'ufl.form' and type(term).__name__ == 'Form':
						raise InputError('cashocs.optimization_problem.OptimizationProblem', 'cost_functional_form', 'cost_functional_form has to be a ufl form or a list of ufl forms.')
					
				self.use_cost_functional_list = True
				self.cost_functional_list = cost_functional_form
				# generate a dummy cost_functional_form, which is overwritten in _scale_cost_functional
				self.cost_functional_form = summation([term for term in self.cost_functional_list])
					
			elif cost_functional_form.__module__ == 'ufl.form' and type(cost_functional_form).__name__ == 'Form':
				self.cost_functional_form = cost_functional_form
			else:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'cost_functional_form', 'cost_functional_form has to be a ufl form.')
		except:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'cost_functional_form', 'Type of cost_functional_form is wrong.')

		### states
		try:
			if type(states) == list and len(states) > 0:
				for i in range(len(states)):
					if states[i].__module__ == 'dolfin.function.function' and type(states[i]).__name__ == 'Function':
						pass
					else:
						raise InputError('cashocs.optimization_problem.OptimizationProblem', 'states', 'states have to be fenics Functions.')

				self.states = states

			elif states.__module__ == 'dolfin.function.function' and type(states).__name__ == 'Function':
				self.states = [states]
			else:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'states', 'Type of states is wrong.')
		except:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'states', 'Type of states is wrong.')

		### adjoints
		try:
			if type(adjoints) == list and len(adjoints) > 0:
				for i in range(len(adjoints)):
					if adjoints[i].__module__ == 'dolfin.function.function' and type(adjoints[i]).__name__ == 'Function':
						pass
					else:
						raise InputError('cashocs.optimization_problem.OptimizationProblem', 'adjoints', 'adjoints have to fenics Functions.')

				self.adjoints = adjoints

			elif adjoints.__module__ == 'dolfin.function.function' and type(adjoints).__name__ == 'Function':
				self.adjoints = [adjoints]
			else:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'adjoints', 'Type of adjoints is wrong.')
		except:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'adjoints', 'Type of adjoints is wrong.')

		### config
		if config is None:
			self.config = configparser.ConfigParser()
			self.config.add_section('OptimizationRoutine')
			self.config.set('OptimizationRoutine', 'algorithm', 'none')
		else:
			if config.__module__ == 'configparser' and type(config).__name__ == 'ConfigParser':
				self.config = config
			else:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'config', 'config has to be of configparser.ConfigParser type')

		### initial guess
		if initial_guess is None:
			self.initial_guess = initial_guess
		else:
			try:
				if type(initial_guess) == list:
					self.initial_guess = initial_guess
				elif initial_guess.__module__ == 'dolfin.function.function' and type(initial_guess).__name__ == 'Function':
					self.initial_guess = [initial_guess]
			except:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'initial_guess', 'initial guess has to be a list of functions')


		### ksp_options
		if ksp_options is None:
			self.ksp_options = []
			option = [
				['ksp_type', 'preonly'],
				['pc_type', 'lu'],
				['pc_factor_mat_solver_type', 'mumps'],
				['mat_mumps_icntl_24', 1]
			]

			for i in range(self.state_dim):
				self.ksp_options.append(option)

		elif type(ksp_options) == list and type(ksp_options[0]) == list and type(ksp_options[0][0]) == str:
			self.ksp_options = [ksp_options[:]]

		elif type(ksp_options) == list and type(ksp_options[0]) == list and type(ksp_options[0][0]) == list:
			self.ksp_options = ksp_options[:]

		else:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'ksp_options', 'Wrong input format for ksp_options.')



		### adjoint_ksp_options
		if adjoint_ksp_options is None:
			self.adjoint_ksp_options = self.ksp_options[:]

		elif type(adjoint_ksp_options) == list and type(adjoint_ksp_options[0]) == list and type(adjoint_ksp_options[0][0]) == str:
			self.adjoint_ksp_options = [adjoint_ksp_options[:]]

		elif type(adjoint_ksp_options) == list and type(adjoint_ksp_options[0]) == list and type(adjoint_ksp_options[0][0]) == list:
			self.adjoint_ksp_options = adjoint_ksp_options[:]

		else:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'adjoint_ksp_options', 'Wrong input format for adjoint_ksp_options.')

		### desired_weights
		if desired_weights is not None:
			if type(desired_weights) == list:
				for weight in desired_weights:
					if not (type(weight) == int or type(weight) == float):
						raise InputError('cashocs.optimization_problem.OptimizationProblem', 'desired_weights', 'desired_weights needs to be a list of numbers (int or float).')
				
				self.desired_weights = desired_weights
				
			else:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'desired_weights', 'desired_weights needs to be a list of numbers (int or float).')
		else:
			self.desired_weights = None


		if not len(self.bcs_list) == self.state_dim:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'bcs_list', 'Length of states does not match.')
		if not len(self.states) == self.state_dim:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'states', 'Length of states does not match.')
		if not len(self.adjoints) == self.state_dim:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'adjoints', 'Length of states does not match.')

		if self.initial_guess is not None:
			if not len(self.initial_guess) == self.state_dim:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'initial_guess', 'Length of states does not match.')

		if not len(self.ksp_options) == self.state_dim:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'ksp_options', 'Length of states does not match.')
		if not len(self.adjoint_ksp_options) == self.state_dim:
			raise InputError('cashocs.optimization_problem.OptimizationProblem', 'ksp_options', 'Length of states does not match.')
		
		if self.desired_weights is not None:
			try:
				if not len(self.cost_functional_list) == len(self.desired_weights):
					raise InputError('cashocs.optimization_problem.OptimizationProblem', 'desired_weights', 'Length of desired_weights and cost_functional does not match.')
			except:
				raise InputError('cashocs.optimization_problem.OptimizationProblem', 'desired_weights', 'Length of desired_weights and cost_functional does not match.')
			
		fenics.set_log_level(fenics.LogLevel.CRITICAL)

		self.state_problem = None
		self.adjoint_problem = None
		
		self.lagrangian = Lagrangian(self.state_forms, self.cost_functional_form)
		self.form_handler = None
		self.has_custom_adjoint = False
		self.has_custom_derivative = False
		
		self._scale_cost_functional()
Esempio n. 7
0
ffc_options = {"optimize": True}
problem = fe.NonlinearVariationalProblem(F_static,
                                         w,
                                         bcs,
                                         J=J_static,
                                         form_compiler_parameters=ffc_options)
solver = fe.NonlinearVariationalSolver(problem)

# set parameters
prm = solver.parameters
if True:
    iterative_solver = True
    #prm['newton_solver']['absolute_tolerance'] = 1E-3
    #prm['newton_solver']['relative_tolerance'] = 1E-2
    #prm['newton_solver']['maximum_iterations'] = 20
    #prm['newton_solver']['relaxation_parameter'] = 1.0
    prm['newton_solver']['linear_solver'] = 'bicgstab'
    #if iterative_solver:
    #    prm['newton_solver']['krylov_solver']['absolute_tolerance'] = 1E-4
    #    prm['newton_solver']['krylov_solver']['relative_tolerance'] = 1E-5
    #    prm['newton_solver']['krylov_solver']['maximum_iterations'] = 1000

fe.set_log_level(fe.PROGRESS)

solver.solve()

(u, p) = w.split()

file_u << u
file_p << p
Esempio n. 8
0
# https://github.com/matthias-k/cyipopt
from ipopt import minimize_ipopt

import fenics as fn
import fenics_adjoint as fa
import ufl

from jaxfenics_adjoint import build_jax_fem_eval
from jaxfenics_adjoint import numpy_to_fenics, fenics_to_numpy

import matplotlib.pyplot as plt

config.update("jax_enable_x64", True)

fn.set_log_level(fn.LogLevel.ERROR)

tr, sym, grad, Identity = ufl.tr, ufl.sym, ufl.grad, ufl.Identity
inner, dot, dx = ufl.inner, ufl.dot, ufl.dx

# Geometry and elasticity
t, h, L = 2.0, 1.0, 5.0  # Thickness, height and length
E, nu = 210e3, 0.3  # Young Modulus
G = E / (2.0 * (1.0 + nu))  # Shear Modulus
lmbda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu))  # Lambda


def simp(x):
    return eps + (1 - eps) * x**p

Esempio n. 9
0
import time
import fenics

import numpy as np
import matplotlib.pyplot as plt

from fenics import RectangleMesh, Expression, Constant, Function, Point
from fenics import FunctionSpace, TrialFunction, TestFunction, DirichletBC
from fenics import solve, lhs, rhs, errornorm, interpolate, assign
from fenics import dot, grad, dx
from fenics import File

if __name__ == '__main__':
    fenics.set_log_level(30)  # only display warnings or errors

    # here we solve a heat diffusion equation over time:
    # we use a finite difference scheme in time (backward Euler) and a variational approach in space
    # namely we iterate over (small) timesteps, each time solving a Poisson equation via finite elements

    T = 2.0  # final time
    num_steps = 50  # number of time steps
    dt = T / num_steps  # time step size

    # Create mesh and define function space
    nx = ny = 30
    mesh = RectangleMesh(Point(-2, -2), Point(2, 2), nx, ny)
    V = FunctionSpace(mesh, 'P', 1)

    # Define boundary condition
    def boundary(x, on_boundary):
        return on_boundary
Esempio n. 10
0
import time
import os
import csv
from matplotlib import pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
from global_values import *
import shutil

#CRITICAL  = 50, // errors that may lead to data corruption and suchlike
#ERROR     = 40, // things that go boom
#WARNING   = 30, // things that may go boom later
#INFO      = 20, // information of general interest
#PROGRESS  = 16, // what's happening (broadly)
#TRACE     = 13, // what's happening (in detail)
#DBG       = 10  // sundry
fe.set_log_level(30)

#fe.set_log_level(20)


class RHTSolver(object):
    def __init__(self, mesh, V, T_old, bc_T, bc_phi, t_end, phi_old, crho_m,
                 kT, w, dt_fe, delta_p, dps_dT, ps, Dw, xi, max_steps,
                 time_gamma, max_dt, prope_fields, prope_xs, prope_times,
                 order_2nd, v_materials, material_markers, out_dir):
        self.max_steps = max_steps
        self.t_end = t_end
        self.bc_T = bc_T
        self.bc_phi = bc_phi
        self.order_2nd = order_2nd
from __future__ import print_function

import fenics as d

print("Importing...")

from problem import IREProblem

d.set_log_level(1000)

print("Initializing")

ire = IREProblem()

print("Loading")

ire.load()

print("Solving")

ire.solve()

print("Plotting result")

#ire.plot_bitmap_result()
ire.plot_result()
def load_and_clean_results(path):
    data = pd.read_csv(path)
    data = data.replace('\n','', regex=True) 
    data = data.replace('\r', '', regex=True)

    for i in range(data.shape[0]):
        data.xvals[i] = np.asarray(np.matrix(data.xvals[i])).flatten() 
        data.sol[i] = np.asarray(np.matrix(data.sol[i])).flatten() 
        data.num_steps[i] = np.asarray(np.matrix(data.num_steps[i])).flatten() 
    
    return data 


if __name__ == "__main__":

    set_log_level(40)

    nxs = np.arange(20, 1000, 70)
    times = np.arange(20, 1000, 70)

    params = list(itertools.product(nxs, times)) 

    result_lst = []

    for nx, num_steps in params:

        print("="*8 + " Running %s, %s " % (nx, num_steps) + '='*8)
        sol, xvals = run_fokker_planck(nx, num_steps, t_final=100)
        results = {'nx': nx, 
                   'num_steps': num_steps,
                   'xvals': xvals.flatten(), 
Esempio n. 13
0
HAVE_BEMPP = False
try:
    import bempp.api
    from bempp.api.shapes.shapes import __generate_grid_from_geo_string as generate_from_string
    from bempp.api.grid import Grid as BemppGrid
    HAVE_BEMPP = True
except ImportError:
    bempp = None
    BemppGrid = None


HAVE_FENICS = False
try:
    import fenics as fn
    fn.set_log_level(60)
    HAVE_FENICS = True
except ImportError:
    fn = None


class FenicsField(object):
    def __init__(self, field):
        self._field = field
        self._field.set_allow_extrapolation(True)

    def __call__(self, position):
        return self._field(position[0], position[1], position[2])


def calculate_efield_bempp(si):
Esempio n. 14
0
import os
import math
import numpy as np
import fenics as fc

import tensorflow as tf
tflog = tf.logging

# https://github.com/patrick-kidger/tools
import tools

from . import data_gen_base as dgb
from . import exceptions as ex
from . import grid

fc.set_log_level(30)  # i.e. warning


def _create_function_spaces(t, T, a, b, fineness_t, fineness_x):
    """Here we create the function spaces involved (and also the
    mesh as well).
    """
    
    nx = round((b - a) / fineness_x)  # number of space steps

    ### Define periodic boundary
    class PeriodicBoundary(fc.SubDomain):

        def inside(self, x, on_boundary):
            return bool(x[0] < fc.DOLFIN_EPS and 
                        x[0] > -fc.DOLFIN_EPS and 
Esempio n. 15
0
def run(output_dir="output/wang2010_natural_convection_air",
        rayleigh_number=1.e6,
        prandtl_number=0.71,
        stefan_number=0.045,
        heat_capacity=1.,
        thermal_conductivity=1.,
        liquid_viscosity=1.,
        solid_viscosity=1.e8,
        gravity=(0., -1.),
        m_B=None,
        ddT_m_B=None,
        penalty_parameter=1.e-7,
        temperature_of_fusion=-1.e12,
        regularization_smoothing_factor=0.005,
        mesh=fenics.UnitSquareMesh(fenics.dolfin.mpi_comm_world(), 20, 20,
                                   "crossed"),
        initial_values_expression=("0.", "0.", "0.",
                                   "0.5*near(x[0],  0.) -0.5*near(x[0],  1.)"),
        boundary_conditions=[{
            "subspace": 0,
            "value_expression": ("0.", "0."),
            "degree": 3,
            "location_expression":
            "near(x[0],  0.) | near(x[0],  1.) | near(x[1], 0.) | near(x[1],  1.)",
            "method": "topological"
        }, {
            "subspace": 2,
            "value_expression": "0.5",
            "degree": 2,
            "location_expression": "near(x[0],  0.)",
            "method": "topological"
        }, {
            "subspace": 2,
            "value_expression": "-0.5",
            "degree": 2,
            "location_expression": "near(x[0],  1.)",
            "method": "topological"
        }],
        start_time=0.,
        end_time=10.,
        time_step_size=1.e-3,
        stop_when_steady=True,
        steady_relative_tolerance=1.e-4,
        adaptive=False,
        adaptive_metric="all",
        adaptive_solver_tolerance=1.e-4,
        nlp_absolute_tolerance=1.e-8,
        nlp_relative_tolerance=1.e-8,
        nlp_max_iterations=50,
        restart=False,
        restart_filepath=""):
    """Run Phaseflow.
    
    Phaseflow is configured entirely through the arguments in this run() function.
    
    See the tests and examples for demonstrations of how to use this.
    """

    # Handle default function definitions.
    if m_B is None:

        def m_B(T, Ra, Pr, Re):

            return T * Ra / (Pr * Re**2)

    if ddT_m_B is None:

        def ddT_m_B(T, Ra, Pr, Re):

            return Ra / (Pr * Re**2)

    # Report arguments.
    phaseflow.helpers.print_once(
        "Running Phaseflow with the following arguments:")

    phaseflow.helpers.print_once(phaseflow.helpers.arguments())

    phaseflow.helpers.mkdir_p(output_dir)

    if fenics.MPI.rank(fenics.mpi_comm_world()) is 0:

        arguments_file = open(output_dir + "/arguments.txt", "w")

        arguments_file.write(str(phaseflow.helpers.arguments()))

        arguments_file.close()

    # Check if 1D/2D/3D.
    dimensionality = mesh.type().dim()

    phaseflow.helpers.print_once("Running " + str(dimensionality) +
                                 "D problem")

    # Initialize time.
    if restart:

        with h5py.File(restart_filepath, "r") as h5:

            time = h5["t"].value

            assert (abs(time - start_time) < TIME_EPS)

    else:

        time = start_time

    # Define the mixed finite element and the solution function space.
    W_ele = make_mixed_fe(mesh.ufl_cell())

    W = fenics.FunctionSpace(mesh, W_ele)

    # Set the initial values.
    if restart:

        mesh = fenics.Mesh()

        with fenics.HDF5File(mesh.mpi_comm(), restart_filepath, "r") as h5:

            h5.read(mesh, "mesh", True)

        W_ele = make_mixed_fe(mesh.ufl_cell())

        W = fenics.FunctionSpace(mesh, W_ele)

        w_n = fenics.Function(W)

        with fenics.HDF5File(mesh.mpi_comm(), restart_filepath, "r") as h5:

            h5.read(w_n, "w")

    else:

        w_n = fenics.interpolate(
            fenics.Expression(initial_values_expression, element=W_ele), W)

    # Organize the boundary conditions.
    bcs = []

    for item in boundary_conditions:

        bcs.append(
            fenics.DirichletBC(W.sub(item["subspace"]),
                               item["value_expression"],
                               item["location_expression"],
                               method=item["method"]))

    # Set the variational form.
    """Set local names for math operators to improve readability."""
    inner, dot, grad, div, sym = fenics.inner, fenics.dot, fenics.grad, fenics.div, fenics.sym
    """The linear, bilinear, and trilinear forms b, a, and c, follow the common notation 
    for applying the finite element method to the incompressible Navier-Stokes equations,
    e.g. from danaila2014newton and huerta2003fefluids.
    """
    def b(u, q):
        return -div(u) * q  # Divergence

    def D(u):

        return sym(grad(u))  # Symmetric part of velocity gradient

    def a(mu, u, v):

        return 2. * mu * inner(D(u), D(v))  # Stokes stress-strain

    def c(w, z, v):

        return dot(dot(grad(z), w), v)  # Convection of the velocity field

    dt = fenics.Constant(time_step_size)

    Re = fenics.Constant(reynolds_number)

    Ra = fenics.Constant(rayleigh_number)

    Pr = fenics.Constant(prandtl_number)

    Ste = fenics.Constant(stefan_number)

    C = fenics.Constant(heat_capacity)

    K = fenics.Constant(thermal_conductivity)

    g = fenics.Constant(gravity)

    def f_B(T):

        return m_B(T=T, Ra=Ra, Pr=Pr, Re=Re) * g  # Buoyancy force, $f = ma$

    gamma = fenics.Constant(penalty_parameter)

    T_f = fenics.Constant(temperature_of_fusion)

    r = fenics.Constant(regularization_smoothing_factor)

    def P(T):

        return 0.5 * (1. - fenics.tanh(
            (T_f - T) / r))  # Regularized phase field.

    mu_l = fenics.Constant(liquid_viscosity)

    mu_s = fenics.Constant(solid_viscosity)

    def mu(T):

        return mu_s + (mu_l - mu_s) * P(T)  # Variable viscosity.

    L = C / Ste  # Latent heat

    u_n, p_n, T_n = fenics.split(w_n)

    w_w = fenics.TrialFunction(W)

    u_w, p_w, T_w = fenics.split(w_w)

    v, q, phi = fenics.TestFunctions(W)

    w_k = fenics.Function(W)

    u_k, p_k, T_k = fenics.split(w_k)

    F = (b(u_k, q) - gamma * p_k * q + dot(u_k - u_n, v) / dt + c(u_k, u_k, v)
         + b(v, p_k) + a(mu(T_k), u_k, v) + dot(f_B(T_k), v) + C / dt *
         (T_k - T_n) * phi - dot(C * T_k * u_k, grad(phi)) +
         K / Pr * dot(grad(T_k), grad(phi)) + 1. / dt * L *
         (P(T_k) - P(T_n)) * phi) * fenics.dx

    def ddT_f_B(T):

        return ddT_m_B(T=T, Ra=Ra, Pr=Pr, Re=Re) * g

    def sech(theta):

        return 1. / fenics.cosh(theta)

    def dP(T):

        return sech((T_f - T) / r)**2 / (2. * r)

    def dmu(T):

        return (mu_l - mu_s) * dP(T)

    # Set the Jacobian (formally the Gateaux derivative) in variational form.
    JF = (b(u_w, q) - gamma * p_w * q + dot(u_w, v) / dt + c(u_k, u_w, v) +
          c(u_w, u_k, v) + b(v, p_w) + a(T_w * dmu(T_k), u_k, v) +
          a(mu(T_k), u_w, v) + dot(T_w * ddT_f_B(T_k), v) +
          C / dt * T_w * phi - dot(C * T_k * u_w, grad(phi)) -
          dot(C * T_w * u_k, grad(phi)) + K / Pr * dot(grad(T_w), grad(phi)) +
          1. / dt * L * T_w * dP(T_k) * phi) * fenics.dx

    # Set the functional metric for the error estimator for adaptive mesh refinement.
    """I haven't found a good way to make this flexible yet.
    Ideally the user would be able to write the metric, but this would require giving the user
    access to much data that phaseflow is currently hiding.
    """
    M = P(T_k) * fenics.dx

    if adaptive_metric == "phase_only":

        pass

    elif adaptive_metric == "all":

        M += T_k * fenics.dx

        for i in range(dimensionality):

            M += u_k[i] * fenics.dx

    else:

        assert (False)

    # Make the problem.
    problem = fenics.NonlinearVariationalProblem(F, w_k, bcs, JF)

    # Make the solvers.
    """ For the purposes of this project, it would be better to just always use the adaptive solver; but
    unfortunately the adaptive solver encounters nan's whenever evaluating the error for problems not 
    involving phase-change. So far my attempts at writing a MWE to reproduce the  issue have failed.
    """
    adaptive_solver = fenics.AdaptiveNonlinearVariationalSolver(problem, M)

    adaptive_solver.parameters["nonlinear_variational_solver"]["newton_solver"]["maximum_iterations"]\
        = nlp_max_iterations

    adaptive_solver.parameters["nonlinear_variational_solver"]["newton_solver"]["absolute_tolerance"]\
        = nlp_absolute_tolerance

    adaptive_solver.parameters["nonlinear_variational_solver"]["newton_solver"]["relative_tolerance"]\
        = nlp_relative_tolerance

    static_solver = fenics.NonlinearVariationalSolver(problem)

    static_solver.parameters["newton_solver"][
        "maximum_iterations"] = nlp_max_iterations

    static_solver.parameters["newton_solver"][
        "absolute_tolerance"] = nlp_absolute_tolerance

    static_solver.parameters["newton_solver"][
        "relative_tolerance"] = nlp_relative_tolerance

    # Open a context manager for the output file.
    with fenics.XDMFFile(output_dir + "/solution.xdmf") as solution_file:

        # Write the initial values.
        write_solution(solution_file, w_n, time)

        if start_time >= end_time - TIME_EPS:

            phaseflow.helpers.print_once(
                "Start time is already too close to end time. Only writing initial values."
            )

            return w_n, mesh

        # Solve each time step.
        progress = fenics.Progress("Time-stepping")

        fenics.set_log_level(fenics.PROGRESS)

        for it in range(1, MAX_TIME_STEPS):

            if (time > end_time - TIME_EPS):

                break

            if adaptive:

                adaptive_solver.solve(adaptive_solver_tolerance)

            else:

                static_solver.solve()

            time = start_time + it * time_step_size

            phaseflow.helpers.print_once("Reached time t = " + str(time))

            write_solution(solution_file, w_k, time)

            # Write checkpoint/restart files.
            restart_filepath = output_dir + "/restart_t" + str(time) + ".h5"

            with fenics.HDF5File(fenics.mpi_comm_world(), restart_filepath,
                                 "w") as h5:

                h5.write(mesh.leaf_node(), "mesh")

                h5.write(w_k.leaf_node(), "w")

            if fenics.MPI.rank(fenics.mpi_comm_world()) is 0:

                with h5py.File(restart_filepath, "r+") as h5:

                    h5.create_dataset("t", data=time)

            # Check for steady state.
            if stop_when_steady and steady(W, w_k, w_n,
                                           steady_relative_tolerance):

                phaseflow.helpers.print_once(
                    "Reached steady state at time t = " + str(time))

                break

            # Set initial values for next time step.
            w_n.leaf_node().vector()[:] = w_k.leaf_node().vector()

            # Report progress.
            progress.update(time / end_time)

            if time >= (end_time - fenics.dolfin.DOLFIN_EPS):

                phaseflow.helpers.print_once("Reached end time, t = " +
                                             str(end_time))

                break

    # Return the interpolant to sample inside of Python.
    w_k.rename("w", "state")

    return w_k, mesh
Esempio n. 16
0
# -*- encoding: utf-8 -*-
"""
Desc      :   solver for layout-generator equation.
"""
# File    :   fenics_solver.py
# Time    :   2020/03/29 15:16:48
# Author  :   Zweien
# Contact :   [email protected]

import fenics as fs

fs.set_log_level(40)  # ERROR = 40
TOL = 1e-14


class Source(fs.UserExpression):
    """热源布局"""
    def __init__(self, layouts, length, length_unit, powers):
        """

        Arguments:
            layouts {list or int} -- 组件摆放位置
            length {float} -- 布局板尺寸
            length_unit {float} -- 组件尺寸
        """
        super().__init__(self)
        self.layout_list = layouts if isinstance(layouts, list) else [layouts]
        self.length = length
        self.length_unit = length_unit
        self.n = length / length_unit  # unit_per_row
        self.powers = powers
Esempio n. 17
0
from simulation import  ElastodynamicSimulationParameters, Simulation, CommonSimulationParameters, DDDbParameters,\
    DDSolverSimulationParameters
import logging
import fenics

logging.getLogger('FFC').setLevel(logging.WARNING)
logging.getLogger('UFL').setLevel(logging.WARNING)
fenics.set_log_level(logging.WARNING)
# simulation = Simulation(simulation_parameters=ElastodynamicSimulationParameters(),
#                         common_simulation_parameters=CommonSimulationParameters())
#
simulation = Simulation(
    simulation_parameters=DDDbParameters(),
    common_simulation_parameters=CommonSimulationParameters())

# simulation = Simulation(simulation_parameters=DDSolverSimulationParameters(),
#                         common_simulation_parameters=CommonSimulationParameters())

simulation.run()
def main():
    fenics.set_log_level(30)
    np.random.seed(1226)

    # problem parameters
    dim = 2
    tol = 1e-5                              # tol is used for not drawing nodes from the boundary
    num_data = 100                          # sample's dimension of data collected
    noise_sigma = .3                        # since we generate the data we add some artificial noise
    logit_true_z = np.array([.25, .75])

    # distribution parameters
    prior_means = np.array([0., 0.])
    prior_sigmas = np.array([1.5, 1.5])
    proposal_sigma = .05
    gamma_parameters = np.array([1., 1.])

    # MCMC parameters
    samples = 50000
    subchain_len = 1000
    upper_th = 1e-4
    error_th = 1e-2
    init_z = np.array([.0, .0])
    init_sigma = 1.
    init_radius = .1
    rho = .9
    burn = 2000

    # surrogate parameters
    use_gpr = True
    quad_points = 20
    multi_fidelity_q = 12

    # definition of the forward model for data generation - finer grid to avoid inverse crimes
    equation = 'exp(-100*(pow(x[0] - param0, 2) + pow(x[1] - param1, 2)))'
    data_gen_forward_model = PoissonEquation(
        np.array([128, 128]), equation,
        np.array([.5, .5]), '0', reparam=True)

    def pressure(xx, yy):
        xy = np.array([xx.flatten(), yy.flatten()])
        zz = np.array([np.exp(-100 * ((x_ - logit_true_z[0])**2 + (y_ - logit_true_z[1])**2)) for x_, y_ in xy.T])
        return zz.reshape(xx.shape)

    def displacement(xx, yy):
        xy = np.array([xx.flatten(), yy.flatten()])
        zz = data_gen_forward_model(true_z, xy)
        return np.exp(zz.reshape(xx.shape))

    # generation of the dataset
    true_z = logit(logit_true_z)
    x = np.random.uniform(0 + tol, 1 - tol, size=(2, num_data))
    true_data = data_gen_forward_model(true_z, x)
    noise = np.random.normal(0, noise_sigma, size=true_data.shape)
    data = true_data + noise

    # surface plots of the true solution
    surface_plot(
        [0+tol, 1-tol], [0+tol, 1-tol], displacement, step=.025, angles=(35, 110), save=True)
    surface_plot(
        [0+tol, 1-tol], [0+tol, 1-tol], displacement, step=.025, color_fun=pressure, angles=(35, 110), save=True)

    # wireframe plot of the solution with sampled data
    fig, ax = wireframe_plot(
        [0+tol, 1-tol], [0+tol, 1-tol], displacement, step=.025, angles=(35, 110), show=False)
    points_plot(fig, ax, x, np.exp(data), color=noise, save=True)

    # forward model for the MCMCs
    forward_model = PoissonEquation(
        np.array([32, 32]), equation,
        np.array([.5, .5]), '0', reparam=True)

    # useful distributions and densities
    def log_prior(z_):
        return -np.sum((z_ - prior_means) ** 2 / (2 * prior_sigmas ** 2))

    prior = cpy.J(*[cpy.Normal(m, s) for m, s in zip(prior_means, prior_sigmas)])
    log_err_density = GaussDensity(1.)
    proposal = GaussDensity(proposal_sigma)
    full_cnd_sigma2 = CondInvGamma(*gamma_parameters)

    # models
    hfm = HighFidelityModel(
        forward_model, data, x, log_err_density, log_prior)
    lfm = PCESurrogate(data, log_err_density, prior, log_prior, 2, multi_fidelity_q)
    gps = GPSurrogate(data, log_err_density, prior, log_prior, RBF(.1), multi_fidelity_q)

    if use_gpr:
        low_fi_models = [lfm, gps]
        surrogate_types = ['PCE', 'GPR']
    else:
        low_fi_models = [lfm]
        surrogate_types = ['PCE']
    method_names = ['true model (MH)'] + \
        ['{} surrogate (MH)'.format(typ) for typ in surrogate_types] + \
        ['{} surr. (adap. MH)'.format(typ) for typ in surrogate_types]

    # remapping to the physical space
    def inv_logit(x_):
        return 1./(1. + np.exp(-x_))

    # running MCMCs
    fit_times, fit_calls, exec_times, exec_calls, mh_samples = run_and_track(
        hfm, low_fi_models, quad_points,
        proposal, full_cnd_sigma2, init_z, init_sigma,
        samples, subchain_len, upper_th, error_th, init_radius, rho,
        remap_functions=[inv_logit, inv_logit, None])

    # displaying results
    diagnostics_report(
        method_names, exec_times, exec_calls, fit_times, fit_calls,
        len(low_fi_models), samples, burn, mh_samples)
    visual_inspection(dim, method_names, mh_samples, samples, burn, save=True)
from pytest_check import check
import fdm
import jax
from jax.config import config
import jax.numpy as np

import fenics
import ufl

from jaxfenics import build_jax_solve_eval_fwd

config.update("jax_enable_x64", True)
fenics.parameters["std_out_all_processes"] = False
fenics.set_log_level(fenics.LogLevel.ERROR)

mesh = fenics.UnitSquareMesh(3, 2)
V = fenics.FunctionSpace(mesh, "P", 1)


def solve_fenics(kappa0, kappa1):

    f = fenics.Expression(
        "10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)", degree=2)

    u = fenics.Function(V)
    bcs = [fenics.DirichletBC(V, fenics.Constant(0.0), "on_boundary")]

    inner, grad, dx = ufl.inner, ufl.grad, ufl.dx
    JJ = 0.5 * inner(kappa0 * grad(u), grad(u)) * dx - kappa1 * f * u * dx
    v = fenics.TestFunction(V)
    F = fenics.derivative(JJ, u, v)
import fenics as fe
import numpy as np
import matplotlib.pyplot as plt
import sys
from Artery_class import *
from Global import *
from utils import *
plt.rcParams.update({'font.size': 12})

fe.set_log_level(40)

class Artery_Network:

    ''' Class defines the artery network and its connectivity '''
    def __init__(self, inputFile, dt, theta, nt=None):
        self.input = inputFile
        (self.numVessels, self.connectivity, self.vesselIDs) = self.getConnectivity()
        self.paramDict = self.getParameters()
        self.Arteries = []
        ne_max = 0
        self.E_array  = np.zeros(self.numVessels)
        self.h0_array = np.zeros(self.numVessels) 
        self.id_array = np.zeros(self.numVessels)
        self.ne_array = np.zeros(self.numVessels)
        self.A0_array = np.zeros(self.numVessels)
        self.L_array  = np.zeros(self.numVessels)
        for i in range(0,self.numVessels):
            L    = self.paramDict["L"][i]
            ne   = self.paramDict["ne"][i]
            r0   = self.paramDict["r0"][i]
            Q0   = self.paramDict["Q0"][i]
Esempio n. 21
0
import fenics
import pde
import rheology
from settings import *
import testcases

fenics.set_log_level(fenics.ERROR)

fluid = rheology.Sigmoid(rho, mu, ty, eps)
problem = testcases.Poiseuille(nx, ny)
u, u0 = problem.define_functions()
algorithm = pde.Abali(problem, dt, fluid, u, u0)

outdir = './output/'
file_p = fenics.File(outdir + problem.name + '_' + fluid.name + '_' +
                     'pressure.pvd')
file_v = fenics.File(outdir + problem.name + '_' + fluid.name + '_' +
                     'velocity.pvd')

t = 0.0
step = 0
change = 1
print('step\t time\t vmax\t change')

while t < t_end and change > sstol:
    step += 1
    t += dt
    algorithm.advance(u)

    pressure, velocity = u.split(deepcopy=True)
Esempio n. 22
0
    def init_V(self):
        '''
        This function sets up various parameters for the calculation of
        the chemical potential profile using fenics.
        It is generally assumed that during the simulation of a 'sample'
        the following are unchanged:
        - dopant positions
        - electrode positions/number
        Note: only 2D support for now
        '''
        # Turn off log messages
        fn.set_log_level(logging.WARNING)

        # Put electrode positions and values in a dict
        self.fn_electrodes = {}
        for i in range(self.P):
            self.fn_electrodes[f'e{i}_x'] = self.electrodes[i, 0]
            if(self.dim > 1):
                self.fn_electrodes[f'e{i}_y'] = self.electrodes[i, 1]
            self.fn_electrodes[f'e{i}'] = self.electrodes[i, 3]
        for i in range(self.static_electrodes.shape[0]):
            self.fn_electrodes[f'es{i}_x'] = self.static_electrodes[i, 0]
            if(self.dim > 1):
                self.fn_electrodes[f'es{i}_y'] = self.static_electrodes[i, 1]
            self.fn_electrodes[f'es{i}'] = self.static_electrodes[i, 3]


        # Define boundary expression string
        self.fn_expression = ''
        if(self.dim == 1):
            for i in range(self.P):
                self.fn_expression += (f'x[0] == e{i}_x ? e{i} : ')
            for i in range(self.static_electrodes.shape[0]):
                self.fn_expression += (f'x[0] == es{i}_x ? es{i} : ')

        if(self.dim == 2):
            surplus = self.xdim/10  # Electrode modelled as point +/- surplus
            #TODO: Make this not hardcoded
            for i in range(self.P):
                if(self.electrodes[i, 0] == 0 or self.electrodes[i, 0] == self.xdim):
                    self.fn_expression += (f'x[0] == e{i}_x && '
                                           f'x[1] >= e{i}_y - {surplus} && '
                                           f'x[1] <= e{i}_y + {surplus} ? e{i} : ')
                else:
                    self.fn_expression += (f'x[0] >= e{i}_x - {surplus} && '
                                           f'x[0] <= e{i}_x + {surplus} && '
                                           f'x[1] == e{i}_y ? e{i} : ')
            for i in range(self.static_electrodes.shape[0]):
                if(self.static_electrodes[i, 0] == 0 or self.static_electrodes[i, 0] == self.xdim):
                    self.fn_expression += (f'x[0] == es{i}_x && '
                                           f'x[1] >= es{i}_y - {surplus} && '
                                           f'x[1] <= es{i}_y + {surplus} ? es{i} : ')
                else:
                    self.fn_expression += (f'x[0] >= es{i}_x - {surplus} && '
                                           f'x[0] <= es{i}_x + {surplus} && '
                                           f'x[1] == es{i}_y ? es{i} : ')

        self.fn_expression += f'{self.mu}'  # Add constant chemical potential

        # Define boundary expression
        self.fn_boundary = fn.Expression(self.fn_expression,
                                         degree = 1,
                                         **self.fn_electrodes)

        # Define FEM mesh (res should be small enough, otherwise solver may break)
        if(self.dim == 1):
            self.fn_mesh = fn.IntervalMesh(int(self.xdim//self.res), 0, self.xdim)
        if(self.dim == 2):
            self.fn_mesh = fn.RectangleMesh(fn.Point(0, 0),
                                            fn.Point(self.xdim, self.ydim),
                                            int(self.xdim//self.res),
                                            int(self.ydim//self.res))

        # Define function space
        self.fn_functionspace = fn.FunctionSpace(self.fn_mesh, 'P', 1)

        # Define fenics boundary condition
        self.fn_bc = fn.DirichletBC(self.fn_functionspace,
                                    self.fn_boundary,
                                    self.fn_onboundary)

        # Write problem as fn_a == fn_L
        self.V = fn.TrialFunction(self.fn_functionspace)
        self.fn_v = fn.TestFunction(self.fn_functionspace)
        self.fn_a = fn.dot(fn.grad(self.V), fn.grad(self.fn_v)) * fn.dx
        self.fn_f = fn.Constant(0)
        self.fn_L = self.fn_f*self.fn_v*fn.dx

        # Solve V
        self.V = fn.Function(self.fn_functionspace)
        fn.solve(self.fn_a == self.fn_L, self.V, self.fn_bc)