-
Notifications
You must be signed in to change notification settings - Fork 45
Strain-dependent active stress model #245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
KatrinKoesler
wants to merge
44
commits into
SimVascular:master
Choose a base branch
from
KatrinKoesler:strain_dependency
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
571180d
added test file
08886ec
remove test file
71d1e39
Added strain dependent active stress
KatrinKoesler 73f3ce3
Merge branch 'ventricle_gr_144' of github.com:KatrinKoesler/svZeroDSo…
KatrinKoesler 9aa5e15
Merge branch 'master' into ventricle_gr_144
KatrinKoesler 104243b
Merge branch 'SimVascular:master' into ventricle_gr_144
KatrinKoesler 47f3373
Merge branch 'SimVascular:master' into ventricle_gr_144
KatrinKoesler 2540640
Merge branch 'SimVascular:master' into ventricle_gr_144
KatrinKoesler bc6cbca
Added the ChamberSphere_StrainDepActStress files to the Model, BlockT…
KatrinKoesler 4552416
Merge branch 'SimVascular:master' into ventricle_gr_144
KatrinKoesler 2435840
Change material model to exponential, added test case for this model …
KatrinKoesler 308696d
Changed input file to match the parameters from Caruel 2013
KatrinKoesler c219c7b
Added the file name to CMakeLists.txt
KatrinKoesler b215953
Codeformat
KatrinKoesler 1bc55ea
Merge branch 'ventricle_gr_144' of github.com:KatrinKoesler/svZeroDSo…
KatrinKoesler 2752858
Codeformat
KatrinKoesler b703629
Added test case for closed loop cisrculation based on Sharifi 2024 an…
KatrinKoesler ace7ac7
Copied test case from closed loop model with exponential material model
KatrinKoesler 1438d15
Corrected viscosity term in .yaml file with strain dependent active s…
KatrinKoesler 0ed8ba2
Changes to be committed:
KatrinKoesler e933071
Added Caruel circulation test file
KatrinKoesler ba85cb7
Corrected strain dependent active stress equations and added test fil…
KatrinKoesler 83d21ef
Corrected absolute value of de_c_dt in active stress calculation
KatrinKoesler 33545ed
Merge remote-tracking branch 'origin/master' into ventricle_gr_144
KatrinKoesler 3b9defe
Changed timing of activation function in strain dependent active stre…
KatrinKoesler a7a595d
Merge branch 'SimVascular:master' into ventricle_gr_144
KatrinKoesler b2aef7e
Merge remote-tracking branch 'upstream/master' into ventricle_gr_144
KatrinKoesler be4969b
Merge branch 'ventricle_gr_144' of github.com:KatrinKoesler/svZeroDSo…
KatrinKoesler 74ab22f
codeformat
KatrinKoesler c4e6f28
exclude test cases from dirgraph test
KatrinKoesler f58a14c
documentation fix
KatrinKoesler c40b73e
Merge branch 'master' into strain_dependency
KatrinKoesler e08803e
Added ActiveStress files and renamed strain-dependent active stress i…
KatrinKoesler 61d80cb
Merge branch 'master' into strain_dependency
KatrinKoesler 70a34d7
Active stress is now calculated inside ActiveStress from activation f…
KatrinKoesler ca2f43d
Active Stress generation for strain-dependent and strain-independent…
KatrinKoesler 7a47f70
Adjusted pre-existing chamber sphere test cases for new active stress…
KatrinKoesler b1fe63a
Codeformat
KatrinKoesler f97b987
Documentation corrections, comment corrections, added error message i…
KatrinKoesler 546eab9
Merge branch 'SimVascular:master' into strain_dependency
KatrinKoesler 4e1b59b
Added linear interpolation to be able to set piecewise-linear time/st…
KatrinKoesler 64bd651
Added break point vectors to the input file for strain-dependent acti…
KatrinKoesler 0abe79a
excluded strain-dependent test from dirgraph test
KatrinKoesler 6eab1d8
add caruel13 citation
KatrinKoesler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| variables: | ||
| - Pin | ||
| - Qin | ||
| - Pout | ||
| - Qout | ||
| - radius | ||
| - velo | ||
| - stress | ||
| - tau | ||
| - volume | ||
| - e_c | ||
| - tau_c | ||
| - k_c | ||
| - omega | ||
|
|
||
| derivatives: | ||
| - dPin_dt | ||
| - dQin_dt | ||
| - dPout_dt | ||
| - dQout_dt | ||
| - dradius_dt | ||
| - dvelo_dt | ||
| - dstress_dt | ||
| - dtau_dt | ||
| - dvolume_dt | ||
| - de_c_dt | ||
| - dtau_c_dt | ||
| - dk_c_dt | ||
| - domega_dt | ||
|
|
||
| constants: | ||
| - rho | ||
| - thick0 | ||
| - radius0 | ||
| - C0 | ||
| - C1 | ||
| - C2 | ||
| - C3 | ||
| - eta | ||
| - E_s | ||
| - mu | ||
| - alpha_r | ||
| - alpha | ||
| - k_0 | ||
| - sigma_0 | ||
| - m_0 | ||
| - n_0 | ||
| - u_plus | ||
| - u_minus | ||
|
|
||
| time_dependent: | ||
| - u_plus | ||
| - u_minus | ||
|
|
||
| strain_dependent: | ||
| - m_0 | ||
| - n_0 | ||
|
|
||
| helper_functions: | | ||
| def CG(radius): | ||
| return (1 + (radius / radius0)) ** 2 | ||
|
|
||
| def dCG(radius, dradius_dt): | ||
| return 2 * (1 + (radius / radius0)) * (1 / radius0) * dradius_dt | ||
|
|
||
| def dW1(radius): | ||
| return C0 * exp(C1 * (((1 + (radius / radius0))**(-4) + 2*(1 + (radius / radius0))**2) - 3)**2) * 2 * C1 * (((1 + (radius / radius0))**(-4) + 2*(1 + (radius / radius0))**2) - 3) | ||
|
|
||
| def dW4(radius): | ||
| return C2 * exp(C3 * ((((1 + (radius / radius0)) ** 2)-1)**2)) * 2 * C3 * (((1 + (radius / radius0)) ** 2) - 1) | ||
|
|
||
| residuals: | ||
| - rho * thick0 * dvelo_dt + (thick0 / radius0) * (1 + (radius / radius0)) * stress - Pout * CG(radius) | ||
| - -stress + 4 * dW1(radius) *(1 - CG(radius)**(-3)) + 2 * dW4(radius) + eta * dCG(radius,dradius_dt) * (1 + 2 * (CG(radius)**(-6))) + tau | ||
| - 4 * pi * radius0 ** 2 * CG(radius) * velo - dvolume_dt | ||
| - dradius_dt - velo | ||
| - Qin - Qout - dvolume_dt | ||
| - Pin - Pout | ||
| - -tau + E_s * (radius/radius0 +0.5 * (radius / radius0) ** 2 - e_c) / ((1 + 2 * e_c) ** 2) | ||
| - -alpha_r * domega_dt + m_0 - omega | ||
| - -tau_c - mu * de_c_dt + E_s * ((radius/radius0 +0.5 * (radius / radius0) ** 2 - e_c) * (1 + 2 * radius/radius0 + (radius/ radius0) ** 2)) / ((1 + 2 * e_c) ** 3) | ||
| - -dk_c_dt - (u_plus + omega * abs(u_minus) + alpha * abs(de_c_dt)) * k_c + n_0 * k_0 * u_plus | ||
| - -dtau_c_dt - (u_plus + omega * abs(u_minus) + alpha * abs(de_c_dt)) * tau_c + n_0 * sigma_0 * u_plus + k_c * de_c_dt |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| from sympy import symbols, Matrix, simplify, pi, Abs, exp | ||
| from sympy.printing import ccode | ||
| import re | ||
| import pdb | ||
| import yaml | ||
| import argparse | ||
|
|
||
| def load_model(filepath): | ||
| with open(filepath, 'r') as file: | ||
| data = yaml.safe_load(file) | ||
|
|
||
| variables = Matrix(symbols(' '.join(data['variables']), real=True)) | ||
| derivatives = Matrix(symbols(' '.join(data['derivatives']), real=True)) | ||
| constants = symbols(' '.join(data['constants']), real=True) | ||
|
|
||
| context = {str(s): s for s in list(variables) + list(derivatives) + list(constants)} | ||
| context['pi'] = pi | ||
| context['abs'] = Abs | ||
| context['exp'] = exp | ||
|
|
||
| if 'helper_functions' in data: | ||
| exec(data['helper_functions'], context, context) | ||
| residual_exprs = [eval(res, context, context) for res in data['residuals']] | ||
|
|
||
| residuals = Matrix(residual_exprs) | ||
| time_dependent = {context[s] for s in data.get('time_dependent', [])} | ||
| strain_dependent = {context[s] for s in data.get('strain_dependent', [])} | ||
|
|
||
| assert len(variables) == len(derivatives), f"Number of variables must be equal to number of derivatives" | ||
| assert len(variables) - 2 == len(residuals), f"Number of residuals must be number of unknowns minus 2" | ||
|
|
||
| return variables, derivatives, constants, residuals, time_dependent, strain_dependent | ||
|
|
||
| def extract_linear(residuals, y, dy): | ||
| def is_constant_coeff(coeff): | ||
| return (coeff and coeff.free_symbols.isdisjoint(y.free_symbols | dy.free_symbols)) | ||
| nr = residuals.shape[0] | ||
| ny = y.shape[0] | ||
| E = Matrix.zeros(nr, ny) | ||
| F = Matrix.zeros(nr, ny) | ||
| for i in range(nr): | ||
| for j in range(ny): | ||
| for mat, dd in zip([E, F], [dy, y]): | ||
| coeff = residuals[i].coeff(dd[j]) | ||
| if is_constant_coeff(coeff): | ||
| mat[i, j] = coeff | ||
| return E, F | ||
|
|
||
| def extract_nonlinear(residuals, E, F, y, dy): | ||
| C = simplify(residuals - E * dy - F * y) | ||
| dC_dy = simplify(C.jacobian(y)) | ||
| dC_dydot = simplify(C.jacobian(dy)) | ||
| return C, dC_dy, dC_dydot | ||
|
|
||
| def depends_on(expr, symbols_set): | ||
| return any(sym in expr.free_symbols for sym in symbols_set) | ||
|
|
||
| def partition_terms(E, F, C, dC_dy, dC_dydot, time_dependent_symbols, strain_dependent_symbols): | ||
| parts = {"constant": [], "time": [], "solution": [], "strain": []} | ||
| for i in range(E.shape[0]): | ||
| parts["solution"].append(("C", i, C[i])) | ||
| for j in range(E.shape[1]): | ||
| for label, mat in [("E", E), ("F", F), ("dC_dy", dC_dy), ("dC_dydot", dC_dydot)]: | ||
| if label.startswith('d'): | ||
| target = "solution" | ||
| elif depends_on(mat[i, j], time_dependent_symbols): | ||
| target = "time" | ||
| elif depends_on(mat[i, j], strain_dependent_symbols): | ||
| target = "strain" | ||
| else: | ||
| target = "constant" | ||
| parts[target].append((label, i, j, mat[i, j])) | ||
| return parts | ||
|
|
||
| def get_expressions(parts, type): | ||
| expressions = [part[-1] for part in parts] | ||
| deps = set().union(*(expr.free_symbols for expr in expressions)) | ||
| return deps.intersection(type) | ||
|
|
||
| def replace_symbolic_indices(expr, base): | ||
| pattern = re.compile(rf'\b{base}(\d+)\b') | ||
| return pattern.sub(rf'{base}[global_var_ids[\1]]', expr) | ||
|
|
||
| def format_cpp_expr(expr): | ||
| try: | ||
| cpp_expr = ccode(expr).replace('3.141592653589793', 'M_PI') | ||
| except: | ||
| pdb.set_trace() | ||
| cpp_expr = replace_symbolic_indices(cpp_expr, 'y') | ||
| cpp_expr = replace_symbolic_indices(cpp_expr, 'dy') | ||
| return cpp_expr | ||
|
|
||
| def print_index(i, j=None): | ||
| print(f".coeffRef(global_eqn_ids[{i}]", end='') | ||
| if j is not None: | ||
| print(f", global_var_ids[{j}]", end='') | ||
| print(f")", end='') | ||
|
|
||
| def print_system(parts): | ||
| for part in parts: | ||
| if part[-1] != 0: | ||
| print(f" system.{part[0]}", end='') | ||
| if len(part) == 3: | ||
| print_index(part[1]) | ||
| elif len(part) == 4: | ||
| print_index(part[1], part[2]) | ||
| print(f" = {format_cpp_expr(part[-1])};") | ||
|
|
||
| def print_constants(parts, constants, time_dependent, strain_dependent): | ||
| for out in get_expressions(parts, constants): | ||
| if out in time_dependent: | ||
| print(f" // compute time dependent constant {out}") | ||
| elif out in strain_dependent: | ||
| print(f" // compute strain dependent constant {out}") | ||
| else: | ||
| print(f" const double {out} = parameters[global_param_ids[ParamId::{out}]];") | ||
|
|
||
| def print_variables(parts, y, dy): | ||
| for out in get_expressions(parts, y.free_symbols | dy.free_symbols): | ||
| for name, vec in zip(['y', 'dy'], [y, dy]): | ||
| if out in vec: | ||
| index = next(i for i, sym in enumerate(vec) if sym == out) | ||
| print(f" const double {out} = {name}[global_var_ids[{index}]];") | ||
|
|
||
| def main(yaml_path): | ||
| # read model from yaml file | ||
| y, dy, constants, residuals, time_dependent, strain_dependent = load_model(yaml_path) | ||
|
|
||
| # extract linear and nonlinear terms | ||
| E, F = extract_linear(residuals, y, dy) | ||
| C, dC_dy, dC_dydot = extract_nonlinear(residuals, E, F, y, dy) | ||
|
|
||
| # split into constant, time dependent and solution parts | ||
| parts = partition_terms(E, F, C, dC_dy, dC_dydot, time_dependent, strain_dependent) | ||
|
|
||
| # print C++ code | ||
| for section in ['constant', 'time', 'strain', 'solution']: | ||
| print(f'update_{section}') | ||
| print_constants(parts[section], constants, time_dependent, strain_dependent) | ||
| print_variables(parts[section], y, dy) | ||
| print_system(parts[section]) | ||
| print() | ||
|
|
||
| if __name__ == "__main__": | ||
| parser = argparse.ArgumentParser(description='Process YAML model file to generate C++ code for svZeroDSolver') | ||
| parser.add_argument('yaml_file', help='Path to YAML model file') | ||
| args = parser.parse_args() | ||
| main(args.yaml_file) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This differs from other activation functions in that it returns values outside [0,1] and has a physical unit (1/s). Is there a way to consolidate these? Or should the rate activation have its own class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The activation function is a function summarizing the chemical activation of the cardiomyocytes and needs to be defined in this way so that the model works. I could make its own class because it doesn't really make sense to use it with the strain-independent active stress models, we currently have implemented
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if this activation function is parametrized, it could also be used to create activation functions that are piecewise linear and between [0,1]