예제 #1
0
    def reset(self):
        self._count = -1
        self._functional = dolfin_adjoint.Function(self._V,
                                                   name="functional {}".format(
                                                       self.model))

        self.model_function = dolfin_adjoint.Function(
            self.model._V, name="model function {}".format(self.model))
        self.data_function = dolfin_adjoint.Function(
            self.model._V, name="data function {}".format(self.model))
예제 #2
0
    def __call__(self, u=None):

        if u is None:
            u = dolfin.Function(
                self._displacement_space,
                name="Zero displacement from strain observation",
            )

        u_int = map_displacement(
            u, self._displacement_space, self._interpolation_space, self._approx
        )
        # We need to correct for th reference deformation
        F = pulse.kinematics.DeformationGradient(u_int) * dolfin.inv(self._F_ref)

        # Compute the strains
        if self._tensor == "gradu":
            tensor = pulse.kinematics.EngineeringStrain(F, isochoric=self._isochoric)

        elif self._tensor == "E":
            tensor = pulse.kinematics.GreenLagrangeStrain(F, isochoric=self._isochoric)

        elif self._tensor == "almansi":
            tensor = pulse.kinematics.EulerAlmansiStrain(F, isochoric=self._isochoric)

        form = dolfin.inner(tensor * self.field, self.field)
        strain = dolfin_adjoint.Function(self._V, name="Simulated Strain")
        dolfin_adjoint.solve(
            dolfin.inner(self._trial, self._test) / self._vol * self._dmu
            == dolfin.inner(self._test, form) * self._dmu,
            strain,
            solver_parameters={"linear_solver": "gmres"},
        )

        return strain
예제 #3
0
    def __call__(self, u=None):
        """

        Arguments
        ---------
        u : :py:class:`dolfin.Function`
            The displacement
        """

        if u is None:

            volume_form = (-1.0 / 3.0) * dolfin.dot(self._X, self._N)

        else:
            u_int = map_displacement(
                u, self._displacement_space, self._interpolation_space, self._approx
            )

            # Compute volume
            F = pulse.kinematics.DeformationGradient(u_int)
            J = pulse.kinematics.Jacobian(F)
            volume_form = (-1.0 / 3.0) * dolfin.dot(
                self._X + u_int, J * dolfin.inv(F).T * self._N
            )

        volume = dolfin_adjoint.Function(self._V, name="Simulated volume")
        # Make a project for dolfin-adjoint recording
        dolfin_adjoint.solve(
            dolfin.inner(self._trial, self._test) / self._endoarea * self._dmu
            == dolfin.inner(volume_form, self._test) * self._dmu,
            volume,
        )

        return volume
예제 #4
0
    def assign_control(self, value, annotate=True):
        """
        Assign value to control parameter
        """
        control_new = dolfin_adjoint.Function(self.control.function_space(),
                                              name="new control")

        if isinstance(
                value,
            (
                dolfin.Function,
                dolfin_adjoint.Function,
                RegionalParameter,
                MixedParameter,
            ),
        ):
            control_new.assign(value)

        elif isinstance(value, float) or isinstance(value, int):
            numpy_mpi.assign_to_vector(control_new.vector(), np.array([value]))

        elif isinstance(value, pyadjoint.enlisting.Enlist):
            val_delisted = dolfin_adjoint.delist(value, self.controls)
            control_new.assign(val_delisted)

        else:
            numpy_mpi.assign_to_vector(control_new.vector(),
                                       numpy_mpi.gather_broadcast(value))

        self.control.assign(control_new, annotate=annotate)
def test_volume(volumes):

    geo = pulse.HeartGeometry.from_file(pulse.mesh_paths["simple_ellipsoid"])
    V_cg2 = dolfin.VectorFunctionSpace(geo.mesh, "CG", 2)
    u = dolfin_adjoint.Function(V_cg2)
    volume_obs = VolumeObservation(mesh=geo.mesh,
                                   dmu=geo.ds(geo.markers["ENDO"]),
                                   description="Test LV volume")

    model_volume = volume_obs(u).vector().get_local()[0]
    target = OptimizationTarget(volumes, volume_obs, collect=True)
    if np.isscalar(volumes):
        volumes = (volumes, )

    fs = []
    for t, v in zip(target, volumes):
        fun = dolfin.project(t.assign(u), t._V)
        f = fun.vector().get_local()[0]
        fs.append(f)
        g = (model_volume - v)**2
        assert abs(f - g) < 1e-12

    # We collect the data
    assert np.all(
        np.subtract(
            np.squeeze([v.get_local()
                        for v in target.collector["data"]]), volumes) < 1e-12)
    assert np.all(
        np.subtract(
            np.squeeze([v.get_local() for v in target.collector["model"]]),
            model_volume) < 1e-12)
    assert np.all(np.subtract(target.collector["functional"], fs) < 1e-12)
