Achieving cyclic steady state in CADET

Hi there,

I am new to CADET and am trying to model a simulated moving bed setup. I know there is CADET-SMB however I saw that it was recommended to use the CADET engine for most applications.

My question is, is there a way to setup the simulation to run until cyclic steady state is achieved? Right now it seems like I have to specify how many times I want my ports to switch positions in my smb network during the length of the simulation, and specify the time intervals that this happens. The code below is what I have right now, which switches the port positions 8 times, corresponding to two complete cycles (desorbent → extract → feed → raffinate).

model.root.input.solver.sections.nsec = 8
model.root.input.solver.sections.section_times = [0.0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000]   # s
model.root.input.solver.sections.section_continuity = [0]

However when I am modelling new adsorbents, I will not know how many switches it will take to achieve steady state, and the time between each switch is an operating condition that we would eventually like to optimize. So is there a different way to set up the time switches with CADET?

Any help would be much appreciated! Thank you!

Hi there and welcome to the forum! :slight_smile:

You are right, currently, there is no way to define this directly within CADET. However, this is possible in CADET-Process, which is still under active development. But if you want, i can give you an introduction.

Alternatively, you can just simulate one cycle at a time and always compare the last cycle with the previous cycle to check if you have already reached stationarity. If not, you use the final state of the system as initial state for the next cycle.



Thanks for the suggestions Johannes. I am now trying to use the final state of each column as the initial state for the next cycle but am running into some trouble.

From looking at the CADET documentation, I assumed the way to do this was to enable write_solution_last to get the full state vector, and then use that as the input for init_state in the model? Is this correct? However when I try to output last_state it does not return anything ({ }), same for last_state_ydot. Is there another step I need to do? (I’ve tried to run it both in Jupyter and directly from the command line but still get no output).

I originally tried to use the final concentration profile of each component (i.e. an array of concentration at each axial discretization at the final time) as the inputs for init_c and init_q, however it seems that only the first two terms of the first component array were used to start the simulation. Am I right to assume that init_c and init_q only accepts one value for each component and applies that across the whole column, rather than being able to accept an array?

Any suggestions on how to proceed would be much appreciated! Thank you again!

you’re on the right track! :slight_smile:

First of all, yes, INIT_C is just a vector that is applied to all points in space at t = 0. You can set INIT_STATE for each unit separately (see here). However, currently it is not possible to save the state of each unit individually after the simulation (see here) so that’s probably not very practical for you. It should be an easy fix in C++, though, if you want to contribute to the project! :wink:

Hence, your approach to use the final state and its derivative of the entire system is preferred (and it’s also much simpler). Generally I don’t see where things went wrong. Probably it’s just a dictionary indexing error. As you said, first you set

model.root.input['return'].WRITE_SOLUTION_LAST = True

Then, after the every cycle, you save the state

state_y = model.root.output.last_state_y
state_ydot = model.root.output.last_state_ydot

and for the next cycle, you set:

model.root.input.model.INIT_STATE_Y = state_y
model.root.input.model.INIT_STATE_YDOT = state_ydot

Hope this helps. If not, feel free to upload the script or the h5 file and we can have a look.

Edit: Clarified the bit about the INIT_STATE of unit operations

Edit2: This post contains some mistake. Please see below for why. In summary, use the following (lower case letters):

model.root.input.model.init_state_y = state_y
model.root.input.model.init_state_ydot = state_ydot

Hi Johannes,

Many thanks for the quick response! My code seems to run fine now!

Originally I was trying to call “model.root.output.solution.last_state”, but it now works when i type it the way you did.

Just to point out that on your output group page (Output Group — CADET) the variable is written as last_state not last_state_y, and seems to suggest that you need to type “model.root.output.solution” instead of “model.root.output”, so maybe that should be updated so that others don’t run into the same issue :slight_smile:

Many thanks again!

Best regards,

1 Like

Awesome, happy to hear that! :slight_smile:

Good catch, I fixed it in the documentation! :slight_smile:


Sorry to bother again but another issue has come up. I’m currently trying to put my code in a while loop to automate running the simulation until steady state is reached (i.e. when the sum of differences between final and initial state is less than 1e-3 for example), rather than having to run one cycle at a time. However the code is getting stuck in the 3rd loop, and the error seems to be related to the model.h5 file.

This is the code section where the error comes up:

This is the output I’m able to get:

And this is the error message that comes up:

I’ve tried implementing the code os.remove() to try and delete the file after the simulation has finished running each loop, but that has not made a difference (I have checked for one loop that the file actually does get removed).

Any help or advice would be much appreciated! Thank you!

Best regards,

Hey Ashlyn,

no worries, this is exactly what this forum is for! :slight_smile:

To be honest, it’s a little bit hard for me to debug without more information. But my initial guess is that there is a dictionary collision.

In CADET-Python, we use a special kind of dictionary called Dict() from the addict package. It allows the convenient dot notation (i.e.… instead of cadet['root']['root']['foo'][...] and it’s a defaultdict() that always returns another empty Dict if a key does not exist yet. Otherwise it’s just a regular Python dict().

When you call, this structure is converted to HDF5 (or H5), a binary Hierarchical Data Format which is great for large datasets. The important thing to note here is that keys in dictionaries are case sensitive, so there is a difference between and cadet.root.Foo. But when saving to HDF5, it all converts to the correct CADET notation. If you now have two entries in a dictionary with different cases, then it cannot save the latter one and it will raise an error.

So please check that when loading and saving the state you don’t have any duplicate entries.

This is also something that maybe we could improve in CADET-Python!? @w.heymann, could we raise a warning for duplicate entries, or by default convert any capital case to lower case or the other way round?

If this does not solve your problem, it would help if you could send us your H5 file.



Hi Johannes,

Thanks for this.

So we seem to have found the problem. My code before was like this, with init_state_y in all caps:

When we make it all lower caps like this the simulation is able to loop until it meets the condition:

I don’t understand why this solved the problem though? I only called the init_state_y variable at this instance and it wasn’t repeated any where else with lowercase, it was only ever defined as uppercase to start.

But I’m just happy the code runs now haha :smiley:

Best regards,

Happy that it works now! :partying_face:

I can only guess here but I assume something like the following:

  • 1st run, there is no initial state key
  • 2nd run, you load from file and assign old state as INIT_STATE in capital letters
  • 3rd run, you load from file (old INIT_STATE is now lower case because it was modified when saving) and you assign the new old state as INIT_STATE in capital letters. => Conflict!

Or maybe something else… :sweat_smile:

As I said, we should also look into this to avoid such issues because it pops up from time to time; especially in these more advanced cases where reinitialization is required.