Giter Club home page Giter Club logo

fenics-adapter's Introduction

FEniCS-preCICE adapter

GNU LGPL license DOI Build and Test Run preCICE Tutorials Upload Python Package

preCICE-adapter for the open source computing platform FEniCS.

Installing the package

Using pip3 to install from PyPI

It is recommended to install fenicsprecice from PyPI via

pip3 install --user fenicsprecice

This should work out of the box, if all dependencies are installed correctly. If you face problems during installation or you want to run the tests, see below for a list of dependencies and alternative installation procedures

Clone this repository and use pip3

Required dependencies

Make sure to install the following dependencies:

Build and install the adapter

After cloning this repository and switching to the root directory (fenics-adapter), run pip3 install --user . from your shell.

Test the adapter

As a first test, try to import the adapter via python3 -c "import fenicsprecice".

You can run the other tests via python3 setup.py test.

Single tests can be also be run. For example the test test_vector_write in the file test_write_read.py can be run as follows:

python3 -m unittest tests.test_write_read.TestWriteandReadData.test_vector_write

Troubleshooting

FEniCS is suddenly broken: There are two known issues with preCICE, fenicsprecice and FEniCS:

  • If you see ImportError: cannot import name 'sub_forms_by_domain' run pip3 uninstall -y fenics-ufl. For details, refer to issue #103.
  • If you see ImportError: cannot import name 'cellname2facetname' from 'ufl.cell', refer to issue #154.
  • If you see ModuleNotFoundError: No module named 'dolfin' and have installed PETSc from source, refer to this forum post. Short version: Try to use the PETSc that comes with your system, if possible. Note that you can also compile preCICE without PETSc, if necessary.

If this does not help, you can contact us on gitter or open an issue.

Use the adapter

Please refer to our website.

Packaging

To create and install the fenicsprecice python package the following instructions were used: How To Package Your Python Code from python-packaging.readthedocs.io.

Citing

Development history

The initial version of this adapter was developed by Benjamin Rodenberg during his research stay at Lund University in the group for Numerical Analysis in close collaboration with Peter Meisrimel.

Richard Hertrich contributed the possibility to perform FSI simulations using the adapter in his Bachelor thesis.

Ishaan Desai improved the user interface and extended the adapter to allow for parallel FEniCS computations and 3D cases in certain scenarios.

fenics-adapter's People

Contributors

ajaust avatar benjaminrodenberg avatar boris-martin avatar ishaandesai avatar makish avatar max-hassani avatar rafalkulaga avatar richahert avatar shkodm avatar uekerman avatar valentin-seitz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fenics-adapter's Issues

Implement proper checkpointing

preCICE provides a mechanism for checkpointing (see Adapter Example). In the current implementation of the adapter and the associated examples (fenics-fenics and buoyantPimpleFoam-fenics) this mechanism is not implemented properly. Proper checkpointing would be helpful to resolve #16.

Todo

Build fails due to precice not found

Hi,

I wonder whether I do something wrong or if I miss something. When I want to build the adapter it cannot find precice even though I have the bindings installed and working. I try to build the adapter from the parallelFEniCS branch.

When I change line 16 in setup.py to install_requires=['fenics', 'scipy', 'numpy'], it seems to work. However, the parallel test case fails, but I will open another issue for that.

This is the error message:

Processing /home/jaustar/projects/fenics-precice-parallel/fenics-adapter
Collecting precice (from fenics-adapter==0.1)
Exception:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/pip/basecommand.py", line 215, in main
    status = self.run(options, args)
  File "/usr/lib/python3/dist-packages/pip/commands/install.py", line 353, in run
    wb.build(autobuilding=True)
  File "/usr/lib/python3/dist-packages/pip/wheel.py", line 749, in build
    self.requirement_set.prepare_files(self.finder)
  File "/usr/lib/python3/dist-packages/pip/req/req_set.py", line 380, in prepare_files
    ignore_dependencies=self.ignore_dependencies))
  File "/usr/lib/python3/dist-packages/pip/req/req_set.py", line 554, in _prepare_file
    require_hashes
  File "/usr/lib/python3/dist-packages/pip/req/req_install.py", line 278, in populate_link
    self.link = finder.find_requirement(self, upgrade)
  File "/usr/lib/python3/dist-packages/pip/index.py", line 465, in find_requirement
    all_candidates = self.find_all_candidates(req.name)
  File "/usr/lib/python3/dist-packages/pip/index.py", line 423, in find_all_candidates
    for page in self._get_pages(url_locations, project_name):
  File "/usr/lib/python3/dist-packages/pip/index.py", line 568, in _get_pages
    page = self._get_page(location)
  File "/usr/lib/python3/dist-packages/pip/index.py", line 683, in _get_page
    return HTMLPage.get_page(link, session=self.session)
  File "/usr/lib/python3/dist-packages/pip/index.py", line 795, in get_page
    resp.raise_for_status()
  File "/usr/share/python-wheels/requests-2.18.4-py2.py3-none-any.whl/requests/models.py", line 935, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://pypi.org/simple/precice/

