Beispiel #1
0
    def test_solve_2dheatequation(self):
        def sinusoidal_initial_conditions(X, Y):
            """
            Creates initial conditions with a sinusoidal wave in the x and y directions.
            """
            return [
                np.sin(4 * np.pi * X) * np.sin(2 * np.pi * Y),
                np.sin(4 * np.pi * X) * np.sin(2 * np.pi * Y)
            ]

        testtimes = np.linspace(0.0, 1.0, 20)
        testparams = [0.05, 0.01]  # diffusion coefficients
        s = Solver([0.0, 1.0], [0.0, 1.0], 50, sinusoidal_initial_conditions)
        s.set_timeStepLength(0.0001)
        u, v = s.solve(testtimes, testparams)

        for i in range(len(testtimes)):
            for j in range(50):
                for k in range(50):
                    self.assertAlmostEqual(
                        u[i, j, k],
                        np.sin(4 * np.pi * s.X[j, k]) *
                        np.sin(2 * np.pi * s.Y[j, k]) *
                        np.exp(-testparams[0] * 20 * np.pi**2 * testtimes[i]),
                        places=2)
                    self.assertAlmostEqual(
                        v[i, j, k],
                        np.sin(4 * np.pi * s.X[j, k]) *
                        np.sin(2 * np.pi * s.Y[j, k]) *
                        np.exp(-testparams[1] * 20 * np.pi**2 * testtimes[i]),
                        places=2)
Beispiel #2
0
 def test_solve(self):
     testtimes = np.array([0.0, 1.0])
     testparams = np.array([0.01, 0.03])
     s = Solver([0.0, 1.0], [0.0, 1.0], 100, lambda X, Y: [X, Y])
     u, v = s.solve(testtimes, testparams)
     self.assertTupleEqual(u.shape, (2, 100, 100))
     self.assertTupleEqual(v.shape, (2, 100, 100))
Beispiel #3
0
 def test_set_grid(self):
     testxbounds = np.random.random(2)
     testybounds = np.random.random(2)
     testgridsize = 50
     testfun = lambda X, Y: [
         np.sin(4 * np.pi * X) * np.sin(2 * np.pi * Y),
         np.sin(4 * np.pi * X) * np.sin(2 * np.pi * Y)
     ]
     # Expected values
     expected_x = np.linspace(testxbounds[0], testxbounds[1],
                              testgridsize + 1)[:-1]
     expected_y = np.linspace(testybounds[0], testybounds[1],
                              testgridsize + 1)[:-1]
     expected_X, expected_Y = np.meshgrid(expected_x, expected_y)
     # Test
     s = Solver(np.random.random(2), np.random.random(2),
                np.random.randint(1, 100), testfun)
     s.set_grid(testxbounds, testybounds, testgridsize)
     # Checks
     self.assertTrue(np.array_equal(s.xBounds, testxbounds))
     self.assertTrue(np.array_equal(s.yBounds, testybounds))
     self.assertEqual(s.gridSize, testgridsize)
     self.assertEqual(s.xStepLength,
                      (testxbounds[1] - testxbounds[0]) / (testgridsize))
     self.assertEqual(s.yStepLength,
                      (testybounds[1] - testybounds[0]) / (testgridsize))
     self.assertTrue(np.array_equal(s.x, expected_x))
     self.assertTrue(np.array_equal(s.y, expected_y))
     self.assertTrue(np.array_equal(s.X, expected_X))
     self.assertTrue(np.array_equal(s.Y, expected_Y))
     self.assertTrue(
         np.array_equal(s.initialConditions,
                        testfun(expected_X, expected_Y)))