예제 #6
0
 def solve_problem_variational_form(self):
     u = da.Function(self.V)
     du = fa.TrialFunction(self.V)
     v = fa.TestFunction(self.V)
     E = self._energy_density(u) * fa.dx
     dE = fa.derivative(E, u, v)
     jacE = fa.derivative(dE, u, du)
     da.solve(dE == 0, u, self.bcs, J=jacE)
     return u
예제 #7
0
 def debug(self):
     _, B_np = self.compute_operators()
     dof_data = np.random.rand(self.num_dofs)
     u = da.Function(self.V)
     u.vector()[:] = dof_data
     L1 = da.assemble(u * u * fa.dx)
     L2 = (np.matmul(B_np, dof_data) * dof_data).sum()
     print(L1)
     print(L2)
예제 #8
0
 def _obj(self, x):
     f = da.Function(self.poisson.V)
     f.vector()[:] = x
     self.poisson.source = f
     u = self.poisson.solve_problem_variational_form()
     L_tape = da.assemble(
         (0.5 * fa.inner(u - self.target_u, u - self.target_u) +
          0.5 * self.alpha * fa.inner(f, f)) * fa.dx)
     L = float(L_tape)
     return L, L_tape, f
def test_volume_CG2(approx):

    u = dolfin_adjoint.Function(V_cg2)
    volume_obs = VolumeObservation(
        mesh=geo.mesh,
        dmu=geo.ds(geo.markers["ENDO"]),
        approx=approx,
        description="Test LV volume",
    )

    v1 = volume_obs()
    assert norm(v1.vector().get_local() - 2.51130402) < 1e-8
    v2 = volume_obs(u)
    assert norm(v2.vector().get_local() - 2.51130402) < 1e-8
예제 #10
0
    def __init__(self,
                 bcs,
                 solver_parameters,
                 pressure,
                 params,
                 annotate=False):

        passive_filling_duration = solver_parameters[
            "passive_filling_duration"]
        self.acin = params["active_contraction_iteration_number"]
        fname = "active_state_{}.h5".format(self.acin)
        if os.path.isfile(fname):
            if dolfin.mpi_comm_world().rank == 0:
                os.remove(fname)

        BasicHeartProblem.__init__(self, bcs, solver_parameters, pressure)

        # Load the state from the previous iteration
        w_temp = dolfin_adjoint.Function(self.solver.state_space,
                                         name="w_temp")
        with dolfin.HDF5File(dolfin.mpi_comm_world(), params["sim_file"],
                             "r") as h5file:

            # Get previous state
            if params["active_contraction_iteration_number"] == 0:
                it = (passive_filling_duration
                      if params["unload"] else passive_filling_duration - 1)
                group = "/".join([
                    params["h5group"], PASSIVE_INFLATION_GROUP, "states",
                    str(it)
                ])

            else:
                group = "/".join([
                    params["h5group"],
                    ACTIVE_CONTRACTION_GROUP.format(
                        params["active_contraction_iteration_number"] - 1),
                    "states",
                    "0",
                ])

            h5file.read(w_temp, group)

        self.solver.reinit(w_temp, annotate=annotate)
        self.solver.solve()
예제 #11
0
W = fa.FunctionSpace(mesh, "DG", 0)

# g is the ground truth for source term
# f is the control variable
if case_flag == 0:
    g = da.interpolate(
        da.Expression("1/(1+alpha*4*pow(pi, 4))*w", w=w, alpha=alpha,
                      degree=3), W)
    f = da.interpolate(
        da.Expression("1/(1+alpha*4*pow(pi, 4))*w", w=w, alpha=alpha,
                      degree=3), W)
else:
    g = da.interpolate(da.Expression(("sin(2*pi*x[0])"), degree=3), W)
    f = da.interpolate(da.Expression(("sin(2*pi*x[0])"), degree=3), W)

u = da.Function(V, name='State')
v = fa.TestFunction(V)

F = (fa.inner(fa.grad(u), fa.grad(v)) - f * v) * fa.dx
bc = da.DirichletBC(V, 0.0, "on_boundary")
da.solve(F == 0, u, bc)

