Пример #1
0
 def setUp(self):
     # Domain
     nx = ny = nz = 10
     mesh = fenics.RectangleMesh(fenics.Point(-2, -2), fenics.Point(2, 2),
                                 nx, ny)
     # function spaces
     displacement_element = fenics.VectorElement("Lagrange",
                                                 mesh.ufl_cell(), 1)
     concentration_element = fenics.FiniteElement("Lagrange",
                                                  mesh.ufl_cell(), 1)
     element = fenics.MixedElement(
         [displacement_element, concentration_element])
     subspace_names = {0: 'displacement', 1: 'concentration'}
     functionspace = FunctionSpace(mesh)
     functionspace.init_function_space(element, subspace_names)
     # build a 'solution' function
     u_0_conc_expr = fenics.Expression(
         'sqrt(pow(x[0]-x0,2)+pow(x[1]-y0,2)) < 0.1 ? (1.0) : (0.0)',
         degree=1,
         x0=0.25,
         y0=0.5)
     u_0_disp_expr = fenics.Constant((0.0, 0.0))
     self.U = functionspace.project_over_space(function_expr={
         0: u_0_disp_expr,
         1: u_0_conc_expr
     })
     self.tsd = TimeSeriesData(functionspace=functionspace, name='solution')
Пример #2
0
 def setUp(self):
     # Domain
     nx = ny = nz = 10
     self.mesh = fenics.RectangleMesh(fenics.Point(-2, -2),
                                      fenics.Point(2, 2), nx, ny)
     # function spaces
     self.displacement_element = fenics.VectorElement(
         "Lagrange", self.mesh.ufl_cell(), 1)
     self.concentration_element = fenics.FiniteElement(
         "Lagrange", self.mesh.ufl_cell(), 1)
     self.element = fenics.MixedElement(
         [self.displacement_element, self.concentration_element])
     subspace_names = {0: 'displacement', 1: 'concentration'}
     self.functionspace = FunctionSpace(self.mesh)
     self.functionspace.init_function_space(self.element, subspace_names)
     # subdomains
     label_funspace = fenics.FunctionSpace(self.mesh, "DG", 1)
     label_expr = fenics.Expression('(x[0]>=0) ? (1.0) : (2.0)', degree=1)
     labels = fenics.project(label_expr, label_funspace)
     self.labels = labels
     self.tissue_id_name_map = {0: 'outside', 1: 'tissue', 2: 'tumor'}
     self.parameter = {'outside': 0.0, 'tissue': 1.0, 'tumor': 0.1}
     self.boundary = Boundary()
     boundary_dict = {
         'boundary_1': self.boundary,
         'boundary_2': self.boundary
     }
     self.boundary_dict = boundary_dict
     self.subdomains = SubDomains(self.mesh)
     self.subdomains.setup_subdomains(label_function=self.labels)
     self.subdomains.setup_boundaries(tissue_map=self.tissue_id_name_map,
                                      boundary_fct_dict=self.boundary_dict)
     self.subdomains.setup_measures()
     # parameter instance
     self.params = Parameters(self.functionspace, self.subdomains)
Пример #3
0
 def __init__(self, mesh, time_dependent=True):
     """
     Init routine.
     :param mesh: The mesh.
     :param time_dependent: Boolean switch indicating whether this simulation is time-dependent.
     """
     self.logger = logging.getLogger(__name__)
     self.mesh = mesh
     self.geometric_dimension = self.mesh.geometry().dim()
     self.time_dependent = time_dependent
     self.projection_parameters = {
         'solver_type': 'cg',
         'preconditioner_type': 'amg'
     }
     self.functionspace = FunctionSpace(
         self.mesh, projection_parameters=self.projection_parameters)
     self._define_model_params()
     if fenics.is_version("<2018.1.x"):
         pass
     else:
         if config.USE_ADJOINT:
             self.tape = fenics.get_working_tape()
Пример #4
0
    def test_split_function(self):
        subspace_names = {0: 'displacement', 1: 'concentration'}
        functionspace = FunctionSpace(self.mesh)
        functionspace.init_function_space(self.element, subspace_names)

        u_0_conc_expr = fenics.Expression('sqrt(pow(x[0]-x0,2)+pow(x[1]-y0,2)) < 0.1 ? (1.0) : (0.0)', degree=1,
                                          x0=0.25,
                                          y0=0.5)
        u_0_disp_expr = fenics.Constant((0.0, 0.0))
        U_orig = functionspace.project_over_space(function_expr={0: u_0_disp_expr, 1: u_0_conc_expr})

        U = functionspace.split_function(U_orig)
        self.assertEqual(U_orig, U)
        U_1 = functionspace.split_function(U_orig, subspace_id=1)
        U_0 = functionspace.split_function(U_orig, subspace_id=0)
