def __init__(self, gb):
        self.gb = gb

        # -- flow -- #
        self.discr_flow = Flow(gb)

        shape = self.discr_flow.shape()
        self.flux_pressure = np.zeros(shape)

        # -- temperature -- #
        self.discr_temperature = Heat(gb)

        # -- solute and precipitate -- #
        self.discr_solute_advection_diffusion = Transport(gb)
        self.discr_solute_precipitate_reaction = Reaction(gb)

        # -- porosity -- #
        self.discr_porosity = Porosity(gb)

        # -- fracture aperture -- #
        self.discr_fracture_aperture = FractureAperture(
            gb, "fracture_aperture")

        # -- layer porosity and aperture -- #
        self.discr_layer_porosity = Porosity(gb, "layer_porosity")
        self.discr_layer_aperture = LayerAperture(gb, "layer_aperture")

        # the actual time of the simulation
        self.time = 0
    def __init__(self, gb):
        self.gb = gb

        # -- flow -- #
        self.discr_flow = Flow(gb)

        shape = self.discr_flow.shape()
        self.flux_pressure = np.zeros(shape)

        # -- temperature -- #
        self.discr_temperature = Heat(gb)

        shape = self.discr_temperature.shape()
        self.temperature = np.zeros(shape)
        self.temperature_old = np.zeros(shape)

        # -- solute and precipitate -- #
        self.discr_solute_advection_diffusion = Transport(gb)
        self.discr_solute_precipitate_reaction = Reaction(gb)

        shape = self.discr_solute_advection_diffusion.shape()
        self.solute = np.zeros(shape)
        self.precipitate = np.zeros(shape)
        self.solute_old = np.zeros(shape)
        self.precipitate_old = np.zeros(shape)

        # -- porosity -- #
        self.discr_porosity = Porosity(gb)

        shape = self.discr_porosity.shape()
        self.porosity = np.zeros(shape)
        self.porosity_old = np.zeros(shape)
        self.porosity_star = np.zeros(shape)

        # -- aperture -- #
        self.discr_aperture = Aperture(gb)

        shape = self.discr_aperture.shape()
        self.aperture = np.zeros(shape)
        self.aperture_old = np.zeros(shape)
        self.aperture_star = np.zeros(shape)

        # -- composite variables -- #
        self.porosity_aperture_times_solute = np.zeros(shape)
        self.porosity_aperture_times_precipitate = np.zeros(shape)
    def initialize(self, filename=None):
        """Initialize the Heat model.

        Parameters
        ----------
        filename : str, optional
            Path to name of input file.
        """
        if filename is None:
            self._model = Heat()
        elif isinstance(filename, types.StringTypes):
            with open(filename, "r") as file_obj:
                self._model = Heat.from_file_like(file_obj.read())
        else:
            self._model = Heat.from_file_like(filename)

        self._values = {"plate_surface__temperature": self._model.temperature}
        self._var_units = {"plate_surface__temperature": "K"}
        self._grids = {0: ["plate_surface__temperature"]}
        self._grid_type = {0: "uniform_rectilinear_grid"}
Example #4
0
    def initialize(self, filename=None):
        """Initialize the Heat model.

        Parameters
        ----------
        filename : str, optional
            Path to name of input file.
        """
        if filename is None:
            self._model = Heat()
        elif isinstance(filename, types.StringTypes):
            with open(filename, 'r') as file_obj:
                self._model = Heat.from_file_like(file_obj.read())
        else:
            self._model = Heat.from_file_like(filename)

        self._values = {
            'plate_surface__temperature': self._model.temperature,
        }
        self._var_units = {'plate_surface__temperature': 'K'}
        self._grids = {0: ['plate_surface__temperature']}
        self._grid_type = {0: 'uniform_rectilinear_grid'}
