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')
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)
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)
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)
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))
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()
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'))