Пример #5
0
 def setUp(self):
     # Domain
     nx = ny = nz = 10
     self.nx, self.ny = nx, ny
     self.mesh = fenics.RectangleMesh(fenics.Point(-2, -2), fenics.Point(2, 2), nx, ny)
     # function spaces
     self.displacement_element = fenics.VectorElement("Lagrange", self.mesh.ufl_cell(), 1)
     self.concentration_element = fenics.FiniteElement("Lagrange", self.mesh.ufl_cell(), 1)
     self.element = fenics.MixedElement([self.displacement_element, self.concentration_element])
     subspace_names = {0: 'displacement', 1: 'concentration'}
     self.functionspace = FunctionSpace(self.mesh)
     self.functionspace.init_function_space(self.element, subspace_names)
     # define 'solution' with concentration=1 everywhere
     self.conc_expr =  fenics.Constant(1.0)
     self.conc = self.functionspace.project_over_space(self.conc_expr, subspace_name='concentration')
     # subdomains
     label_funspace = fenics.FunctionSpace(self.mesh, "DG", 1)
     label_expr = fenics.Expression('(x[0]>=0) ? (1.0) : (2.0)', degree=1)
     labels = fenics.project(label_expr, label_funspace)
     self.labels = labels
     self.tissue_id_name_map = {0: 'outside',
                                1: 'tissue',
                                2: 'tumor'}
     self.parameter = {'outside': 0.0,
                       'tissue': 1.0,
                       'tumor': 0.1}
     self.boundary_pos = BoundaryPos()
     self.boundary_neg = BoundaryNeg()
     boundary_dict = {'boundary_pos': self.boundary_pos,
                      'boundary_neg': self.boundary_neg}
     self.boundary_dict = boundary_dict
     self.subdomains = SubDomains(self.mesh)
     self.subdomains.setup_subdomains(label_function=self.labels)
     self.subdomains.setup_boundaries(tissue_map=self.tissue_id_name_map,
                                      boundary_fct_dict=self.boundary_dict)
     self.subdomains.setup_measures()
     # BCs
     self.bcs = BoundaryConditions(self.functionspace, self.subdomains)
     self.dirichlet_bcs = {'clamped_0': {'bc_value': fenics.Constant((0.0, 0.0)),
                                       'boundary': BoundaryPos(),
                                       'subspace_id': 0},
                           'clamped_1': {'bc_value': fenics.Constant((0.0, 0.0)),
                                         'subdomain_boundary': 'tissue_tumor',
                                         'subspace_id': 0},
                           'clamped_pos': {'bc_value': fenics.Constant((0.0, 0.0)),
                                         'named_boundary': 'boundary_pos',
                                         'subspace_id': 0},
                           'clamped_neg': {'bc_value': fenics.Constant((0.0, 0.0)),
                                         'named_boundary': 'boundary_neg',
                                         'subspace_id': 0}
                           }
     self.von_neuman_bcs = {
                            'flux_boundary_pos': {'bc_value': fenics.Constant(1.0),
                                        'named_boundary': 'boundary_pos',
                                        'subspace_id': 1},
                             'flux_boundary_neg': {'bc_value': fenics.Constant(-5.0),
                                                   'named_boundary': 'boundary_neg',
                                                   'subspace_id': 1}
                            # 'no_flux_domain_boundary': {'bc_value': fenics.Constant(1.0),
                            #               'subdomain_boundary': 'tissue_tumor',
                            #               'subspace_id': 1},
                           }