Beispiel #4
0
 def test_set_initialConditions(self):
     testfun = lambda X, Y: [np.sin(X) * np.sin(Y), np.cos(X) * np.cos(Y)]
     s = Solver(np.random.random(2), np.random.random(2),
                np.random.randint(1, 100), lambda X, Y: [X, Y])
     s.set_initialConditions(testfun)
     self.assertTrue(
         np.array_equal(s.initialConditions_u,
                        testfun(s.X, s.Y)[0]))
     self.assertTrue(
         np.array_equal(s.initialConditions_v,
                        testfun(s.X, s.Y)[1]))
     self.assertEqual(s.initial_condition_function, testfun)
Beispiel #5
0
    def set_model(self, xBounds, yBounds, initial_conditions_function):
        '''
        Sets up the reaction diffusion model we wish to consider. We determine the x and y boundaries for consideration
        and provide some initial conditions for our system.

        :param xBounds: x-range of the problem
        :type u_data: list of 2 floats
        :param yBounds: y-range of the problem
        :type u_data: list of 2 floats
        :param initial_condition_function: calculates the values of u and v at t=0 at each gridpoint
        :type initial_condition_function: function that takes two 2d numpy arrays and returns a list of two 2d numpy arrays
        '''

        self.solver = Solver(xBounds, yBounds, self.gridsize,
                             initial_conditions_function)
 def test_set_model(self):
     testudata = np.zeros((2, 50, 50))  # doesn't matter
     testvdata = [0]  # doesn't matter
     testtimes = [0]  # doesn't matter
     infr = Inference(testudata, testvdata, testtimes)
     s = Solver(np.random.random(2), np.random.random(2), 50,
                lambda X, Y: [X, Y])
     infr.set_model(s.xBounds, s.yBounds, s.initial_condition_function)
     #self.assertTrue(np.array_equal(s.xBounds, infr.solver.xBounds))
     #self.assertTrue(np.array_equal(s.yBounds, infr.solver.yBounds))
     #self.assertEqual(s.gridSize, infr.solver.gridSize)
     s_vars = vars(s)
     infr_vars = vars(infr.solver)
     self.assertTrue(s_vars.keys() == infr_vars.keys())
     # Compare the two objects attribute by attribute
     for key in [
             "initial_condition_function", "initialConditions_u",
             "initialConditions_v", "xBounds", "yBounds", "gridSize",
             "xStepLength", "yStepLength", "x", "y", "X", "Y",
             "initialConditions"
     ]:
         if isinstance(s_vars[key], (list, tuple, np.ndarray)):
             self.assertTrue(np.array_equal(s_vars[key], infr_vars[key]))
         else:
             self.assertTrue(s_vars[key] == infr_vars[key])
Beispiel #7
0
    def test_solve_oscillation(self):
        def uniform_initial_conditions(X, Y):
            """
            Creates uniform initial conditions with u = 1, v = 0
            """
            return [np.ones_like(X), np.zeros_like(X)]

        def oscillation_reactionfunction(u, v, parameters):
            """
            Reaction function with a sinusoidal exact solution.
            """
            w = parameters[0]  # angular speed
            return [w * v, -w * u]

        testtimes = np.linspace(0, 2 * np.pi, 20)
        for w in [1.0, 2.0, 3.0]:  # angular speed
            testparams = np.array([0.01, 0.01, w])
            s = Solver([0.0, 1.0], [0.0, 1.0], 20, uniform_initial_conditions)
            s.set_reactionFunction(oscillation_reactionfunction)
            s.set_timeStepLength(0.0001)
            u, v = s.solve(testtimes, testparams)

            for i in range(len(testtimes)):
                # solution remains uniform
                self.assertAlmostEqual(np.std(u[i]), 0)
                self.assertAlmostEqual(np.std(v[i]), 0)

                # solution is sine & cosine with given angular speed
                self.assertAlmostEqual(u[i, 10, 10],
                                       np.cos(w * testtimes[i]),
                                       places=3)
                self.assertAlmostEqual(v[i, 10, 10],
                                       -np.sin(w * testtimes[i]),
                                       places=3)