class BmiHeat(Bmi):

    """Solve the heat equation for a 2D plate."""

    _name = "The 2D Heat Equation"
    _input_var_names = ("plate_surface__temperature",)
    _output_var_names = ("plate_surface__temperature",)

    def __init__(self):
        """Create a BmiHeat model that is ready for initialization."""
        self._model = None
        self._values = {}
        self._var_units = {}
        self._grids = {}
        self._grid_type = {}

    def initialize(self, filename=None):
        """Initialize the Heat model.

        Parameters
        ----------
        filename : str, optional
            Path to name of input file.
        """
        if filename is None:
            self._model = Heat()
        elif isinstance(filename, types.StringTypes):
            with open(filename, "r") as file_obj:
                self._model = Heat.from_file_like(file_obj.read())
        else:
            self._model = Heat.from_file_like(filename)

        self._values = {"plate_surface__temperature": self._model.temperature}
        self._var_units = {"plate_surface__temperature": "K"}
        self._grids = {0: ["plate_surface__temperature"]}
        self._grid_type = {0: "uniform_rectilinear_grid"}

    def update(self):
        """Advance model by one time step."""
        self._model.advance_in_time()

    def update_frac(self, time_frac):
        """Update model by a fraction of a time step.

        Parameters
        ----------
        time_frac : float
            Fraction fo a time step.
        """
        time_step = self.get_time_step()
        self._model.time_step = time_frac * time_step
        self.update()
        self._model.time_step = time_step

    def update_until(self, then):
        """Update model until a particular time.

        Parameters
        ----------
        then : float
            Time to run model until.
        """
        n_steps = (then - self.get_current_time()) / self.get_time_step()

        for _ in xrange(int(n_steps)):
            self.update()
        self.update_frac(n_steps - int(n_steps))

    def finalize(self):
        """Finalize model."""
        self._model = None

    def get_var_type(self, var_name):
        """Data type of variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        str
            Data type.
        """
        return str(self.get_value_ref(var_name).dtype)

    def get_var_units(self, var_name):
        """Get units of variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        str
            Variable units.
        """
        return self._var_units[var_name]

    def get_var_nbytes(self, var_name):
        """Get units of variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        int
            Size of data array in bytes.
        """
        return self.get_value_ref(var_name).nbytes

    def get_var_grid(self, var_name):
        """Grid id for a variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        int
            Grid id.
        """
        for grid_id, var_name_list in self._grids.items():
            if var_name in var_name_list:
                return grid_id

    def get_grid_rank(self, grid_id):
        """Rank of grid.

        Parameters
        ----------
        grid_id : int
            Identifier of a grid.

        Returns
        -------
        int
            Rank of grid.
        """
        return len(self.get_grid_shape(grid_id))

    def get_grid_size(self, grid_id):
        """Size of grid.

        Parameters
        ----------
        grid_id : int
            Identifier of a grid.

        Returns
        -------
        int
            Size of grid.
        """
        return np.prod(self.get_grid_shape(grid_id))

    def get_value_ref(self, var_name):
        """Reference to values.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        array_like
            Value array.
        """
        return self._values[var_name]

    def get_value(self, var_name):
        """Copy of values.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        array_like
            Copy of values.
        """
        return self.get_value_ref(var_name).copy()

    def get_value_at_indices(self, var_name, indices):
        """Get values at particular indices.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.
        indices : array_like
            Array of indices.

        Returns
        -------
        array_like
            Values at indices.
        """
        return self.get_value_ref(var_name).take(indices)

    def set_value(self, var_name, src):
        """Set model values.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.
        src : array_like
            Array of new values.
        """
        val = self.get_value_ref(var_name)
        val[:] = src

    def set_value_at_indices(self, var_name, src, indices):
        """Set model values at particular indices.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.
        src : array_like
            Array of new values.
        indices : array_like
            Array of indices.
        """
        val = self.get_value_ref(var_name)
        val.flat[indices] = src

    def get_component_name(self):
        """Name of the component."""
        return self._name

    def get_input_var_names(self):
        """Get names of input variables."""
        return self._input_var_names

    def get_output_var_names(self):
        """Get names of output variables."""
        return self._output_var_names

    def get_grid_shape(self, grid_id):
        """Number of rows and columns of uniform rectilinear grid."""
        var_name = self._grids[grid_id][0]
        return self.get_value_ref(var_name).shape

    def get_grid_spacing(self, grid_id):
        """Spacing of rows and columns of uniform rectilinear grid."""
        return self._model.spacing

    def get_grid_origin(self, grid_id):
        """Origin of uniform rectilinear grid."""
        return self._model.origin

    def get_grid_type(self, grid_id):
        """Type of grid."""
        return self._grid_type[grid_id]

    def get_start_time(self):
        """Start time of model."""
        return 0.0

    def get_end_time(self):
        """End time of model."""
        return np.finfo("d").max

    def get_current_time(self):
        """Current time of model."""
        return self._model.time

    def get_time_step(self):
        """Time step of model."""
        return self._model.time_step