Пример #6
0
class TestBoundaryConditions(TestCase):

    def setUp(self):
        # Domain
        nx = ny = nz = 10
        self.nx, self.ny = nx, ny
        self.mesh = fenics.RectangleMesh(fenics.Point(-2, -2), fenics.Point(2, 2), nx, ny)
        # function spaces
        self.displacement_element = fenics.VectorElement("Lagrange", self.mesh.ufl_cell(), 1)
        self.concentration_element = fenics.FiniteElement("Lagrange", self.mesh.ufl_cell(), 1)
        self.element = fenics.MixedElement([self.displacement_element, self.concentration_element])
        subspace_names = {0: 'displacement', 1: 'concentration'}
        self.functionspace = FunctionSpace(self.mesh)
        self.functionspace.init_function_space(self.element, subspace_names)
        # define 'solution' with concentration=1 everywhere
        self.conc_expr =  fenics.Constant(1.0)
        self.conc = self.functionspace.project_over_space(self.conc_expr, subspace_name='concentration')
        # subdomains
        label_funspace = fenics.FunctionSpace(self.mesh, "DG", 1)
        label_expr = fenics.Expression('(x[0]>=0) ? (1.0) : (2.0)', degree=1)
        labels = fenics.project(label_expr, label_funspace)
        self.labels = labels
        self.tissue_id_name_map = {0: 'outside',
                                   1: 'tissue',
                                   2: 'tumor'}
        self.parameter = {'outside': 0.0,
                          'tissue': 1.0,
                          'tumor': 0.1}
        self.boundary_pos = BoundaryPos()
        self.boundary_neg = BoundaryNeg()
        boundary_dict = {'boundary_pos': self.boundary_pos,
                         'boundary_neg': self.boundary_neg}
        self.boundary_dict = boundary_dict
        self.subdomains = SubDomains(self.mesh)
        self.subdomains.setup_subdomains(label_function=self.labels)
        self.subdomains.setup_boundaries(tissue_map=self.tissue_id_name_map,
                                         boundary_fct_dict=self.boundary_dict)
        self.subdomains.setup_measures()
        # BCs
        self.bcs = BoundaryConditions(self.functionspace, self.subdomains)
        self.dirichlet_bcs = {'clamped_0': {'bc_value': fenics.Constant((0.0, 0.0)),
                                          'boundary': BoundaryPos(),
                                          'subspace_id': 0},
                              'clamped_1': {'bc_value': fenics.Constant((0.0, 0.0)),
                                            'subdomain_boundary': 'tissue_tumor',
                                            'subspace_id': 0},
                              'clamped_pos': {'bc_value': fenics.Constant((0.0, 0.0)),
                                            'named_boundary': 'boundary_pos',
                                            'subspace_id': 0},
                              'clamped_neg': {'bc_value': fenics.Constant((0.0, 0.0)),
                                            'named_boundary': 'boundary_neg',
                                            'subspace_id': 0}
                              }
        self.von_neuman_bcs = {
                               'flux_boundary_pos': {'bc_value': fenics.Constant(1.0),
                                           'named_boundary': 'boundary_pos',
                                           'subspace_id': 1},
                                'flux_boundary_neg': {'bc_value': fenics.Constant(-5.0),
                                                      'named_boundary': 'boundary_neg',
                                                      'subspace_id': 1}
                               # 'no_flux_domain_boundary': {'bc_value': fenics.Constant(1.0),
                               #               'subdomain_boundary': 'tissue_tumor',
                               #               'subspace_id': 1},
                              }

    def test_setup_dirichlet_boundary_conditions(self):
        self.bcs.setup_dirichlet_boundary_conditions(self.dirichlet_bcs)
        self.assertTrue(hasattr(self.bcs, 'dirichlet_bcs'))
        self.assertEqual(len(self.bcs.dirichlet_bcs),4)

    def test_setup_von_neumann_boundary_conditions(self):
        self.bcs.setup_von_neumann_boundary_conditions(self.von_neuman_bcs)
        self.assertTrue(hasattr(self.bcs, 'von_neumann_bcs'))
        self.assertEqual(len(self.bcs.von_neumann_bcs), 2)

    def test_implement_von_neumann_bcs(self):
        # seutp bcs
        self.bcs.setup_von_neumann_boundary_conditions(self.von_neuman_bcs)
        #v0, v1 = fenics.TestFunctions(self.functionspace.function_space)
        #param = self.subdomains.create_discontinuous_scalar_from_parameter_map(self.parameter, 'param')
        #bc_terms_0 = self.bcs.implement_von_neumann_bc(param * v0, subspace_id=0)
        #bc_terms_1 = self.bcs.implement_von_neumann_bc(param * v1, subspace_id=1)

        param = fenics.Constant(1.0)
        # compute surface integral based on implementation functions
        bc_boundary_auto_form = self.bcs.implement_von_neumann_bc(param * self.conc, subspace_id=1)
        bc_boundary_auto = fenics.assemble(bc_boundary_auto_form)
        # compute surface integral by defining form manually
        boundary_pos_id = self.subdomains.named_boundaries_id_dict['boundary_pos']
        boundary_neg_id = self.subdomains.named_boundaries_id_dict['boundary_neg']
        bc_boundary_pos_form = param * self.conc * fenics.Constant(1.0) * self.subdomains.dsn(boundary_pos_id)
        bc_boundary_neg_form = param * self.conc * fenics.Constant(-5.0) * self.subdomains.dsn(boundary_neg_id)
        bc_boundary = fenics.assemble(bc_boundary_pos_form) + fenics.assemble(bc_boundary_neg_form)
        self.assertAlmostEqual(bc_boundary_auto, bc_boundary)