Issuing ls ~/.local/lib/python3.6/site-packages/precice* gives

/home/jaustar/.local/lib/python3.6/site-packages/precice.cpython-36m-x86_64-linux-gnu.so

/home/jaustar/.local/lib/python3.6/site-packages/precice-1.5.2-py3.6.egg-info:
PKG-INFO  SOURCES.txt  dependency_links.txt  requires.txt  top_level.txt

Message "Found a double-boundary point at ..." is distracting

The message "Found a double-boundary point at ..." is distracting from my point of view, since it shows up very often in the course of a run. Note that it is still important to filter these "double-boundary points".

Some background story and current state

In his thesis, section 5.1.2 @richahert reports stability problems that occur if we set a Neumann BC and a Dirichlet BC at the point, where the "real" and the coupling boundary meeting (see Fig 5.2).

The solution for this problem is to filter for these points and - even if they are declared as part of the coupling boundary - don't provide point sources for these points. Our adapter uses the function filter_point_sources for this purpose. The user has the possibility to provide a fixed_boundary to initialize to define, where the filtering is applied.

This mechanism is used in the example from Richard's thesis to provide the clamped_boundary_domain to initialize

What to do next?

Like explained above the mechanism for filtering of "double-boundary points" is necessary. What I would like to discuss is the question when to inform the user about the fact that this is happening? Currently the message is always being output when filtering happens (see here). This is from my point of view too often.

We could also decide to not post any message. But then the user might be wondering, why no coupling boundary conditions is created for some of the points on the coupling boundary. Any opinions?

Make ExactInterpolationExpression safe

Our current implementation of ExactInterpolationExpression is limited to cases, where the coupling boundary is a straight line that is parallel to the y-axis. This is implicitly assumed by the way how we create our interpolant:

interp1d(self._coords_y, self._vals, bounds_error=False, fill_value="extrapolate", kind="cubic"))

As far as I know, there are no checks that prevent the user from using ExactInterpolationExpression on coupling boundaries that do not fullfill the requirements from above.

In my opinion it is sufficient to add security checks for now. With GeneralInterpolationExpression a good-enough alternative is available and ExactInterpolationExpression is only used for academic test cases, anyway.

How can we create Expression / function representation for boundary conditions properly?

Our FEniCS adapter uses Expressions to define Dirichlet and Neumann boundary conditions. Since preCICE only provides nodal data, we implemented the class CustomExpression to be able to create a functional representation (an Expression) from nodal data.

In our implementation RBFs are used for interpolation (see eval function). However, this approach is not ideal when considering the whole coupled simulation from a broader perspective:

preCICE already uses interpolation to map data from the coupling partner's mesh to the FEniCS mesh (if the meshes are not matching). However, the interpolant is hidden in preCICE and, therefore, we have to perform an additional interpolation step on the FEniCS side to create an interpolant that may be used by FEniCS for defining boundary conditions.

Some first ideas:

  • Does FEniCS allow defining boundary conditions / expressions from nodal data instead of functions?
  • Can we directly access the interpolant inside preCICE to avoid the additional interpolation step?

Use a config file to configure the adapter

For better agreement with the design of the OpenFOAM adapter we should use a config file to configure the FEniCS adapter instead of hardcoding parameters (or handing them over using the command line).

An example config file of the OpenFOAM adapter can be found here. We should follow a similar strategy for the FEniCS adapter.

sidenote: Each participant has its own config file for its respective adapter. This means, for example, for the FEniCS-FEniCS tutorial that we need two config files - one for the Dirichlet, one for the Neumann solver. Compare to the OpenFOAM-OpenFOAM test case.