Example #6
0
class BmiHeat(Bmi):
    """Solve the heat equation for a 2D plate."""

    _name = 'The 2D Heat Equation'
    _input_var_names = ('plate_surface__temperature', )
    _output_var_names = ('plate_surface__temperature', )

    def __init__(self):
        """Create a BmiHeat model that is ready for initialization."""
        self._model = None
        self._values = {}
        self._var_units = {}
        self._grids = {}
        self._grid_type = {}

    def initialize(self, filename=None):
        """Initialize the Heat model.

        Parameters
        ----------
        filename : str, optional
            Path to name of input file.
        """
        if filename is None:
            self._model = Heat()
        elif isinstance(filename, types.StringTypes):
            with open(filename, 'r') as file_obj:
                self._model = Heat.from_file_like(file_obj.read())
        else:
            self._model = Heat.from_file_like(filename)

        self._values = {
            'plate_surface__temperature': self._model.temperature,
        }
        self._var_units = {'plate_surface__temperature': 'K'}
        self._grids = {0: ['plate_surface__temperature']}
        self._grid_type = {0: 'uniform_rectilinear_grid'}

    def update(self):
        """Advance model by one time step."""
        self._model.advance_in_time()

    def update_frac(self, time_frac):
        """Update model by a fraction of a time step.

        Parameters
        ----------
        time_frac : float
            Fraction fo a time step.
        """
        time_step = self.get_time_step()
        self._model.time_step = time_frac * time_step
        self.update()
        self._model.time_step = time_step

    def update_until(self, then):
        """Update model until a particular time.

        Parameters
        ----------
        then : float
            Time to run model until.
        """
        n_steps = (then - self.get_current_time()) / self.get_time_step()

        for _ in xrange(int(n_steps)):
            self.update()
        self.update_frac(n_steps - int(n_steps))

    def finalize(self):
        """Finalize model."""
        self._model = None

    def get_var_type(self, var_name):
        """Data type of variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        str
            Data type.
        """
        return str(self.get_value_ref(var_name).dtype)

    def get_var_units(self, var_name):
        """Get units of variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        str
            Variable units.
        """
        return self._var_units[var_name]

    def get_var_nbytes(self, var_name):
        """Get units of variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        int
            Size of data array in bytes.
        """
        return self.get_value_ref(var_name).nbytes

    def get_var_grid(self, var_name):
        """Grid id for a variable.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        int
            Grid id.
        """
        for grid_id, var_name_list in self._grids.items():
            if var_name in var_name_list:
                return grid_id

    def get_grid_rank(self, grid_id):
        """Rank of grid.

        Parameters
        ----------
        grid_id : int
            Identifier of a grid.

        Returns
        -------
        int
            Rank of grid.
        """
        return len(self.get_grid_shape(grid_id))

    def get_grid_size(self, grid_id):
        """Size of grid.

        Parameters
        ----------
        grid_id : int
            Identifier of a grid.

        Returns
        -------
        int
            Size of grid.
        """
        return np.prod(self.get_grid_shape(grid_id))

    def get_value_ref(self, var_name):
        """Reference to values.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        array_like
            Value array.
        """
        return self._values[var_name]

    def get_value(self, var_name):
        """Copy of values.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.

        Returns
        -------
        array_like
            Copy of values.
        """
        return self.get_value_ref(var_name).copy()

    def get_value_at_indices(self, var_name, indices):
        """Get values at particular indices.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.
        indices : array_like
            Array of indices.

        Returns
        -------
        array_like
            Values at indices.
        """
        return self.get_value_ref(var_name).take(indices)

    def set_value(self, var_name, src):
        """Set model values.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.
        src : array_like
            Array of new values.
        """
        val = self.get_value_ref(var_name)
        val[:] = src

    def set_value_at_indices(self, var_name, src, indices):
        """Set model values at particular indices.

        Parameters
        ----------
        var_name : str
            Name of variable as CSDMS Standard Name.
        src : array_like
            Array of new values.
        indices : array_like
            Array of indices.
        """
        val = self.get_value_ref(var_name)
        val.flat[indices] = src

    def get_component_name(self):
        """Name of the component."""
        return self._name

    def get_input_var_names(self):
        """Get names of input variables."""
        return self._input_var_names

    def get_output_var_names(self):
        """Get names of output variables."""
        return self._output_var_names

    def get_grid_shape(self, grid_id):
        """Number of rows and columns of uniform rectilinear grid."""
        var_name = self._grids[grid_id][0]
        return self.get_value_ref(var_name).shape

    def get_grid_spacing(self, grid_id):
        """Spacing of rows and columns of uniform rectilinear grid."""
        return self._model.spacing

    def get_grid_origin(self, grid_id):
        """Origin of uniform rectilinear grid."""
        return self._model.origin

    def get_grid_type(self, grid_id):
        """Type of grid."""
        return self._grid_type[grid_id]

    def get_start_time(self):
        """Start time of model."""
        return 0.

    def get_end_time(self):
        """End time of model."""
        #Changed by JLG from
        #return np.finfo('d').max
        #to the line below.
        return 100.

    def get_current_time(self):
        """Current time of model."""
        return self._model.time

    def get_time_step(self):
        """Time step of model."""
        return self._model.time_step
Example #7
0
 def __init__(self):
     name = 'Antoine-Laurent de Lavoisier'
     story = "Brought about a rennaissance in chemistry by discovering the Law of Conservation of Mass, inventing our system of naming with Berthollet, working with Laplace on calorimetry, and understanding combustion with his wife."
     year = 1780
     heat = Heat()
     super(Lavoisier, self).__init__(name, year, story, heat)
Example #8
0
 def __init__(self):
     name = 'Alessandro Volta'
     story = 'Constructed the first electrical battery and founded electrochemistry with the invention of galvonic cells.'
     year = 1800
     heat = Heat()
     super(AlessandroVolta, self).__init__(name, year, story, heat)
Example #9
0
 def __init__(self):
     name = 'Robert Boyle'
     story = "Founder of modern chemistry. Boyle's law states that the volume and pressure of a gas are inversely proportional when the temperature is constant. So when the pressure goes up, the volume goes down, and vice versa."
     year = 1662
     heat = Heat()
     super(RobertBoyle, self).__init__(name, year, story, heat)
Example #10
0
 def __init__(self):
     name = 'Sir Francis Bacon'
     story = 'Developed the scientific method.'
     year = 1605
     heat = Heat()
     super(SirFrancisBacon, self).__init__(name, year, story, heat)
Example #11
0
 def __init__(self):
     name = 'Jābir ibn Hayyān (Geber)'
     story = 'Father of chemistry: introduced a systematic classification of chemical substances, and provided instructions for deriving an inorganic compound (sal ammoniac or ammonium chloride) from organic substances (such as plants, blood, and hair) by chemical means. Declared there were 6 idealized elements: air, earth, fire, water, sulphur (combustability), and mercury (metallic properties).'
     year = 800
     heat = Heat()
     super(Geber, self).__init__(name, year, story, heat)
Example #12
0
 def __init__(self):
     story = 'Declared that matter is composed of indivisible and indestructible particles called "atomos"'
     heat = Heat()
     super(Democritus, self).__init__('Democritus', -380, story, heat)