Пример #7
0
 def test_get_functionspace(self):
     # single element
     subspace_name = 'displacement'
     functionspace_single = FunctionSpace(self.mesh)
     functionspace_single.init_function_space(self.displacement_element, subspace_name)
     V = functionspace_single.get_functionspace()
     self.assertEqual(functionspace_single.get_functionspace(subspace_id=1), V)
     # multiple elements
     subspace_names = {0: 'displacement', 1: 'concentration'}
     functionspace_double = FunctionSpace(self.mesh)
     functionspace_double.init_function_space(self.element, subspace_names)
     V = functionspace_double.get_functionspace()
     self.assertNotEqual(functionspace_double.get_functionspace(1), V)
class TestTimeSeriesMultiData(TestCase):

    def setUp(self):
        # Domain
        nx = ny = nz = 10
        mesh = fenics.RectangleMesh(fenics.Point(-2, -2), fenics.Point(2, 2), nx, ny)
        # function spaces
        displacement_element = fenics.VectorElement("Lagrange", mesh.ufl_cell(), 1)
        concentration_element = fenics.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
        element = fenics.MixedElement([displacement_element, concentration_element])
        subspace_names = {0: 'displacement', 1: 'concentration'}
        self.functionspace = FunctionSpace(mesh)
        self.functionspace.init_function_space(element, subspace_names)
        # build a 'solution' function
        u_0_conc_expr = fenics.Expression('sqrt(pow(x[0]-x0,2)+pow(x[1]-y0,2)) < 0.1 ? (1.0) : (0.0)', degree=1,
                                          x0=0.25,
                                          y0=0.5)
        u_0_disp_expr = fenics.Constant((1.0, 0.0))
        self.U = self.functionspace.project_over_space(function_expr={0: u_0_disp_expr, 1: u_0_conc_expr})

    def test_register_time_series(self):
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        self.assertTrue(hasattr(tsmd, tsmd.time_series_prefix+'solution'))
        tsmd.register_time_series(name='solution2', functionspace=self.functionspace)
        self.assertTrue(hasattr(tsmd, tsmd.time_series_prefix+'solution2'))

    def test_get_time_series(self):
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        self.assertEqual(tsmd.get_time_series('solution'), getattr(tsmd, tsmd.time_series_prefix+'solution'))

    def test_add_observation(self):
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=1)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=1)
        self.assertEqual(tsmd.get_time_series('solution').get_observation(1).get_time_step(), 1)

    def test_get_observation(self):
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=1)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=1)
        self.assertEqual(tsmd.get_time_series('solution').get_observation(1),
                         tsmd.get_observation('solution', 1))
        self.assertEqual(tsmd.get_observation('solution3', 1), None)

    def test_get_solution_function(self):
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=1)
        u = tsmd.get_solution_function('solution', subspace_id=None, recording_step=1)
        self.assertEqual(u.function_space(), self.U.function_space())
        self.assertNotEqual(u, self.U)

    def test_get_all_time_series(self):
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        tsmd.register_time_series(name='solution2', functionspace=self.functionspace)
        ts_dict = tsmd.get_all_time_series()
        self.assertEqual(len(ts_dict), 2)
        self.assertTrue('solution' in ts_dict.keys())
        self.assertTrue('solution2' in ts_dict.keys())

    def test_save_to_hdf5(self):
        path_to_file = os.path.join(config.output_dir_testing, 'timeseries_to_hdf5.h5')
        fu.ensure_dir_exists(path_to_file)
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=1)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=2)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=3)
        tsmd.register_time_series(name='solution2', functionspace=self.functionspace)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=1)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=2)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=3)
        tsmd.save_to_hdf5(path_to_file, replace=True)
        # path_to_file2 = os.path.join(config.output_dir_testing, 'timeseries_to_hdf5_manual.h5')
        # hdf = fenics.HDF5File(self.functionspace._mesh.mpi_comm(), path_to_file2, "w")
        # function = tsmd.get_solution_function('solution', recording_step=1)
        # hdf.write(function, 'solution', 1)
        # hdf.write(function, 'solution', 2)
        # hdf.close()

    def test_load_from_hdf5(self):
        path_to_file = os.path.join(config.output_dir_testing, 'timeseries_to_hdf5_for_reading.h5')
        fu.ensure_dir_exists(path_to_file)
        # create file
        tsmd = TimeSeriesMultiData()
        tsmd.register_time_series(name='solution', functionspace=self.functionspace)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=1)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=2)
        tsmd.add_observation('solution', field=self.U, time=1, time_step=1, recording_step=3)
        tsmd.register_time_series(name='solution2', functionspace=self.functionspace)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=1)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=2)
        tsmd.add_observation('solution2', field=self.U, time=1, time_step=1, recording_step=3)
        tsmd.save_to_hdf5(path_to_file, replace=True)
        # read file
        tsmd2 = TimeSeriesMultiData()
        tsmd2.register_time_series(name='solution', functionspace=self.functionspace)
        tsmd2.register_time_series(name='solution2', functionspace=self.functionspace)
        tsmd2.load_from_hdf5(path_to_file)
        self.assertEqual(len(tsmd2.get_all_time_series()),2)
        self.assertEqual(len(tsmd2.get_time_series('solution').get_all_recording_steps()),3)
        self.assertEqual(len(tsmd2.get_time_series('solution2').get_all_recording_steps()), 3)
        u_reloaded = tsmd2.get_solution_function(name='solution')
        # print(u_reloaded.vector().array())
        # print(self.U.vector().array())
        array_1 = u_reloaded.vector().get_local()
        array_2 = self.U.vector().get_local()
        self.assertTrue(np.allclose(array_1, array_2))