Beispiel #8
0
    def test_solve_exponential(self):
        t = np.linspace(0, 5.0, 6)
        testparams = np.array([1.0, 2.0])
        k1, k2 = testparams

        def reaction_function(u, v, K):
            return [-K[0] * u, -K[1] * v]

        def exact(x, y, t):
            return [1.0 * np.exp(-k1 * t), 2.0 * np.exp(-k2 * t)]

        def initial_conditions(x, y):
            return exact(x, y, 0)

        solver = Solver([0.0, 1.0], [0.0, 1.0], 30, initial_conditions)
        solver.set_timeStepLength(0.0001)
        solver.set_reactionFunction(reaction_function)
        u, v = solver.solve(t, [5.0, 1.0, k1, k2])
        T, Y, X = np.meshgrid(t, solver.y, solver.x, indexing='ij')
        solution = exact(X, Y, T)

        error_u = np.max(np.abs((u - solution[0])))
        error_v = np.max(np.abs((v - solution[1])))
        print(error_u)
        print(error_v)

        # Check Uniform
        for i in range(len(t)):
            self.assertAlmostEqual(np.std(u[i]), 0)
            self.assertAlmostEqual(np.std(v[i]), 0)

        self.assertLess(error_u, 1e-4)
        self.assertLess(error_v, 1e-4)
 def test_set_reaction_function(self):
     testudata = np.zeros((2, 50, 50))  # doesn't matter
     testvdata = [0]  # doesn't matter
     testtimes = [0]  # doesn't matter
     testfun = lambda X: X * X
     infr = Inference(testudata, testvdata, testtimes)
     s = Solver(np.random.random(2), np.random.random(2), 50,
                lambda X, Y: [X, Y])
     infr.solver = s
     infr.set_reaction_function(testfun)
     self.assertEqual(infr.solver.reactionFunction, testfun)
Beispiel #10
0
    def test_solve_1ddiffusion(self):
        mode = 1.0
        D1 = 0.05
        D2 = 0.0
        t = np.linspace(0, 1.0, 11)

        def exact(x, y, t):
            return [
                np.sin(4 * np.pi * mode * x) * np.exp(-D1 * t *
                                                      (4 * np.pi * mode)**2), 1
            ]

        def initial_conditions(x, y):
            return exact(x, y, 0)

        solver = Solver([0.0, 1.0], [0.0, 1.0], 200, initial_conditions)
        solver.set_timeStepLength(0.0001)

        u, v = solver.solve(t, [D1, D2, 0])

        T, Y, X = np.meshgrid(t, solver.y, solver.x, indexing='ij')
        solution = exact(X, Y, T)

        error_u = np.max(np.abs((u - solution[0])))
        error_v = np.max(np.abs((v - solution[1])))

        # Check Uniform
        for i in range(len(t)):
            for j in range(len(solver.x)):
                self.assertAlmostEqual(np.std(u[i, :, j]), 0)

        self.assertAlmostEqual(np.std(v), 0)
        self.assertAlmostEqual(v[1, 1, 1], 1)

        self.assertLess(error_u, 3 * 1e-4)
        self.assertLess(error_v, 1e-4)
Beispiel #11
0
 def test_set_timeStepLength(self):
     testlen = np.random.random(1)
     s = Solver(np.random.random(2), np.random.random(2),
                np.random.randint(1, 100), lambda X, Y: [X, Y])
     s.set_timeStepLength(testlen)
     self.assertAlmostEqual(s.timeStepLength, testlen)
Beispiel #12
0
 def test_set_reactionFunction(self):
     testfun = lambda X: 2 * X
     s = Solver(np.random.random(2), np.random.random(2),
                np.random.randint(1, 100), lambda X, Y: [X, Y])
     s.set_reactionFunction(testfun)
     self.assertEqual(s.reactionFunction, testfun)
# Rewrite diffusion coefficients to match the form of solver
Du = 1 / eps
Dv = dv


# Define Reaction Function
def fitzhugh_naguomo(u, v, parameters):
    e = parameters[0]
    d = parameters[1]
    return [(3 * u - u**3 - v) / e, u - d * v]