class Scheme(object):

    # ------------------------------------------------------------------------------#

    def __init__(self, gb):
        self.gb = gb

        # -- flow -- #
        self.discr_flow = Flow(gb)

        shape = self.discr_flow.shape()
        self.flux_pressure = np.zeros(shape)

        # -- temperature -- #
        self.discr_temperature = Heat(gb)

        shape = self.discr_temperature.shape()
        self.temperature = np.zeros(shape)
        self.temperature_old = np.zeros(shape)

        # -- solute and precipitate -- #
        self.discr_solute_advection_diffusion = Transport(gb)
        self.discr_solute_precipitate_reaction = Reaction(gb)

        shape = self.discr_solute_advection_diffusion.shape()
        self.solute = np.zeros(shape)
        self.precipitate = np.zeros(shape)
        self.solute_old = np.zeros(shape)
        self.precipitate_old = np.zeros(shape)

        # -- porosity -- #
        self.discr_porosity = Porosity(gb)

        shape = self.discr_porosity.shape()
        self.porosity = np.zeros(shape)
        self.porosity_old = np.zeros(shape)
        self.porosity_star = np.zeros(shape)

        # -- aperture -- #
        self.discr_aperture = Aperture(gb)

        shape = self.discr_aperture.shape()
        self.aperture = np.zeros(shape)
        self.aperture_old = np.zeros(shape)
        self.aperture_star = np.zeros(shape)

        # -- composite variables -- #
        self.porosity_aperture_times_solute = np.zeros(shape)
        self.porosity_aperture_times_precipitate = np.zeros(shape)

    # ------------------------------------------------------------------------------#

    def compute_flow(self):
        A, b = self.discr_flow.matrix_rhs()
        return sps.linalg.spsolve(A, b)

    # ------------------------------------------------------------------------------#

    def compute_temperature(self, porosity_star, aperture_star):
        # compute the matrices
        A, M, b = self.discr_temperature.matrix_rhs()

        # compute the effective thermal capacity, keeping in mind that the fracture
        # contains only water
        rc_w = self.discr_temperature.data["rc_w"]
        rc_s = self.discr_temperature.data["rc_s"]

        c_star = rc_w * (porosity_star + aperture_star) + rc_s * (1 - porosity_star)
        c_old = rc_w * (self.porosity_old + self.aperture_old) + rc_s * (1 - self.porosity_old)

        # the mass term which considers the contribution from the effective thermal capacity
        M_star = M * sps.diags(c_star, 0)
        M_old = M * sps.diags(c_old, 0)

        # compute the new temperature
        return sps.linalg.spsolve(M_star + A, M_old * self.temperature_old + b)

    # ------------------------------------------------------------------------------#

    def compute_solute_precipitate_advection_diffusion(self, porosity_star, aperture_star):
        # compute the matrices
        A, M, b = self.discr_solute_advection_diffusion.matrix_rhs()

        # the mass term which considers both the porosity and aperture contribution
        M_star = M * sps.diags(porosity_star + aperture_star, 0)
        M_old = M * sps.diags(self.porosity_old + self.aperture_old, 0)

        # compute the new solute
        return sps.linalg.spsolve(M_star + A, M_old * self.solute_old + b)

    # ------------------------------------------------------------------------------#

    def compute_solute_precipitate_rection(self, solute_half, precipitate_half):
        # the dof associated to the porous media and fractures, are the first
        dof = self.gb.num_cells()

        # temporary solution vectors
        solute = np.zeros(self.discr_solute_advection_diffusion.shape())
        precipitate = np.zeros(self.discr_solute_advection_diffusion.shape())

        # compute the new solute and precipitate
        solute[:dof], precipitate[:dof] = self.discr_solute_precipitate_reaction.step(
                                                                    solute_half[:dof],
                                                                    precipitate_half[:dof],
                                                                    self.temperature[:dof])
        return solute, precipitate

    # ------------------------------------------------------------------------------#

    def set_old_variables(self):
        self.temperature_old = self.temperature.copy()
        self.solute_old = self.solute.copy()
        self.precipitate_old = self.precipitate.copy()
        self.porosity_old = self.porosity.copy()
        self.aperture_old = self.aperture.copy()

    # ------------------------------------------------------------------------------#

    def set_data(self, param):

        # set the initial condition
        assembler = self.discr_solute_advection_diffusion.assembler
        dof = np.cumsum(np.append(0, np.asarray(assembler.full_dof)))
        for (g, _), bi in assembler.block_dof.items():
            #g = pair[0]
            if isinstance(g, pp.Grid):
                dof_loc = slice(dof[bi], dof[bi+1])

                data = param["temperature"]["initial"]
                self.temperature[dof_loc] = data(g, param, param["tol"])

                data = param["solute_advection_diffusion"]["initial_solute"]
                self.solute[dof_loc] = data(g, param, param["tol"])

                data = param["solute_advection_diffusion"]["initial_precipitate"]
                self.precipitate[dof_loc] = data(g, param, param["tol"])

                data = param["porosity"]["initial"]
                self.porosity[dof_loc] = data(g, param, param["tol"])

                data = param["aperture"]["initial"]
                self.aperture[dof_loc] = data(g, param, param["tol"])

        # set the old variables
        self.set_old_variables()

        # save the initial porosity and aperture
        self.discr_porosity.extract(self.porosity, "porosity_initial")
        self.discr_aperture.extract(self.aperture, "aperture_initial")

        # extract the initialized variables, useful for setting the data
        self.extract()

        # set now the data for each scheme
        self.discr_flow.set_data(param["flow"], param["time"])
        self.discr_temperature.set_data(param["temperature"], param["time"])
        self.discr_solute_advection_diffusion.set_data(param["solute_advection_diffusion"], param["time"])
        self.discr_solute_precipitate_reaction.set_data(param["solute_precipitate_reaction"], param["time"])
        self.discr_porosity.set_data(param["porosity"])
        self.discr_aperture.set_data(param["aperture"])

    # ------------------------------------------------------------------------------#

    def extract(self):

        self.discr_flow.extract(self.flux_pressure)
        self.discr_temperature.extract(self.temperature, "temperature")
        self.discr_solute_advection_diffusion.extract(self.solute, "solute")
        self.discr_solute_advection_diffusion.extract(self.precipitate, "precipitate")

        self.discr_porosity.extract(self.porosity, "porosity")
        self.discr_porosity.extract(self.porosity_old, "porosity_old")

        self.discr_aperture.extract(self.aperture, "aperture")
        self.discr_aperture.extract(self.aperture_old, "aperture_old")

        self.discr_solute_advection_diffusion.extract(self.porosity_aperture_times_solute,
            "porosity_aperture_times_solute")
        self.discr_solute_advection_diffusion.extract(self.porosity_aperture_times_precipitate,
            "porosity_aperture_times_precipitate")

    # ------------------------------------------------------------------------------#

    def vars_to_save(self):
        name = ["solute", "precipitate", "porosity", "aperture", "temperature"]
        name += ["porosity_aperture_times_solute", "porosity_aperture_times_precipitate"]
        return name + [self.discr_flow.pressure, self.discr_flow.P0_flux]

    # ------------------------------------------------------------------------------#


    def one_step_splitting_scheme(self):

        # the dof associated to the porous media and fractures, are the first
        dof = slice(0, self.gb.num_cells())

        # POINT 1) extrapolate the precipitate to get a better estimate of porosity
        precipitate_star = 2*self.precipitate - self.precipitate_old

        # POINT 2) compute the porosity and aperture star
        porosity_star = self.discr_porosity.step(self.porosity_old, precipitate_star, self.precipitate_old)
        self.discr_porosity.extract(porosity_star, "porosity_star")

        aperture_star = self.discr_aperture.step(self.aperture_old, precipitate_star, self.precipitate_old)
        self.discr_aperture.extract(aperture_star, "aperture_star")

        # -- DO THE FLOW PART -- #

        # POINT 3) update the data from the previous time step
        self.discr_flow.update_data()

        # POINT 4) solve the flow part
        self.flux_pressure = self.compute_flow()
        self.discr_flow.extract(self.flux_pressure)

        # -- DO THE HEAT PART -- #

        # POINT 5) set the flux and update the data from the previous time step
        self.discr_temperature.set_flux(self.discr_flow.flux, self.discr_flow.mortar)
        self.discr_temperature.update_data()

        # POINT 5) solve the temperature part
        self.temperature = self.compute_temperature(porosity_star, aperture_star)

        # -- DO THE TRANSPORT PART -- #

        # set the flux and update the data from the previous time step
        self.discr_solute_advection_diffusion.set_flux(self.discr_flow.flux, self.discr_flow.mortar)
        self.discr_solute_advection_diffusion.update_data()

        # POINT 6) solve the advection and diffusion part to get the intermediate solute solution
        solute_half = self.compute_solute_precipitate_advection_diffusion(porosity_star, aperture_star)

        # POINT 7) Since in the advection-diffusion step we have accounted for porosity changes using
        # phi_star, the new solute concentration accounts for the change in pore volume, thus, the
        # precipitate needs to be updated accordingly
        factor = np.zeros(self.porosity_old.size)
        factor[dof] = (self.porosity_old[dof] + self.aperture_old[dof]) / (porosity_star[dof] + aperture_star[dof])
        precipitate_half = self.precipitate_old * factor

        # POINT 8) solve the reaction part
        solute_star_star, precipitate_star_star = self.compute_solute_precipitate_rection(solute_half, precipitate_half)

        # -- DO THE POROSITY PART -- #

        # POINT 9) solve the porosity and aperture part with the true concentration of precipitate
        self.porosity = self.discr_porosity.step(self.porosity, precipitate_star_star, self.precipitate_old)
        self.aperture = self.discr_aperture.step(self.aperture, precipitate_star_star, self.precipitate_old)

        # POINT 10) finally, we correct the concentrations to account for the difference between the extrapolated
        # and "true" new porosity to ensure mass conservation
        factor = np.zeros(self.porosity_old.size)
        factor[dof] = (porosity_star[dof] + aperture_star[dof]) / (self.porosity[dof] + self.aperture[dof])
        self.solute = solute_star_star * factor
        self.precipitate = precipitate_star_star * factor

        # set the old variables
        self.set_old_variables()

        # compute composite variables
        factor = self.porosity + self.aperture
        self.porosity_aperture_times_solute = factor * self.solute
        self.porosity_aperture_times_precipitate = factor * self.precipitate

        # extract all the variables, useful for exporting
        self.extract()