d = da.Function(V)
d.vector()[:] = u.vector()[:]

J = da.assemble((0.5 * fa.inner(u - d, u - d)) * fa.dx +
                alpha / 2 * f**2 * fa.dx)
control = da.Control(f)
rf = da.ReducedFunctional(J, control)

# set the initial value to be zero for optimization
예제 #12
0
    def __call__(self, value):

        logger.debug("\nEvaluate functional...")

        # annotation.annotate = True
        # Start recording

        # dolfin_adjoint.adj_reset()
        tape = dolfin_adjoint.get_working_tape()
        tape.reset_blocks()

        self.collector["count"] += 1
        new_control = dolfin_adjoint.Function(self.control.function_space(),
                                              name="new_control")
        # self.assign_control(value, annotate=annotation.annotate)

        # annotation.annotate = False
        if isinstance(
                value,
            (
                dolfin_adjoint.Function,
                dolfin.Function,
                dolfin_adjoint.Constant,
                dolfin.Constant,
                RegionalParameter,
                MixedParameter,
            ),
        ):
            new_control.assign(value)
        elif isinstance(value, float) or isinstance(value, int):
            numpy_mpi.assign_to_vector(new_control.vector(), np.array([value]))
        # elif isinstance(value, pyadjoint.enlisting.Enlist):

        else:
            numpy_mpi.assign_to_vector(new_control.vector(),
                                       numpy_mpi.gather_broadcast(value))

        if self.verbose:

            arr = numpy_mpi.gather_broadcast(new_control.vector().array())
            msg = ("\nCurrent value of control:"
                   "\n\t{:>8}\t{:>8}\t{:>8}\t{:>8}\t{:>8}"
                   "\n\t{:>8.2f}\t{:>8.2f}\t{:>8.2f}\t{:>8d}\t{:>8d}").format(
                       "Min",
                       "Mean",
                       "Max",
                       "argmin",
                       "argmax",
                       np.min(arr),
                       np.mean(arr),
                       np.max(arr),
                       np.argmin(arr),
                       np.argmax(arr),
                   )

            logger.debug(msg)

        # Change loglevel to avoid too much printing
        change_log_level = (self.log_level
                            == logging.INFO) and not self.verbose

        if change_log_level:
            logger.setLevel(logging.WARNING)

        t = dolfin.Timer("Forward run")
        t.start()

        logger.debug("\nEvaluate forward model")

        # annotation.annotate = True
        crash = False
        try:
            self.forward_result = self.forward_model(new_control,
                                                     annotate=True)
        except SolverDidNotConverge:
            crash = True

        forward_time = t.stop()
        self.collector["forward_times"].append(forward_time)
        logger.debug(("Evaluating forward model done. "
                      "Time to evaluate = {} seconds".format(forward_time)))

        if change_log_level:
            logger.setLevel(self.log_level)

        if self.first_call:
            # Store initial results
            self.collector["initial_results"] = self.forward_result
            self.first_call = False

            # Some printing
            # logger.info(utils.print_head(self.for_res))

        control = dolfin_adjoint.Control(self.control)

        # dolfin_adjoint.ReducedFunctional.__init__(
        #     self, dolfin_adjoint.Functional(self.forward_result.functional), control
        # )
        super().__init__(self.forward_result.functional, control)

        if crash:
            # This exection is thrown if the solver uses more than x steps.
            # The solver is stuck, return a large value so it does not get
            # stuck again
            logger.warning(
                Text.red(("Iteration limit exceeded. "
                          "Return a large value of the functional")))
            # Return a big value, and make sure to increment the big value
            # so the the next big value is different from the current one.
            func_value = np.inf
            self.collector["nr_crashes"] += 1

        else:
            func_value = self.forward_result.functional

        # grad_norm = None if len(self.grad_norm_scaled) == 0 \
        # else self.grad_norm_scaled[-1]

        self.collector["functional_values"].append(
            float(func_value) * self.scale)
        self.collector["controls"].append(dolfin.Vector(self.control.vector()))

        logger.debug(Text.yellow("Stop annotating"))
        annotation.annotate = False

        self.print_line()
        return self.scale * func_value