Пример #9
0
class TestSimulationParameters(TestCase):
    def setUp(self):
        # Domain
        nx = ny = nz = 10
        self.mesh = fenics.RectangleMesh(fenics.Point(-2, -2),
                                         fenics.Point(2, 2), nx, ny)
        # function spaces
        self.displacement_element = fenics.VectorElement(
            "Lagrange", self.mesh.ufl_cell(), 1)
        self.concentration_element = fenics.FiniteElement(
            "Lagrange", self.mesh.ufl_cell(), 1)
        self.element = fenics.MixedElement(
            [self.displacement_element, self.concentration_element])
        subspace_names = {0: 'displacement', 1: 'concentration'}
        self.functionspace = FunctionSpace(self.mesh)
        self.functionspace.init_function_space(self.element, subspace_names)
        # subdomains
        label_funspace = fenics.FunctionSpace(self.mesh, "DG", 1)
        label_expr = fenics.Expression('(x[0]>=0) ? (1.0) : (2.0)', degree=1)
        labels = fenics.project(label_expr, label_funspace)
        self.labels = labels
        self.tissue_id_name_map = {0: 'outside', 1: 'tissue', 2: 'tumor'}
        self.parameter = {'outside': 0.0, 'tissue': 1.0, 'tumor': 0.1}
        self.boundary = Boundary()
        boundary_dict = {
            'boundary_1': self.boundary,
            'boundary_2': self.boundary
        }
        self.boundary_dict = boundary_dict
        self.subdomains = SubDomains(self.mesh)
        self.subdomains.setup_subdomains(label_function=self.labels)
        self.subdomains.setup_boundaries(tissue_map=self.tissue_id_name_map,
                                         boundary_fct_dict=self.boundary_dict)
        self.subdomains.setup_measures()
        # parameter instance
        self.params = Parameters(self.functionspace, self.subdomains)

    def test_set_initial_value_expressions(self):
        u_0_conc_expr = fenics.Expression(
            'sqrt(pow(x[0]-x0,2)+pow(x[1]-y0,2)) < 0.1 ? (1.0) : (0.0)',
            degree=1,
            x0=0.25,
            y0=0.5)
        u_0_disp_expr = fenics.Constant((0.0, 0.0))
        ivs = {1: u_0_conc_expr, 0: u_0_disp_expr}
        self.params.set_initial_value_expressions(ivs)
        self.assertEqual(self.params.get_iv(1), u_0_conc_expr)
        self.assertEqual(self.params.get_iv(0), u_0_disp_expr)

    def test_define_required_params(self):
        req_params = ['a', 'b', 'c']
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=False)
        self.params.define_required_params(req_params)
        self.assertEqual(sorted(req_params),
                         sorted(self.params.params_required))
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=True)
        self.params.define_required_params(req_params)
        self.assertEqual(sorted(req_params + ['sim_time', 'sim_time_step']),
                         sorted(self.params.params_required))

    def test_define_optional_params(self):
        opt_params = ['d', 'e', 'f']
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=False)
        self.params.define_optional_params(opt_params)
        self.assertEqual(sorted(opt_params),
                         sorted(self.params.params_optional))

    def test_check_param_arguments(self):
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=False)
        req_params = ['a', 'b', 'c']
        self.params.define_required_params(req_params)
        kw_args = {'a': 1, 'b': 2, 'c': 3}
        test = self.params._check_param_arguments(kw_args)
        self.assertTrue(test)
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=True)
        req_params = ['a', 'b', 'c']
        self.params.define_required_params(req_params)
        kwargs = {'a': 1, 'b': 2, 'c': 3}
        test = self.params._check_param_arguments(kw_args)
        self.assertTrue(~test)

    def test_set_parameters(self):
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=False)
        req_params = ['a', 'b', 'c']
        self.params.define_required_params(req_params)
        opt_params = ['d']
        self.params.define_optional_params(opt_params)
        input_params = {'a': 1, 'b': self.parameter, 'c': 1, 'd': 1, 'e': 1}
        self.params.init_parameters(input_params)
        self.assertTrue(hasattr(self.params, 'a'))
        self.assertTrue(hasattr(self.params, 'b'))
        self.assertTrue(hasattr(self.params, 'b_dict'))
        self.assertEqual(type(self.params.b), DiscontinuousScalar)
        self.assertTrue(hasattr(self.params, 'c'))
        self.assertTrue(hasattr(self.params, 'd'))
        self.assertFalse(hasattr(self.params, 'e'))

    def test_create_initial_value_function(self):
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=False)
        u_0_conc_expr = fenics.Expression(
            'sqrt(pow(x[0]-x0,2)+pow(x[1]-y0,2)) < 0.1 ? (1.0) : (0.0)',
            degree=1,
            x0=0.25,
            y0=0.5)
        u_0_disp_expr = fenics.Constant((0.0, 0.0))
        ivs = {1: u_0_conc_expr, 0: u_0_disp_expr}
        self.params.set_initial_value_expressions(ivs)
        u = self.params.create_initial_value_function()
