FESTIM icon indicating copy to clipboard operation
FESTIM copied to clipboard

Ability to make a copy of `Simulation` object

Open RemDelaporteMathurin opened this issue 1 year ago • 2 comments

I can see a few use cases where being able to make a copy of a simulation could be useful.

At the moment, these are the options:

  1. New simulation + transfer parameters
import festim as F


model1 = F.Simulation()
model1.materials = F.Material(D_0=1, E_D=1, id=1)
model1.boundary_conditions = [F.DirichletBC(surfaces=[1], value=0, field="solute")]
model1.sources = [F.Source(volume=1, value=1, field="solute")]
model1.mesh = F.MeshFromVertices([1, 2, 3, 4])
model1.traps = [F.Trap(k_0=1, E_k=1, p_0=1, E_p=1, density=1, materials=model1.materials[0])]
model1.settings = F.Settings(transient=True, final_time=1, absolute_tolerance=1e-10, relative_tolerance=1e-10)
model1.T = 200
model1.dt = F.Stepsize(0.1)

model1.initialise()
model1.run()

model2 = F.Simulation()
model2.materials = model1.materials
model2.boundary_conditions = model1.boundary_conditions
model2.sources = model1.sources
model2.mesh = model1.mesh
model2.traps = model1.traps
model2.settings = model1.settings
model2.T = 500  # CHANGE THIS PARAMETER ONLY
model2.dt = model1.dt
  1. Changing the original model
import festim as F

model1 = F.Simulation()
model1.materials = F.Material(D_0=1, E_D=1, id=1)
model1.boundary_conditions = [F.DirichletBC(surfaces=[1], value=0, field="solute")]
model1.sources = [F.Source(volume=1, value=1, field="solute")]
model1.mesh = F.MeshFromVertices([1, 2, 3, 4])
model1.traps = [F.Trap(k_0=1, E_k=1, p_0=1, E_p=1, density=1, materials=model1.materials[0])]
model1.settings = F.Settings(transient=True, final_time=1, absolute_tolerance=1e-10, relative_tolerance=1e-10)
model1.T = 200
model1.dt = F.Stepsize(0.1)

model1.initialise()
model1.run()

model1.T = 500  # CHANGE THIS PARAMETER ONLY
model1.initialise()
model1.run()

Ideally we would want:

  1. deepcopy the simulation
import festim as F

model1 = F.Simulation()
model1.materials = F.Material(D_0=1, E_D=1, id=1)
model1.boundary_conditions = [F.DirichletBC(surfaces=[1], value=0, field="solute")]
model1.sources = [F.Source(volume=1, value=1, field="solute")]
model1.mesh = F.MeshFromVertices([1, 2, 3, 4])
model1.traps = [F.Trap(k_0=1, E_k=1, p_0=1, E_p=1, density=1, materials=model1.materials[0])]
model1.settings = F.Settings(transient=True, final_time=1, absolute_tolerance=1e-10, relative_tolerance=1e-10)
model1.T = 200
model1.dt = F.Stepsize(0.1)

model1.initialise()
model1.run()

from copy import deepcopy

model2 = deepcopy(model1)
model2 .T = 500  # CHANGE THIS PARAMETER ONLY
model2 .initialise()
model2 .run()

However this is not possible because tries to copy some parameters that are not copiable (eg. fenics.Constant).

TypeError: cannot pickle 'dolfin.cpp.function.Constant' object

We could make our own copy method

  1. Our own copy method
import festim as F
from copy import deepcopy


model1 = F.Simulation()
model1.materials = F.Material(D_0=1, E_D=1, id=1)
model1.boundary_conditions = [F.DirichletBC(surfaces=[1], value=0, field="solute")]
model1.sources = [F.Source(volume=1, value=1, field="solute")]
model1.mesh = F.MeshFromVertices([1, 2, 3, 4])
model1.traps = [F.Trap(k_0=1, E_k=1, p_0=1, E_p=1, density=1, materials=model1.materials[0])]
model1.settings = F.Settings(transient=True, final_time=1, absolute_tolerance=1e-10, relative_tolerance=1e-10)
model1.T = 200
model1.dt = F.Stepsize(0.1)

model1.initialise()
model1.run()


def copy(model):
    new_model = F.Simulation()
    new_model.materials = [deepcopy(mat) for mat in model.materials]
    new_model.boundary_conditions = [deepcopy(bc) for bc in model.boundary_conditions]
    new_model.sources = [deepcopy(src) for src in model.sources]
    new_model.mesh = deepcopy(model.mesh)
    new_model.traps = [deepcopy(trap) for trap in model.traps]
    new_model.settings = deepcopy(model.settings)
    new_model.T = deepcopy(model.T)
    new_model.dt = deepcopy(model.dt)
    
    return new_model

model2 = copy(model1)

However some of these objects are also not copiable. like the BCs.

So we would have to create copy methods for these two.

It could end up requiring a lot of effort to maintain these copy methods over time.

I don't know what others think.

RemDelaporteMathurin avatar Aug 02 '24 14:08 RemDelaporteMathurin

Could you please show the cases where copying is needed?

KulaginVladimir avatar Aug 02 '24 15:08 KulaginVladimir

Here's an example that I can think of. Say you want to make a parametric study and decouple the plotting and simulation.

import festim as F


model = F.Simulation()
model.materials = F.Material(D_0=1, E_D=1, id=1)
model.boundary_conditions = [F.DirichletBC(surfaces=[1], value=0, field="solute")]
model.sources = [F.Source(volume=1, value=1, field="solute")]
model.mesh = F.MeshFromVertices([1, 2, 3, 4])
model.traps = [
    F.Trap(k_0=1, E_k=1, p_0=1, E_p=1, density=1, materials=model.materials[0])
]
model.settings = F.Settings(
    transient=True, final_time=1, absolute_tolerance=1e-10, relative_tolerance=1e-10
)
model.T = 200
model.dt = F.Stepsize(0.1)


# parametric study

for source_val in [1, 2, 3]:
    model.sources[0].value = source_val

    model.initialise()
    model.run()

    # plot the results
    # plt.plot(..., label=f"source_val={source_val}")

# later

# make another plot with first value source_val

This would be facilitated if you could do:

# parametric study
models = []
for source_val in [1, 2, 3]:
    new_model = model.copy()
    new_model.sources[0].value = source_val
    new_model.run()
    models.append(new_model)


# later 

for model in models:
    # plot something

RemDelaporteMathurin avatar Aug 05 '24 08:08 RemDelaporteMathurin