예제 #13
0
def main():
    N = 5
    mesh = da.UnitCubeMesh(N, N, N)

    W = df.FunctionSpace(mesh, "CG", 1)

    active = da.Function(W)
    problem = create_forward_problem(mesh, active, active_value=0.1)
    u, _ = problem.state.split()

    V = df.VectorFunctionSpace(mesh, "CG", 2)
    u_synthetic = da.project(u, V)

    active_ctrl = da.Function(W)
    problem = create_forward_problem(mesh, active_ctrl, active_value=0.0)
    u_model, _ = problem.state.split()

    J = cost_function(
        u_model,
        u_synthetic,
    )

    cost_func_values = []
    control_values = []

    def eval_cb(j, m):
        """Callback function"""
        cost_func_values.append(j)
        control_values.append(m)

    reduced_functional = da.ReducedFunctional(
        J,
        da.Control(active_ctrl),
        eval_cb_post=eval_cb,
    )

    problem = da.MinimizationProblem(reduced_functional, bounds=[(0, 0.4)])

    parameters = {
        "limited_memory_initialization": "scalar2",
        "maximum_iterations": 10,
    }
    solver = da.IPOPTSolver(problem, parameters=parameters)

    optimal_control = solver.solve()

    control_error = [
        df.assemble((c - active)**2 * df.dx) for c in control_values
    ]

    fig, ax = plt.subplots(2, 1, sharex=True)
    ax[0].semilogy(cost_func_values)
    ax[0].set_xlabel("#iterations")
    ax[0].set_ylabel("cost function")

    ax[1].semilogy(control_error)
    ax[1].set_xlabel("#iterations")
    ax[1].set_ylabel(r"$\int (\gamma - \gamma^*) \mathrm{d}x$")
    fig.tight_layout()
    fig.savefig("results")

    mean = optimal_control.vector().get_local().mean()
    std = optimal_control.vector().get_local().std()
    print(f"Optimal control is {mean} +/- {std}")
예제 #14
0
def create_problem():

    geometry = pulse.HeartGeometry.from_file(
        pulse.mesh_paths["simple_ellipsoid"])

    activation = dolfin_adjoint.Function(dolfin.FunctionSpace(
        geometry.mesh, "R", 0),
                                         name="activation")
    activation.assign(dolfin_adjoint.Constant(0.0))
    matparams = pulse.HolzapfelOgden.default_parameters()

    V = dolfin.FunctionSpace(geometry.mesh, "R", 0)
    control = dolfin_adjoint.Function(V)
    control.assign(dolfin_adjoint.Constant(1.0))
    # control = dolfin_adjoint.Constant(1.0, name="matparam control (a)")
    matparams["a"] = control

    f0 = dolfin_adjoint.Function(geometry.f0.function_space())
    f0.assign(geometry.f0)
    s0 = dolfin_adjoint.Function(geometry.s0.function_space())
    s0.assign(geometry.s0)
    n0 = dolfin_adjoint.Function(geometry.n0.function_space())
    n0.assign(geometry.n0)

    material = pulse.HolzapfelOgden(activation=activation,
                                    parameters=matparams,
                                    f0=f0,
                                    s0=s0,
                                    n0=n0)

    # LV Pressure
    lvp = dolfin_adjoint.Constant(0.0, name="lvp")
    lv_marker = geometry.markers["ENDO"][0]
    lv_pressure = pulse.NeumannBC(traction=lvp, marker=lv_marker, name="lv")
    neumann_bc = [lv_pressure]

    # Add spring term at the base with stiffness 1.0 kPa/cm^2
    base_spring = 1.0
    robin_bc = [
        pulse.RobinBC(
            value=dolfin_adjoint.Constant(base_spring, name="base_spring"),
            marker=geometry.markers["BASE"][0],
        )
    ]

    # Fix the basal plane in the longitudinal direction
    # 0 in V.sub(0) refers to x-direction, which is the longitudinal direction
    def fix_basal_plane(W):
        V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0)
        bc = dolfin_adjoint.DirichletBC(
            V.sub(0),
            dolfin.Constant(0.0, name="fix_base"),
            geometry.ffun,
            geometry.markers["BASE"][0],
        )
        return bc

    dirichlet_bc = [fix_basal_plane]

    # Collect boundary conditions
    bcs = pulse.BoundaryConditions(dirichlet=dirichlet_bc,
                                   neumann=neumann_bc,
                                   robin=robin_bc)

    # Create the problem
    problem = pulse.MechanicsProblem(geometry, material, bcs)

    return problem, control
예제 #15
0
 def set_control_variable(self, dof_data):
     self.source = da.Function(self.V)
     self.source.vector()[:] = dof_data