Пример #10
0
class FenicsSimulation(ABC):
    """
    This class serves as 'master class' for the definition of time-dependent FEniCS problems that may differ in their
    underlying governing forms and input parameters.
    The implementation is optimised for the class of problems of interest in GlimS.
    This implies:

    - support for dolfin-adjoint based parameter assimilation
    - automatic construction of subdomains and boundary specifications from segmentation label maps to facilitate
      image-based modeling

    An implementation of this class requires at least the following abstract methods to be implemented::

        class NewSimulationClass(FenicsSimulation):

            def _setup_functionspace(self):
                ...

            def setup_model_parameters(self, **kwargs):
                ...

            def _setup_problem(self, u_previous):
                ...

            def run_for_adjoint(self, u_previous):
                ...

    It is instantiated and configured by::

        sim = NewSimulationClass()
        sim.setup_global_parameters(...)
        sim.setup_model_parameters(...)
        sim.run(...)

    and run by::

        sim.run(...)

    or::

        sim.run_for_adjoint(...)


     .. warning::
        Parameters for heterogeneous subdomains are internally defined as `DiscontinuousScalar` Expressions,
        see :py:meth:`simulation.helpers.helper_classes.DiscontinuousScalar`.
        This works well for forward simulation, however, FEniCS dolfin-adjoint does not seem to handle them well.

     .. warning::
        Von Neumann 'named_boundary' and 'subdomain_boundary' boundary conditions cannot be mixed in the current
        implementation, see :py:meth:`simulation.helpers.helper_classes.BoundaryConditions` for details.
        Also, in the current implementation, von Neumann BCs can only be applied to mesh boundaries not to interfaces
        between subdomains.
    """
    def __init__(self, mesh, time_dependent=True):
        """
        Init routine.
        :param mesh: The mesh.
        :param time_dependent: Boolean switch indicating whether this simulation is time-dependent.
        """
        self.logger = logging.getLogger(__name__)
        self.mesh = mesh
        self.geometric_dimension = self.mesh.geometry().dim()
        self.time_dependent = time_dependent
        self.projection_parameters = {
            'solver_type': 'cg',
            'preconditioner_type': 'amg'
        }
        self.functionspace = FunctionSpace(
            self.mesh, projection_parameters=self.projection_parameters)
        self._define_model_params()
        if fenics.is_version("<2018.1.x"):
            pass
        else:
            if config.USE_ADJOINT:
                self.tape = fenics.get_working_tape()

    @abstractmethod
    def _define_model_params(self):
        """
        Defines the models parameters expected by :py:meth:`setup_model_parameters`.
        In addition to these, time dependent simulations require parameters:

            - `sim_time`
            - `sim_time_step`
        """
        self.required_params = ['a', 'b']
        self.optional_params = ['c', 'd']

    @abstractmethod
    def _setup_functionspace(self):
        """
        This is a test_cases implementation that needs to be overwritten.
        Any implementation of this function must initialize self.functionspace.
        """
        # Element definitions
        displacement_element = fenics.VectorElement("Lagrange",
                                                    self.mesh.ufl_cell(), 1)
        concentration_element = fenics.FiniteElement("Lagrange",
                                                     self.mesh.ufl_cell(), 1)
        element = fenics.MixedElement(
            [displacement_element, concentration_element])
        subspace_names = {0: 'displacement', 1: 'concentration'}
        # Initialisation of self.functionspace
        self.functionspace.init_function_space(element, subspace_names)

    @abstractmethod
    def _setup_problem(self, u_previous):
        """
        This function takes the initial value function u_previous as input and, after its execution,
        is expected to provide a fenics.solver as class instance attribute `self.solver`.
        The function needs to be overloaded with the model-specific problem definition, including:

        - governing form
        - problem
        - solver

        The function is executed by :py:meth:`self.run()`.

        :param u_previous: initial value function, or previous solution
        """

    @abstractmethod
    def run_for_adjoint(self, parameters):
        """
        Run the time-dependent simulation with minimum number of updated parameters for adjoint optimisation.
        :param parameters: list of parameters
        """

    def setup_global_parameters(self,
                                label_function=None,
                                subdomains=None,
                                domain_names=None,
                                boundaries=None,
                                dirichlet_bcs=None,
                                von_neumann_bcs=None):
        """
        This function bundles the setup of 'global' simulation properties that remain unchanged
        when other simulation parameters are modified.

        We separate initialisation of those parameters here from :py:meth:`self.setup_model_parameters` to ensure that all
        simulations can be rerun with different parameter settings but utilising the same (identical) functionspace and
        mesh.

        After execution, the class instance has attributes:

        - `functionspace`, an instance of :py:meth:`simulation.helpers.helper_classes.FunctionSpace`
            with subspaces if appropriate.
        - `subdomains`, an instance of :py:meth:`simulation.helpers.helper_classes.SubDomains`
        - `bcs`, an instance of :py:meth:`simulation.helpers.helper_classes.BoundaryConditions`

        :param label_function: a `fenics.Function()` assigning integer value to each point of domain, e.g. segmentation image.
        :param subdomains: a `fenics.MeshFunction()` assigning value to each cell of domain.
        :param domain_names: a dictionary mapping each value in `label_function` or `subdomains` to a string.
        :param boundaries: da dictionary of `{name : function}` containing boundaries defined by functions
        :param dirichlet_bcs: a dictionary of the form `{bc_name : bc }`.
        :param von_neumann_bcs: a dictionary of the form `{bc_name : bc }`.
        """
        self.logger.info("-- Setting up global parameters")
        self.logger.info("   - assigning _mesh")
        self.geometric_dimension = self.mesh.geometry().dim()

        # Subdomains
        self.subdomains = SubDomains(self.mesh)
        self.subdomains.setup_subdomains(label_function=label_function,
                                         subdomains=subdomains,
                                         replace=False)
        self.subdomains.setup_boundaries(tissue_map=domain_names,
                                         boundary_fct_dict=boundaries)
        self.subdomains.setup_measures()
        # Function space
        self._setup_functionspace()
        # Boundary Conditions
        self.bcs = BoundaryConditions(self.functionspace, self.subdomains)
        self.bcs.setup_dirichlet_boundary_conditions(dirichlet_bcs)
        self.bcs.setup_von_neumann_boundary_conditions(von_neumann_bcs)

    def setup_model_parameters(self, iv_expression, **kwargs):
        """
        Initialises model-specific parameters.

        :param iv_expression:
        :param kwargs: keyword-value pairs corresponding to the required parameters defined in `self.required_params`.
            In time-dependent simulations, additionally the following parameters must be defined:

            - `sim_time`: total simulation time
            - `sim_time_step`: simulation time step
        """
        self._define_model_params()
        self.params = Parameters(self.functionspace,
                                 self.subdomains,
                                 time_dependent=self.time_dependent)
        self.params.set_initial_value_expressions(iv_expression)
        self.params.define_required_params(self.required_params)
        self.params.define_optional_params(self.optional_params)
        self.params.init_parameters(kwargs)

    def _update_expressions(self, time):
        """
        Updates parameters and boundary conditions with current time.
        :param time: current time.
        """
        self.params.time_update_parameters(time)
        self.bcs.time_update_bcs(time, kind='dirichlet')
        self.bcs.time_update_bcs(time, kind='von-neumann')

    def _update_mesh_displacements(self, displacement):
        """
        Applies displacement function to mesh.
        .. warning:: This changes the current mesh! Multiple updates result in additive mesh deformations!
        """
        fenics.ALE.move(self.mesh, displacement)
        self.mesh.bounding_box_tree().build(self.mesh)

    def run(self,
            keep_nth=1,
            save_method='xdmf',
            clear_all=False,
            plot=True,
            output_dir=config.output_dir_simulation_tmp):
        """
        Run the time-dependent simulation.
        :param keep_nth: keep every nth simulation step
        :param save_method : None, 'vtk', 'xdmf'
        """
        if self.geometric_dimension == 3:
            plot = False

        self.logger.info("-- Computing solutions: ")
        # Results instance
        self.results = Results(self.functionspace,
                               self.subdomains,
                               output_dir=output_dir)
        self.results.save_solution_start(method=save_method,
                                         clear_all=clear_all)
        # Plotting
        self.plotting = Plotting(self.results,
                                 output_dir=os.path.join(output_dir, 'plots'))
        # Initial Conditions
        u_previous = self.params.create_initial_value_function()
        self._setup_problem(u_previous)

        if not self.time_dependent:
            self.logger.info("    - solving stationary problem")
            self.solver.solve()
            self.results.add_to_results(0, 0, 0, self.solution)
            self.results.save_solution(0, 0, method=save_method)
            if plot:
                self.plotting.plot_all(0)
            u_previous.vector()[:] = self.solution.vector()
        else:
            # == t=0
            current_sim_time = 0.0
            self._update_expressions(current_sim_time)
            time_step = 0
            recording_step = 0
            u_0 = u_previous
            self.results.add_to_results(0, 0, recording_step, u_0)
            self.results.save_solution(recording_step,
                                       current_sim_time,
                                       function=u_0,
                                       method=save_method)
            if plot:
                self.plotting.plot_all(recording_step)
            continue_simulation = True
            # == t>0
            while (current_sim_time <=
                   self.params.sim_time - 1e-5) and continue_simulation:
                if hasattr(self, 'tape'):
                    with self.tape.name_scope("Timestep"):
                        current_sim_time += float(self.params.sim_time_step)
                        time_step = time_step + 1
                        self._update_expressions(current_sim_time)
                        self.logger.info(
                            "    - solving for time = %.2f / %.2f" %
                            (current_sim_time, self.params.sim_time))
                        try:
                            self.solver.solve()
                        except:
                            self.logger.warning(
                                "    - Solver did not converge -- will shutdown simulation"
                            )
                            continue_simulation = False
                        if (time_step % keep_nth == 0) and continue_simulation:
                            recording_step = recording_step + 1
                            self.results.add_to_results(
                                current_sim_time, time_step, recording_step,
                                self.solution)
                            self.results.save_solution(recording_step,
                                                       current_sim_time,
                                                       method=save_method)
                            if plot:
                                self.plotting.plot_all(recording_step)
                        u_previous.assign(self.solution)
                else:
                    current_sim_time += float(self.params.sim_time_step)
                    time_step = time_step + 1
                    self._update_expressions(current_sim_time)
                    self.logger.info("    - solving for time = %.2f / %.2f" %
                                     (current_sim_time, self.params.sim_time))
                    try:
                        self.solver.solve()
                    except:
                        self.logger.warning(
                            "    - Solver did not converge -- will shutdown simulation"
                        )
                        continue_simulation = False
                    if (time_step % keep_nth == 0) and continue_simulation:
                        recording_step = recording_step + 1
                        self.results.add_to_results(current_sim_time,
                                                    time_step, recording_step,
                                                    self.solution)
                        self.results.save_solution(recording_step,
                                                   current_sim_time,
                                                   method=save_method)
                        if plot:
                            self.plotting.plot_all(recording_step)
                    u_previous.assign(self.solution)

        self.results.save_solution_end(method=save_method)
        # save entire time series as hdf5
        self.results.save_solution_hdf5()
        return self.solution

    def reload_from_hdf5(self,
                         path_to_hdf5,
                         output_dir=config.output_dir_simulation_tmp):
        self.logger.info("-- Reloading from hdf5: ")
        # Results instance
        self.results = Results(self.functionspace,
                               self.subdomains,
                               output_dir=output_dir)
        self.results.data.load_from_hdf5(path_to_hdf5)
        # Plotting
        self.plotting = Plotting(self.results,
                                 output_dir=os.path.join(output_dir, 'plots'))