Run Travis for fenics-adapter tests

Using the command python setup.py test we can run tests for the adapter. Doing this automatically in Travis (like we already do it in our main precice project) would be great.

An additional nice-to-have: In my testing environment I eliminate the dependency on preCICE by using the mock module. This also allows running the tests without preCICE being installed on the system. Therefore, we can test on different configurations, such as:

  • Linux with preCICE (still needs to be added in systemtests)
  • Linux without preCICE (closed by #27)
  • MacOS (closed by #27)
  • Windows (which I sometimes use for test-driven development of this adapter, closed by #27)

Small error at boundaries in fenics-fenics tutorial

Plotting the L2 error on the domain of the tutorial HT/partitioned-heat/fenics-fenics shows a small error at the coupling interface, if we are close to the boundary.

smallerroratboundary

Inside the domain everything looks fine and the error also does not grow over time. If the resolution is reduced, the error gets smaller. These are generally good things. The error does not get smaller, if we reduce the tolerance of the coupling.

What's wrong with this error? We should be able to recover the exact solution in this example case:

  • The analytical solution is first order in time and second order in space. Our discretization (implicit Euler/linear FEM) should be able to recover this solution. For details, see the [FEniCS tutorials book, p.40)[https://fenicsproject.org/tutorial/].
  • The meshes are matching and therefore no mapping is necessary (which might introduce an error; see #11 )

Where does this error come from? Possible candidates:

  • Wrong computation of the flux at the interface, since this is still a point where I am not 100% sure that we are doing everything correctly
  • Wrong treatment of the point, where coupling interface and outer boundary meet.

The results from above can be recovered using precice/tutorials@f9fe120 and a006b30.

Fix issue with non-matching subcycling

If we use subcycling, we can have one of the following setups:

  • matching: timestep size of FEniCS perfectly fits the windows size of preCICE. Example: fenics_dt = 0.1, precice_dt = 1. Resulting in timesteps at [0,0.2,0.4,0.6,0.8,1]
  • non-matching: timestep size of FEniCS has to be reduced for the last timestep to fit into the window. Example: fenics_dt = 0.3, precice_dt = 1. Resulting in timesteps at [0,0.3,0.6,0.9,1]

For both setups the error increases compared to the setup without subcycling (this makes sense, since timestepping is strongly affected, if no proper waveform relaxation is used). However, for the non-matching case, the error is quite high compared to the matching case.

The size of the error seems not to be related to the timestep size for the non-matching case (if fenics_dt for the non-matching case is smaller than fenics_dt for the matching case, we still have higher error for the non-matching case). This makes me assume that there is another issue with non-matching subcycling besides the fact that we should use proper waveform relaxation to obtain the exact solution. What is this additional issue?

Bug for non-matching meshes

Running the HT/partitioned-heat/fenics-fenics example gives an error, if the mesh of the right partition (solverName = "HeatNeumann") is finer than the mesh of the left partition (solverName = "HeatDirichlet"). If the mesh of the left partition is finer than the mesh of the right partition, we do not observe this bug.

Run:

$ ~/tutorials/HT/partitioned-heat/fenics-fenics$ python3 heat.py -d

and

$ ~/tutorials/HT/partitioned-heat/fenics-fenics$ python3 heat.py -n

Output for process running python3 heat.py -d:

(0) 17:57:16 []:0 in : This is preCICE version 1.3.0. Starting 2018-11-28 17:57:16
(0) 17:57:16 []:78 in xmlEndTagCallback: This is preCICE version 1.3.0. Starting 2018-11-28 17:57:16
(0) 17:57:16 [impl::SolverInterfaceImpl]:87 in configure: Configuring preCICE with configuration: "precice-config.xml"
(0) 17:57:16 [impl::SolverInterfaceImpl]:129 in configure: Run in coupling mode
(0) 17:57:16 [impl::SolverInterfaceImpl]:194 in initialize: Setting up master communication to coupling partner/s 
(0) 17:57:18 [impl::SolverInterfaceImpl]:210 in initialize: Coupling partner/s are connected 
(0) 17:57:18 [partition::ReceivedPartition]:35 in communicate: Receive global mesh NeumannNodes
(0) 17:57:18 [partition::ProvidedPartition]:80 in compute: Compute partition for mesh DirichletNodes
(0) 17:57:18 [impl::SolverInterfaceImpl]:219 in initialize: Setting up slaves communication to coupling partner/s 
(0) 17:57:18 [impl::SolverInterfaceImpl]:234 in initialize: Slaves are connected
(0) 17:57:18 [impl::SolverInterfaceImpl]:260 in initialize: it 1 of 100 | dt# 1 | t 0 of 1 | dt 0.1 | max dt 0.1 | ongoing yes | dt complete no | write-iteration-checkpoint | 
(0) 17:57:18 [impl::SolverInterfaceImpl]:1572 in mapWrittenData: Compute write mapping from mesh "DirichletNodes" to mesh "NeumannNodes".
(0) 17:57:18 [impl::SolverInterfaceImpl]:1631 in mapReadData: Compute read mapping from mesh "NeumannNodes" to mesh "DirichletNodes".
ASSERTION FAILED
  Location:          void precice::impl::SolverInterfaceImpl::readBlockScalarData(int, int, int*, double*)
  File:              src/precice/impl/SolverInterfaceImpl.cpp:1350
  Rank:              0
  Failed expression: valueIndices[i] < valuesInternal.size()
  Argument 0: 6
  Argument 1: 6
Stack Trace:
  (18)  /home/benjamin/precice/build/last/libprecice.so : precice::impl::SolverInterfaceImpl::readBlockScalarData(int, int, int*, double*)+0x1c8c
  (17)  /home/benjamin/precice/build/last/libprecice.so : precice::SolverInterface::readBlockScalarData(int, int, int*, double*)+0x41
  (16)  /usr/local/lib/python3.6/dist-packages/PySolverInterface.cpython-36m-x86_64-linux-gnu.so : ()+0xfbc4
  (15)  python3 : ) [0x511b75]()+python3(
  (14)  python3 : _PyEval_EvalFrameDefault()+0x467
  (13)  python3 : ) [0x4f3338]()+python3(
  (12)  python3 : ) [0x510fb0]()+python3(
  (11)  python3 : ) [0x5119bd]()+python3(
  (10)  python3 : _PyEval_EvalFrameDefault()+0x1260
  ( 9)  python3 : ) [0x4f3338]()+python3(
  ( 8)  python3 : PyEval_EvalCode()+0x23
  ( 7)  python3 : ) [0x640862]()+python3(
  ( 6)  python3 : PyRun_FileExFlags()+0x9a
  ( 5)  python3 : PyRun_SimpleFileExFlags()+0x190
  ( 4)  python3 : Py_Main()+0x587
  ( 3)  python3 : main()+0xe0
  ( 2)  /lib/x86_64-linux-gnu/libc.so.6 : __libc_start_main()+0xe7
  ( 1)  python3 : _start()+0x2a

Refactor adapter for clearer control flow

# continue FEniCS computation from checkpoint
u_n.assign(self._u_cp) # set u_n to value of checkpoint
t = self._t_cp
n = self._n_cp
self._interface.fulfilled_action(precice.action_read_iteration_checkpoint())

--> Create Function restore_solver_state_from_checkpoint


u_n.assign(u_np1)
t = new_t = t + dt # todo the variables new_t, new_n could be saved, by just using t and n below, however I think it improved readability.
n = new_n = n + 1

--> Create Function advance_solver_state


# continue FEniCS computation with u_np1
# update checkpoint
self._u_cp.assign(u_np1)
self._t_cp = new_t
self._n_cp = new_n
self._interface.fulfilled_action(precice.action_write_iteration_checkpoint())
precice_step_complete = True

--> Create Function save_solver_state_to_checkpoint


if self._interface.is_action_required(precice.action_write_iteration_checkpoint()):

--> add assertion after this line assert(not solver_state_has_been_restored_before), since this control flow is invalid.


self._interface.write_block_scalar_data(self._write_data_id, self._n_vertices, self._vertex_ids, self._write_data)

--> add if is_read_data_available():


self._interface.read_block_scalar_data(self._read_data_id, self._n_vertices, self._vertex_ids, self._read_data)

--> add if is_write_data_required():


if self._interface.is_read_data_available():
self._interface.read_block_scalar_data(self._read_data_id, self._n_vertices, self._vertex_ids, self._read_data)
if self._interface.is_action_required(precice.action_write_iteration_checkpoint()):
self._u_cp = u_n.copy(deepcopy=True)
self._t_cp = t
self._n_cp = n
self._interface.fulfilled_action(precice.action_write_iteration_checkpoint())

is code duplication of

self._interface.read_block_scalar_data(self._read_data_id, self._n_vertices, self._vertex_ids, self._read_data)

if self._interface.is_action_required(precice.action_write_iteration_checkpoint()):
# continue FEniCS computation with u_np1
# update checkpoint
self._u_cp.assign(u_np1)
self._t_cp = new_t
self._n_cp = new_n
self._interface.fulfilled_action(precice.action_write_iteration_checkpoint())
precice_step_complete = True

Expose mid-level functions to user

The fenics-adapter offers a very high level interface. This allows to have minimal code changes in existing examples (see heat.py), but also limits the user. The following functions can wrap preCICE functionality, while allowing the user to access preCICE at a low level:

def read_block_scalar_data(self._read_data_id, mesh, V):
    precice.read_block_scalar_data(self._read_data_id, self._n_vertices, self._vertex_ids, self._read_data)
    # read_data -> CustomExpression
    return CustomExpression

def write_block_scalar_data(self._write_data_id, u):
    # u -> write_data
    precice.write_block_scalar_data(self._write_data_id, self._n_vertices, self._vertex_ids, self._write_data)

def set_mesh(mesh, SubDomain: coupling_boundary):
    precice.setMeshEdge(...)
    precice.setMeshVertices(...)

#preprocessing

mesh = UnitSquareMesh(10, 10)
m_f = MeshFunction(size_t, mesh, 1)
subdomain.mark(m_f, marker)

def set_mesh(mesh, int: marker, m_f):
    # mesh, marker, m_f ->  coupling_mesh_vertices, n_vertices
    precice.setMeshEdge(...)
    precice.setMeshVertices(...)

Implement and test explicit coupling

To support explicit coupling, some things still have to be implemented. Generally, there are two flavours that we would like to support:

  • FEniCS participant only writes data. See also #59 (comment)
  • FEniCS participant only reads data.

Move heat flux computation into a CHT module

In the examples fenics-fenics and buoyantPimpleFoam-fenics the flux computation is basically identical, since the examples deal with identical physics.

The solver uses Dirichlet BC on the whole boundary, while heat flux (Neumann BC) is computed in a post-processing step. Heat flux computation is problem dependent, but is not a required feature to solve the actual problem. We only need heat flux for coupling.

Therefore, we should move this functionality into a common CHT (Conjugate Heat Transfer) module (see OpenFOAM Adapter) to avoid code duplication and to ease reuse for coupled simulations with the same coupling physics (CHT), but a different solver (e.g. different boundary conditions, modeling...).

Support multiple Neumann boundary conditions per domain and provide a tutorial on this feature

Our adapter only supports a single Neumann boundary condition on the whole domain (see FEniCS tutorial book, p.85). We add g*ds in the weak form to implement Neumann boundary conditions.

To be able to support multiple Neumann boundary conditions (for example, one coupling boundary condition and one on the original boundary), we have to use markers to restrict the boundary integral to the respective subdomain. Pseudocode:

coupling_domain.mark(boundary_markers, 1)
F += g*ds(1)

For more information see FEniCS tutorial book, section 4.4

Reduce error with waveform relaxation + subcycling

Theoretically waveform relaxation should reduce the error for a scenario with subcycling to the order of magnitude that we observe without subcycling. Is this true? We should check this!

Cases to check

for cases see fenics-fenics/README.md

Machine precision can be reached

  • WR11
  • WR12 (Neumann solver subcycles; temperature at interface is subcycled, flux is interpolated)
  • WR21 (Dirichlet solver subcycles; flux at interface is subcycled, temperature is interpolated)
  • WR22 (Both subcycle; temperature and flux at interface are subcycled)

Identical results for Quasi-Newton (compare precice-*-iterations.log of scenario with and without WR)

  • WR11
  • WR22 (can we expect identical results here? See #21 (comment))

Support 3D cases

Current handling of Quasi 2D - 3D cases:

To be able to support the 2D/3D coupling in the example tutorials/CHT/flow-over-plate/buoyantPimpleFoam-fenics we have to allow simulations, where the dimensionality is 3, even though there is no proper support for 3D simulations in the fenics adapter yet. See, for example

elif self._dimensions == 3:
return np.stack([vertices_x, vertices_y, vertices_z]), n

and

if self._dimensions == 3:
vertices_z = vertices[2, :]

If someone wants to run a "real" 3D simulation, the adapter does not work. We should either throw an exception in this case or implement a proper 3D treatment.

Update:

  • Support 3D cases with the new modular design of the adapter.

Create a simple structure solver in FEniCS and a corresponding tutorial

Currently this is just a very rough idea, but something in the direction of our already existing FSI tutorials would be very nice.

Necessary steps:

  • Implement a simple FEM Structure solver in FEniCS
  • Implement one of already existing tutorials with FEniCS as structure participant
  • set up 3D tube example

Flexible Datastructure for Waveform Relaxation related data?

Currently (99058d0), there are many if-branches in the adapter due to waveform relaxation. The main questions that are answered through these branches are either regarding 1. logic or 2. data (for examples see below). I assume we cannot do a lot about the branches dealing with logic, however, it would be nice to have a flexible and smart data structure that avoids the branches and iterations necessary for data access.

Remark: Lists of numpy arrays are used for waveform relaxation, single numpy arrays are used elsewise.

1.Logic

Do we have to use specialized logic for treatment of waveform relaxation? In the following there are some examples:

initialize boundary from given read_data

if self._waveform_relaxation_is_used():
self._coupling_bc_expression.set_boundary_data(self._read_data[-1], x_vert, y_vert)
else:
self._coupling_bc_expression.set_boundary_data(self._read_data, x_vert, y_vert)

perform substep in window

if self._waveform_relaxation_is_used():
t, n, success = self._perform_substep(write_function, t, dt, n)

initialize read/write_data_id

if self._waveform_relaxation_is_used():
self._write_data_id = []
for i in range(self._N_this + 1):
self._write_data_id.append(self._interface.getDataID(self._write_data_name[i], self._mesh_id))
else:
self._write_data_id = self._interface.getDataID(self._write_data_name, self._mesh_id)

if self._waveform_relaxation_is_used():
self._read_data_id = []
for i in range(self._N_other + 1):
self._read_data_id.append(self._interface.getDataID(self._read_data_name[i], self._mesh_id))
else:
self._read_data_id = self._interface.getDataID(self._read_data_name, self._mesh_id)

do initial guess for next window

if self._waveform_relaxation_is_used():
# todo the following part of code is really cryptic. we should really refactor it
"""
What's the actual purpose: We never update self._write_data[0], but we have to keep it for
interpolation. Therefore, after an iteration has ended successfully, we have to use the last sample of
the successful iteration as the first sample for the next. Additionally, we need an initial guess for
the read data.
"""
initial_guess_write = np.copy(self._write_data[-1])
for i in range(self._N_this + 1):
self._write_data[i] = np.copy(initial_guess_write)
initial_guess_read = np.copy(self._read_data[-1])
for i in range(self._N_other + 1):
self._read_data[i] = np.copy(initial_guess_read)
"""
until here
"""

reset counters

if self._waveform_relaxation_is_used():
self._reset_window_counters()

2.Data

Do we have to iterate over multiple samples of data? In the following there are some examples:

initialize read/write_data

if self._waveform_relaxation_is_used():
for i in range(self._N_this + 1):
self._write_data.append(self.convert_fenics_to_precice(write_function_init, self._mesh_fenics, self._coupling_subdomain))
self._write_data[0] = None # todo: nonsense code
assert (self._write_data.__len__() == self._N_this + 1)
else:
self._write_data = self.convert_fenics_to_precice(write_function_init, self._mesh_fenics, self._coupling_subdomain)

if self._waveform_relaxation_is_used():
for i in range(self._N_other + 1):
self._read_data.append(self.convert_fenics_to_precice(read_function_init, self._mesh_fenics, self._coupling_subdomain))
self._read_data[0] = None # todo: nonsense code
assert (self._read_data.__len__() == self._N_other + 1)
else:
self._read_data = self.convert_fenics_to_precice(read_function_init, self._mesh_fenics, self._coupling_subdomain)

read/writeBlockScalarData in advance

if self._waveform_relaxation_is_used():
for i in range(1, self._N_this + 1): # todo should start at 0
self._interface.writeBlockScalarData(self._write_data_id[i], self._n_vertices, self._vertex_ids, self._write_data[i])
else:
x_vert, y_vert = self.extract_coupling_boundary_coordinates()
self._write_data = self.convert_fenics_to_precice(write_function, self._mesh_fenics, self._coupling_subdomain)
self._interface.writeBlockScalarData(self._write_data_id, self._n_vertices, self._vertex_ids, self._write_data)

if self._waveform_relaxation_is_used():
for i in range(1, self._N_other + 1): # todo should start at 0
self._interface.readBlockScalarData(self._read_data_id[i], self._n_vertices, self._vertex_ids, self._read_data[i])
assert(False) # TODO possible reason for bug here? Should we update the coupling bc at this point?
else:
self._interface.readBlockScalarData(self._read_data_id, self._n_vertices, self._vertex_ids, self._read_data)
self._coupling_bc_expression.update_boundary_data(self._read_data, x_vert, y_vert) # TODO: this should go somewhere inside _perform_substep, however, if we do not use Waveform relaxation, we have to run the command after calling advance and readBlockScalarData

Note: Here execution logic and data access is mixed. Can we split this properly? See this comment.

read/writeBlockScalarData in initialize

if self._waveform_relaxation_is_used():
for i in range(1, self._N_this + 1): # todo should start at 0
self._interface.writeBlockScalarData(self._write_data_id[i], self._n_vertices, self._vertex_ids, self._write_data[i])
else:
self._interface.writeBlockScalarData(self._write_data_id, self._n_vertices, self._vertex_ids, self._write_data)

if self._waveform_relaxation_is_used():
for i in range(1, self._N_other + 1): # todo should start at 0
self._interface.readBlockScalarData(self._read_data_id[i], self._n_vertices, self._vertex_ids, self._read_data[i])
else:
self._interface.readBlockScalarData(self._read_data_id, self._n_vertices, self._vertex_ids, self._read_data)

Create a first draft for Waveform Relaxation in this adapter

Is it possible to hide a waveform relaxation in this adapter? This would be a useful step towards solving precice/precice#133.

What we want:

  • use waveform relaxation in a tutorial case. For example: fenics-fenics
  • The source code of the tutorial case should not be changed.
  • All the logic for waveform relaxation (with linear interpolation) should be hidden inside this adapter.
  • How to configure the timestep- and window-size in waveform relaxation is an open question.

Naming of the adapter package is inconsistent

Hi,

I found the following inconsistency with naming of the package.

Short version

The adapter is sometimes referred to as fenics-adapter and sometimes as fenicsadapter. It would be nice to stick if one name. I would prefer fenicsadapter as this is also the name of the module in Python.

Detailed description

The adapter is sometimes referred to as fenics-adapter, see for example the name of this repositiory. However the package is called fenicsadapter so in your code you have something like

from fenicsadapter import Adapter, ExactInterpolationExpression, GeneralInterpolationExpression

I was a bit confused about that when I wanted to remove the adapter using pip. I only got a warning about the package not being instaled:

pip3 uninstall fenicsadapter
WARNING: Skipping fenicsadapter as it is not installed.

Instead I had to do

pip3 uninstall fenics-adapter

I expected that the adapter would be uninstalled via the first command without the dash.

In my local ~/.local/lib/python3.6/site-packages/ I find fenics_adapter-0.1.dist-info/ and fenicsadapter/ that both refer to the adapter if I understand correctly?! I guess that has to be like it.

Thanks for your work!

Provide robust initialization

Currently the user has to provide coupling_subdomain, mesh and function_space as mandatory arguments of initialize. write_function=None, fixed_boundary=None are optional arguments.

In a general FEniCS setup the read function and write function need not live on the same function space V (see heat.py). The current design of the adapter assumes that the user provides the function space associated with the read function to initialize (see heat.py). This prompts the need to extract additional information of the function space associated with the write function when writing data to preCICE.

A more general initialization routine can handle this smartly. The way to approach this is to use this adapter in more and more advanced FEniCS applications and modify the design by custom requirement. Parallelization of the adapter in #71 already highlights such a design methodology.

Currently the same initialization routine is used if a user wants to read and write data via FEniCS Function / Expression objects or if the user wants to handle multiple PointSource objects. Further design ideas to differentiate these two cases need to be implemented.

Handling of vector-valued coupling data

For structural solver handling of vector function space is required, however this is currently not supported by the fenicsadapter.py . At first it seemed that it can be fixed easily, as there are Vector equivalents of Scalar functions in PySolverInterface.py (e.g. writeBlockScalarData() and writeBlockVectorData()), but actually there is no difference in the body of these functions, hence it does not help.

Anyway, I am first going to change from 'PySolverInterface.py' to 'precice' and then try to cope with this issue (#25).

Provide package via PyPI

We want to allow installation via

pip3 install --user fenicsadapter

from PyPI (no need to clone this repository).

Why not use names from the preCICE API?

The following API functions are just wrapping the preCICE API. Why do we use different names, here?

If there is no specific reason, I would suggest to keep the names consistent.

def action_write_checkpoint(self):
"""
Get name of action to convey to preCICE that a checkpoint has been written.
Notes
-----
Refer action_write_iteration_checkpoint() in https://github.com/precice/python-bindings/blob/develop/precice.pyx
Returns
-------
action : string
Name of action related to writing a checkpoint.
"""
return action_write_iteration_checkpoint()
def action_read_checkpoint(self):
"""
Get name of action to convey to preCICE that a checkpoint has been read and the state of the system has been
restored to that checkpoint.
Notes
-----
Refer action_read_iteration_checkpoint() in https://github.com/precice/python-bindings/blob/develop/precice.pyx
Returns
-------
action : string
Name of action related to reading a checkpoint.
"""
return action_read_iteration_checkpoint()

Location of read_data in partitioned-heat correct?

Fix NP with 2D/3D mapping

Currently, we cannot use the CHT tutorial with nearest projection (NP), since 2D-3D mapping is not covered. Until this bug is fixed, we deactivate NP and use nearest neighbour as default.

Add Unit Tests

There is already a test for the CustomExpression class in fenics-adapter/tests. However, this test is not embedded in Travis and it is also not implemented in a proper Unit Testing framework. Generally speaking unit tests for the adapter would be very helpful to be able to make sure that everythign works as expected for different combinations python/fenics/precice versions.

Allow One Way Coupling

Currently the adapter expects 2-way coupling: reading and writing. For aste I want to read only, so there are a few small changes needed.

Naming of software, package and repository

We want to use the following names for this project:

  • The name of the software is "FEniCS-preCICE Adapter", since this is a name that tells both preCICE and FEniCS users/developers what the software does.
  • The name of this repository is precice/fenics-adapter, since this is consistent with the other adapters and represents the perspective of a preCICE developer.
  • The name of the package is fenicsprecice, since this repesents the perspective of a FEniCS user import fenicsprecice.

This comes with a few todos:

Support multiple coupling subdomains

def configure(self, participant, precice_config_file, mesh, write_data, read_data):
self._solver_name = participant
self._interface = PySolverInterface.PySolverInterface(self._solver_name, 0, 1)
self._interface.configure(precice_config_file)
self._dimensions = self._interface.getDimensions()
self._mesh_name = mesh
self._write_data_name = write_data
self._read_data_name = read_data

By having the configure() directly inside the Adapter class, we assume that the adapter can only work with one coupling interface. By separating the adapter class (that also handles the time control etc) from the interface itself, we could support multiple coupling interfaces (for example, for Fluid-Solid-Fluid coupling, as in the shell-and-tubes heat exchanger tutorial).

Then, the configure() method would not be called in the adapter itself, but in each coupling interface. The same for the methods to read/write data etc.

Use 2nd order mapping for FEniCS-FEniCS example

In https://github.com/precice/tutorials/tree/master/HT/partitioned-heat/fenics-fenics we currently use nearest-projection. Since no connectivity infomation is given, this falls back to (1st order) nearest-neightbor.

We use a linear finite element discretization (2nd order error). Therefore, we should also use a 2nd order mapping. Otherwise, the discretization error (probably) drops to 1st order due to the mapping.

Bonus: Thorough study on this expected behaviour would be interesting.

Write test to check get_point_sources functionality

Functionality get_point_sources() needs to be tested using the mock testing framework. A test was attempted here but it is not as straightforward as testing the dolfin Expression functionality or the preCICE API calls.
More details on this are provided as a TODO in the commented code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.