# Initialise Solver
# Set x, y bounds and grid size
# Set initial condition function to obtain spiralling behavior
solver = Solver(xbounds, ybounds, 256, initial_conditions_spiral)

# Set reaction terms
solver.set_reactionFunction(fitzhugh_naguomo)

# Set time step lengths for backward Euler integration scheme
solver.set_timeStepLength(0.01)

# Define array of times at which to record solution
t = np.linspace(0, 30, 100)

# Run solve method of solver.
# Input time array of times to record solution and list of parameters for the system.
# First two parameters are diffusion coefficients, remaining parameters are passed to diffusion function
# Return solution arrays for u, v
u, v = solver.solve(t, [Du, Dv, eps, delta])
Beispiel #14
0
class Inference(Solver):
    def __init__(self, u_data, v_data, times):
        '''
        Initializes our inference class by receiving the observed grids of u and v values and the times in the time
        series. We set the x and y boundaries and the initial conditions separately.

        :param u_data: observed values of u at each point in the grid at each time in the time series
        :type u_data: n x gridsize x gridsize numpy array
        :param u_data: observed values of v at each point in the grid at each time in the time series
        :type u_data: n x gridsize x gridsize numpy array
        :param times: array of n evenly-spaced times
        :type times: 1-dimensional numpy array
        '''

        self.u_data = u_data
        self.v_data = v_data
        self.times = times

        self.gridsize = u_data.shape[
            1]  #infer the gridsize from this dimension

    def set_model(self, xBounds, yBounds, initial_conditions_function):
        '''
        Sets up the reaction diffusion model we wish to consider. We determine the x and y boundaries for consideration
        and provide some initial conditions for our system.

        :param xBounds: x-range of the problem
        :type u_data: list of 2 floats
        :param yBounds: y-range of the problem
        :type u_data: list of 2 floats
        :param initial_condition_function: calculates the values of u and v at t=0 at each gridpoint
        :type initial_condition_function: function that takes two 2d numpy arrays and returns a list of two 2d numpy arrays
        '''

        self.solver = Solver(xBounds, yBounds, self.gridsize,
                             initial_conditions_function)

    def set_reaction_function(self, function):
        '''
        This is optional. We can choose to provide a nonlinear term to our system.

        :param function: calculates the value of the reaction terms at the given u and v
        :type function: function that takes two numpy arrays (containing values of u and v) and a list of parameters and returns a list of two numpy arrays
        '''

        self.solver.set_reactionFunction(function)

    def _error_func(self):
        '''
        Returns L2 error between output using current proposed set of parameters and the observed data

        :return tot_error: the total sum of L2 errors at each timestep between the true u,v values and the solved values
        '''

        tot_error = 0
        for i in range(len(self.times)):
            u_diff_mat = self.u_data[i, :, :] - self.u_output[i, :, :]
            v_diff_mat = self.v_data[i, :, :] - self.v_output[i, :, :]
            tot_error += np.sum(np.square(u_diff_mat)) + np.sum(
                np.square(v_diff_mat))
        return tot_error

    def cost_func(self, parametervalues):
        '''
        Takes newly proposed set of parameter values and returns the L2 error between the values of u,v given by the
        solver with these values and the values of u,v from the input

        :param parametervalues: the proposed set of parameter values at this point in the optimization
        :return: the L2 error with these parameters
        '''

        self.u_output, self.v_output = self.solver.solve(
            self.times, parametervalues)
        return self._error_func()

    def fit_params(self, x0):
        '''
        The master function to call. Fits parameters from some initial estimate by minimizing the cost function using
        the Nelder-Mead method.

        :param x0: the initial estimate of parameters
        :return best_fit_parameters: a vector of the fitted parameters
        '''

        res = optimize.minimize(self.cost_func, x0, method='Nelder-Mead')
        best_fit_parameters = res.x
        return best_fit_parameters