nrow = 20
dz = 10
file_like = StringIO(
    """
shape: [{nrow},]
spacing: [{dz},]
kappa: {kappa}
k: {k}
Qm: {Qm}
""".format(
        kappa=kappa, k=k, Qm=Qm, nrow=nrow, dz=dz,
    )
)

# Create an instance of the Heat model, initialize it from the file.
h = Heat()
h.initialize(file_like)

# set the initial temperature based on our linear fit.
model_z = np.arange(0, nrow * dz, dz)
T_init = fit.intercept_[0] + fit.coef_[0][0] * model_z
h.set_value("temperature", T_init)

# override the default timestep to use 1 day.
h.timestep = seconds_per_day

# run the model forward in time forced by the surface temperature.
while h.get_current_time() < duration_years * seconds_per_year:
    # calculate the time to run until.
    run_until = min([h.get_current_time() + seconds_per_year,
                     duration_years*seconds_per_year])
class Scheme(object):

    # ------------------------------------------------------------------------------#

    def __init__(self, gb):
        self.gb = gb

        # -- flow -- #
        self.discr_flow = Flow(gb)

        shape = self.discr_flow.shape()
        self.flux_pressure = np.zeros(shape)

        # -- temperature -- #
        self.discr_temperature = Heat(gb)

        # -- solute and precipitate -- #
        self.discr_solute_advection_diffusion = Transport(gb)
        self.discr_solute_precipitate_reaction = Reaction(gb)

        # -- porosity -- #
        self.discr_porosity = Porosity(gb)

        # -- fracture aperture -- #
        self.discr_fracture_aperture = FractureAperture(
            gb, "fracture_aperture")

        # -- layer porosity and aperture -- #
        self.discr_layer_porosity = Porosity(gb, "layer_porosity")
        self.discr_layer_aperture = LayerAperture(gb, "layer_aperture")

        # the actual time of the simulation
        self.time = 0

    # ------------------------------------------------------------------------------#

    def compute_precipitate_temperature_star(self):
        for g, d in self.gb:
            d[pp.STATE]["precipitate_star"] = 2 * d[
                pp.STATE]["precipitate"] - d[pp.STATE]["precipitate_old"]
            d[pp.STATE]["temperature_star"] = 2 * d[
                pp.STATE]["temperature"] - d[pp.STATE]["temperature_old"]

    # ------------------------------------------------------------------------------#

    def compute_porosity_aperture_star(self):
        dim_max = self.gb.dim_max()
        # only the rock matrix has the variable porosity
        for g in self.gb.grids_of_dimension(dim_max):
            d = self.gb.node_props(g)
            d[pp.STATE]["porosity_star"] = self.discr_porosity.step(
                d[pp.STATE]["porosity_old"], d[pp.STATE]["precipitate_star"],
                d[pp.STATE]["precipitate_old"])

        # only the fracture and the layer have the variable aperture
        for g in self.gb.grids_of_dimension(dim_max - 1):
            d = self.gb.node_props(g)
            if "fracture" in g.name:
                d[pp.STATE][
                    "fracture_aperture_star"] = self.discr_fracture_aperture.step(
                        d[pp.STATE]["fracture_aperture_old"],
                        d[pp.STATE]["precipitate_star"],
                        d[pp.STATE]["precipitate_old"])
            if "layer" in g.name:
                d[pp.STATE]["layer_aperture_star"] = d[
                    pp.STATE]["layer_aperture_old"]
                d[pp.STATE][
                    "layer_porosity_star"] = self.discr_layer_porosity.step(
                        d[pp.STATE]["layer_porosity_old"],
                        d[pp.STATE]["precipitate_star"],
                        d[pp.STATE]["precipitate_old"])

    # ------------------------------------------------------------------------------#

    def compute_flow(self):
        A, b = self.discr_flow.matrix_rhs()
        x = sps.linalg.spsolve(A, b)
        if not np.all(np.isfinite(x)):
            raise ValueError
        self.discr_flow.extract(x)

    # ------------------------------------------------------------------------------#

    def compute_temperature(self):
        A, b = self.discr_temperature.matrix_rhs()
        x = sps.linalg.spsolve(A, b)
        if not np.all(np.isfinite(x)):
            raise ValueError
        self.discr_temperature.extract(x, "temperature")

    # ------------------------------------------------------------------------------#

    def compute_solute_precipitate_advection_diffusion(self):
        A, b = self.discr_solute_advection_diffusion.matrix_rhs()
        x = sps.linalg.spsolve(A, b)
        if not np.all(np.isfinite(x)):
            raise ValueError
        self.discr_solute_advection_diffusion.extract(x, "solute_half")

    # ------------------------------------------------------------------------------#

    def correct_precipitate_with_star(self):
        for g, d in self.gb:
            vol_star = d[pp.STATE]["porosity_star"] + d[pp.STATE]["fracture_aperture_star"] +\
                       d[pp.STATE]["layer_porosity_star"]

            vol_old = d[pp.STATE]["porosity_old"] + d[pp.STATE]["fracture_aperture_old"] + \
                      d[pp.STATE]["layer_porosity_old"]

            d[pp.STATE]["precipitate_half"] = d[
                pp.STATE]["precipitate_old"] * (vol_old / vol_star)

    # ------------------------------------------------------------------------------#

    def compute_solute_precipitate_rection(self):
        for g, d in self.gb:
            d[pp.STATE]["solute_star_star"], d[pp.STATE]["precipitate_star_star"] = \
                self.discr_solute_precipitate_reaction.step(d[pp.STATE]["solute_half"],
                                                            d[pp.STATE]["precipitate_half"],
                                                            d[pp.STATE]["temperature"])

    # ------------------------------------------------------------------------------#

    def compute_porosity_aperture(self):
        dim_max = self.gb.dim_max()
        # only the rock matrix has the variable porosity
        for g in self.gb.grids_of_dimension(dim_max):
            d = self.gb.node_props(g)
            d[pp.STATE]["porosity"] = self.discr_porosity.step(
                d[pp.STATE]["porosity"], d[pp.STATE]["precipitate_star_star"],
                d[pp.STATE]["precipitate_old"])

        # only the fracture and the layer have the variable aperture
        for g in self.gb.grids_of_dimension(dim_max - 1):
            d = self.gb.node_props(g)
            if "fracture" in g.name:
                d[pp.STATE][
                    "fracture_aperture"] = self.discr_fracture_aperture.step(
                        d[pp.STATE]["fracture_aperture"],
                        d[pp.STATE]["precipitate_star_star"],
                        d[pp.STATE]["precipitate_old"])

            if "layer" in g.name:
                flux_fracture = np.zeros(g.num_cells)
                solute_fracture = np.zeros(g.num_cells)
                porosity = np.zeros(g.num_cells)
                for e, d_e in self.gb.edges_of_node(g):
                    g_m = d_e["mortar_grid"]
                    g_l, g_h = self.gb.nodes_of_edge(e)
                    if g_h.dim < self.gb.dim_max():
                        # save the flux from the fracture
                        flux_fracture = -g_m.master_to_mortar_avg().T * d_e[
                            pp.STATE][self.discr_flow.mortar] / g.cell_volumes
                        d_fracture = self.gb.node_props(g_l if "fracture" in
                                                        g_l.name else g_h)
                        solute_fracture = g_m.slave_to_mortar_avg(
                        ) * d_fracture[pp.STATE]["solute"]
                    else:
                        cell_cell = g_m.mortar_to_master_int(
                        ).T * g_h.cell_faces
                        # the cells are already ordered according to the layer ordering of the cells given by the mortar
                        # map
                        interface_cells = cell_cell.indices
                        d_h = self.gb.node_props(g_h)

                        porosity = d_h[pp.STATE]["porosity"][interface_cells]
                        temperature = d_h[
                            pp.STATE]["temperature"][interface_cells]
                        lmbda = self.discr_solute_precipitate_reaction.data[
                            "lambda"](temperature)

                d[pp.STATE]["layer_aperture"] = self.discr_layer_aperture.step(
                    flux_fracture, porosity, lmbda, self.time, solute_fracture)

                d[pp.STATE]["layer_porosity"] = self.discr_layer_porosity.step(
                    d[pp.STATE]["layer_porosity"],
                    d[pp.STATE]["precipitate_star_star"],
                    d[pp.STATE]["precipitate_old"])

    # ------------------------------------------------------------------------------#

    def correct_precipitate_solute(self):
        for g, d in self.gb:
            vol_star = d[pp.STATE]["porosity_star"] + d[pp.STATE]["fracture_aperture_star"] +\
                       d[pp.STATE]["layer_porosity_star"]

            vol = d[pp.STATE]["porosity"] + d[pp.STATE]["fracture_aperture"] + \
                  d[pp.STATE]["layer_porosity"]

            d[pp.STATE]["solute"] = d[pp.STATE]["solute_star_star"] * (
                vol_star / vol)
            d[pp.STATE]["precipitate"] = d[
                pp.STATE]["precipitate_star_star"] * (vol_star / vol)

    # ------------------------------------------------------------------------------#

    def set_old_variables(self):
        for g, d in self.gb:
            d[pp.STATE]["temperature_old"] = d[pp.STATE]["temperature"].copy()
            d[pp.STATE]["solute_old"] = d[pp.STATE]["solute"].copy()
            d[pp.STATE]["precipitate_old"] = d[pp.STATE]["precipitate"].copy()
            d[pp.STATE]["porosity_old"] = d[pp.STATE]["porosity"].copy()
            d[pp.STATE]["fracture_aperture_old"] = d[
                pp.STATE]["fracture_aperture"].copy()
            d[pp.STATE]["layer_aperture_old"] = d[
                pp.STATE]["layer_aperture"].copy()
            d[pp.STATE]["layer_porosity_old"] = d[
                pp.STATE]["layer_porosity"].copy()

    # ------------------------------------------------------------------------------#

    def set_data(self, param):

        for g, d in self.gb:
            data = param["temperature"]["initial"]
            d[pp.STATE]["temperature"] = data(g, param, param["tol"])
            d[pp.STATE]["temperature_star"] = d[pp.STATE]["temperature"].copy()

            data = param["solute_advection_diffusion"]["initial_solute"]
            d[pp.STATE]["solute"] = data(g, param, param["tol"])

            data = param["solute_advection_diffusion"]["initial_precipitate"]
            d[pp.STATE]["precipitate"] = data(g, param, param["tol"])
            d[pp.STATE]["precipitate_star"] = d[pp.STATE]["precipitate"].copy()

            data = param["porosity"]["initial"]
            d[pp.STATE]["porosity_initial"] = data(g, param, param["tol"])
            d[pp.STATE]["porosity_star"] = d[
                pp.STATE]["porosity_initial"].copy()
            d[pp.STATE]["porosity"] = d[pp.STATE]["porosity_initial"].copy()

            data = param["fracture_aperture"]["initial"]
            d[pp.STATE]["fracture_aperture_initial"] = data(
                g, param, param["tol"])
            d[pp.STATE]["fracture_aperture_star"] = d[
                pp.STATE]["fracture_aperture_initial"].copy()
            d[pp.STATE]["fracture_aperture"] = d[
                pp.STATE]["fracture_aperture_initial"].copy()

            data = param["layer_aperture"]["initial"]
            d[pp.STATE]["layer_aperture_initial"] = data(
                g, param, param["tol"])
            d[pp.STATE]["layer_aperture_star"] = d[
                pp.STATE]["layer_aperture_initial"].copy()
            d[pp.STATE]["layer_aperture"] = d[
                pp.STATE]["layer_aperture_initial"].copy()

            data = param["layer_porosity"]["initial"]
            d[pp.STATE]["layer_porosity_initial"] = data(
                g, param, param["tol"])
            d[pp.STATE]["layer_porosity_star"] = d[
                pp.STATE]["layer_porosity_initial"].copy()
            d[pp.STATE]["layer_porosity"] = d[
                pp.STATE]["layer_porosity_initial"].copy()

            vol = d[pp.STATE]["porosity"] + d[pp.STATE]["fracture_aperture"] +\
                  d[pp.STATE]["layer_aperture"] * d[pp.STATE]["layer_porosity"]
            d[pp.STATE]["porosity_aperture_times_solute"] = vol * d[
                pp.STATE]["solute"]
            d[pp.STATE]["porosity_aperture_times_precipitate"] = vol * d[
                pp.STATE]["precipitate"]

            d[pp.STATE]["pressure"] = np.zeros(g.num_cells)
            d[pp.STATE]["P0_darcy_flux"] = np.zeros((3, g.num_cells))

        for e, d in self.gb.edges():
            d[pp.STATE][self.discr_flow.mortar] = np.zeros(
                d["mortar_grid"].num_cells)

        # set the old variables
        self.set_old_variables()

        # extract the initialized variables, useful for setting the data
        ###self.extract()

        # set now the data for each scheme
        self.discr_flow.set_data(param["flow"], param["time"])
        self.discr_temperature.set_data(param["temperature"], param["time"])
        self.discr_solute_advection_diffusion.set_data(
            param["solute_advection_diffusion"], param["time"])
        self.discr_solute_precipitate_reaction.set_data(
            param["solute_precipitate_reaction"], param["time"])
        self.discr_porosity.set_data(param["porosity"])
        self.discr_fracture_aperture.set_data(param["fracture_aperture"])
        self.discr_layer_aperture.set_data(param["layer_aperture"],
                                           param["time"])
        self.discr_layer_porosity.set_data(param["layer_porosity"])

    # ------------------------------------------------------------------------------#

    def compute_composite_variables(self):

        for g, d in self.gb:
            vol = d[pp.STATE]["porosity"] + d[pp.STATE]["fracture_aperture"] +\
                  d[pp.STATE]["layer_aperture"] * d[pp.STATE]["layer_porosity"]

            d[pp.STATE]["porosity_aperture_times_solute"] = vol * d[
                pp.STATE]["solute"]
            d[pp.STATE]["porosity_aperture_times_precipitate"] = vol * d[
                pp.STATE]["precipitate"]

    # ------------------------------------------------------------------------------#

    def vars_to_save(self):
        name = [
            "solute", "precipitate", "porosity", "fracture_aperture",
            "layer_aperture", "layer_porosity", "temperature"
        ]
        name += [
            "porosity_aperture_times_solute",
            "porosity_aperture_times_precipitate", "fracture", "layer"
        ]
        return name + [self.discr_flow.pressure, self.discr_flow.P0_flux]

    # ------------------------------------------------------------------------------#

    def one_step_splitting_scheme(self, time):

        # save the time of the current step
        self.time = time

        # POINT 1) extrapolate the precipitate to get a better estimate of porosity
        self.compute_precipitate_temperature_star()

        # POINT 2) compute the porosity and aperture star for both the fracture and layer
        self.compute_porosity_aperture_star()

        # -- DO THE FLOW PART -- #

        # POINT 3) update the data from the previous time step
        self.discr_flow.update_data()

        # POINT 4) solve the flow part
        self.compute_flow()

        # -- DO THE HEAT PART -- #

        # set the flux and update the data from the previous time step
        self.discr_temperature.set_flux(self.discr_flow.flux,
                                        self.discr_flow.mortar)
        self.discr_temperature.update_data()

        # POINT 5) solve the temperature part
        self.compute_temperature()

        # -- DO THE TRANSPORT PART -- #

        # set the flux and update the data from the previous time step
        self.discr_solute_advection_diffusion.set_flux(self.discr_flow.flux,
                                                       self.discr_flow.mortar)
        self.discr_solute_advection_diffusion.update_data()

        # POINT 6) solve the advection and diffusion part to get the intermediate solute solution
        self.compute_solute_precipitate_advection_diffusion()

        # POINT 7) Since in the advection-diffusion step we have accounted for porosity changes using
        # phi_star and aperture_star, the new solute concentration accounts for the change in pore volume, thus, the
        # precipitate needs to be updated accordingly
        self.correct_precipitate_with_star()

        # POINT 8) solve the reaction part
        self.compute_solute_precipitate_rection()

        # -- DO THE POROSITY PART -- #

        # POINT 9) solve the porosity and aperture (fracture and layer) part with the true concentration of precipitate
        self.compute_porosity_aperture()

        # POINT 10) finally, we correct the concentrations to account for the difference between the extrapolated
        # and "true" new porosity to ensure mass conservation
        self.correct_precipitate_solute()

        # set the old variables
        self.set_old_variables()

        # compute composite variables
        self.compute_composite_variables()
# simple python code to run the heat module under local bmi-python

from __future__ import absolute_import, division, print_function
import numpy as np
import yaml
import matplotlib.pyplot as plt

from heat import Heat
from bmi_heat import BmiHeat

if __name__ == '__main__':
    # Verify that this code is running
    print("Running runheat.py")

    # Create a bmi-heat object
    bheat = Heat()

    """
    # Write out the temperature array, so I can re-read it later
    bheat._temperature.tofile("bheat_inittemp.dat")
    """

    # Read in the temperature array, so it's consistent
    bheat._temperature = np.fromfile("bheat_inittemp.dat").reshape(10,20)

    # Write out a quick 10x20 floating point array
    bheat._temperature.astype('float32').tofile("tempcheck.dat")

    # Create an array with the output of the heat run
    harr = np.zeros((100, 10, 20))
    harr[0, :, :] = bheat._temperature