cadet / CADET-Core

A modular, fast, and accurate simulation framework for (bio)chemical processes
Other
53 stars 26 forks source link

2D GRM needs at least one particle type #233

Open jbreue16 opened 2 months ago

jbreue16 commented 2 months ago

Using the FV 2DGRM, and specifying NPARTYPE=0, we are forced to specify the field PAR_TYPE_VOLFRAC. Specifying this as PAR_TYPE_VOLFRAC = [], we get an error (invalid vector subscript) and specifying this with some value also gives an error since the length of this vector has to match NPARTYPE times spatial dependencies. Looking into the code, this seems to be caused by how the parameters are registered and it seems like we need to catch some of these with if(!_disc.nPartype) conditions.

This should also be checked for the 1D GRM and LRMP. Note the model can still be used effectively without particles by setting NPARTYPE=1 and film diffusion to zero.

jbreue16 commented 2 months ago

The same probably happens for the 1D GRM and LRMP, however thats more of a modelling issue since using these models without particles reduces to the LRM. With the same argument, this wouldnt be an issue anymore, if we had a 2DLRM, which is actually work in progress, at least as a DG unit. The issue remains in my opinion as long as we cannot model 2D flow without particles.

schmoelder commented 2 months ago

Good catch!

This could also be a chance to reconsider ways to improve the interface for column-type unit operations. In my opinion, it would make sense to introduce an additional layer for particle related parameters (i.e. geometric parameters, discretization, and binding model) instead of having to define them globally on unit operation level.

In the current version, I feel we're not properly separating responsibilities which in my experience can be tricky to maintain.

E.g. currently, we have something like the following (for a LRMP):

Details

``` # Column model.root.input.model.model_001.model_type = 'LUMPED_RATE_MODEL_WITH_PORES' model.root.input.model.model_001.ncomp = n_comp ## Discretization model.root.input.model.model_001.discretization.ncol = 100 model.root.input.model.model_001.discretization.npar = 5 model.root.input.model.model_001.discretization.npartype = 1 model.root.input.model.model_001.discretization.par_disc_type = 'EQUIDISTANT_PAR' model.root.input.model.model_001.discretization.use_analytic_jacobian = 1 model.root.input.model.model_001.discretization.reconstruction = 'WENO' model.root.input.model.model_001.discretization.gs_type = 1 model.root.input.model.model_001.discretization.max_krylov = 0 model.root.input.model.model_001.discretization.max_restarts = 10 model.root.input.model.model_001.discretization.schur_safety = 1.0e-8 model.root.input.model.model_001.discretization.weno.boundary_model = 0 model.root.input.model.model_001.discretization.weno.weno_eps = 1e-10 model.root.input.model.model_001.discretization.weno.weno_order = 3 ## Unit Operation Parameters model.root.input.model.model_001.col_length = 0.6 model.root.input.model.model_001.cross_section_area = 1.0386890710931253E-4 model.root.input.model.model_001.col_porosity = 0.37 model.root.input.model.model_001.par_porosity = 0.33 model.root.input.model.model_001.par_radius = 4.5e-5 model.root.input.model.model_001.col_dispersion = 2.0e-7 model.root.input.model.model_001.film_diffusion = [1e-4, 1e-4] ## Adsorption Model model.root.input.model.model_001.adsorption_model = 'MULTI_COMPONENT_LANGMUIR' model.root.input.model.model_001.adsorption.is_kinetic = True model.root.input.model.model_001.adsorption.mcl_ka = [2, 3] model.root.input.model.model_001.adsorption.mcl_kd = [1, 1] model.root.input.model.model_001.adsorption.mcl_qmax = [1, 1] model.root.input.model.model_001.init_c = n_comp*[0.0,] model.root.input.model.model_001.init_q = n_comp*[0.0,] ```

Instead, I propose something like the following:

Details

``` # Where to put film diffusion is still not 100 % clear to me. # Option 1: Leave it on Unit Operation Level s.t. entries are `NCOMP` * `NPARTYPE` # Option 2: Also move it to particle level since it cannot exist without the particle. (preferred option) model.root.input.model.model_001.model_type = 'LUMPED_RATE_MODEL_WITH_PORES' model.root.input.model.model_001.ncomp = n_comp ## Unit Operation Parameters model.root.input.model.model_001.col_length = 0.6 model.root.input.model.model_001.cross_section_area = 1.0386890710931253E-4 model.root.input.model.model_001.col_porosity = 0.37 model.root.input.model.model_001.col_dispersion = 2.0e-7 ## Unit Operation Discretization model.root.input.model.model_001.discretization.ncol = 100 model.root.input.model.model_001.discretization.npartype = 1 model.root.input.model.model_001.discretization.par_type_volfrac = [1] model.root.input.model.model_001.discretization.use_analytic_jacobian = 1 model.root.input.model.model_001.discretization.reconstruction = 'WENO' model.root.input.model.model_001.discretization.gs_type = 1 model.root.input.model.model_001.discretization.max_krylov = 0 model.root.input.model.model_001.discretization.max_restarts = 10 model.root.input.model.model_001.discretization.schur_safety = 1.0e-8 model.root.input.model.model_001.discretization.weno.boundary_model = 0 model.root.input.model.model_001.discretization.weno.weno_eps = 1e-10 model.root.input.model.model_001.discretization.weno.weno_order = 3 ## Unit Operation Initial conditions model.root.input.model.model_001.init_c = n_comp*[0.0,] ## Particle Parameters model.root.input.model.model_001.particle_000.par_porosity = 0.33 model.root.input.model.model_001.particle_000.par_radius = 4.5e-5 model.root.input.model.model_001.particle_000.film_diffusion = [1e-4, 1e-4] model.root.input.model.model_001.particle_000.adsorption_model = 'MULTI_COMPONENT_LANGMUIR' model.root.input.model.model_001.particle_000.adsorption.nbound = [1, 1] model.root.input.model.model_001.particle_000.adsorption.is_kinetic = True model.root.input.model.model_001.particle_000.adsorption.mcl_ka = [2, 3] model.root.input.model.model_001.particle_000.adsorption.mcl_kd = [1, 1] model.root.input.model.model_001.particle_000.adsorption.mcl_qmax = [1, 1] ## Particle Discretization model.root.input.model.model_001.particle_000.discretization.npar = 5 model.root.input.model.model_001.particle_000.discretization.par_disc_type = 'EQUIDISTANT_PAR' ## Particle initial Conditions model.root.input.model.model_001.particle_000.init_q = n_comp*[0.0,] ```

This version of the interface is much more modular. It allows for an independent specification of geometric parameters, discretization parameters, and adsorption models for each particle type, while being explicit to which of the particles each parameter "belongs". It would also simplify adding/removing different particles when composing the unit operation model and seems easier to maintain / extend.

Not sure if this is the best place to discuss it but I wanted to get it out of my system. We could also add a separate issue for it. In any case, it's something we can only address once we have a proper interface specification in place (which we should really give more priority).

schmoelder commented 2 months ago

See also: https://github.com/cadet/CADET-Specification/issues/4