Repository navigation
Conversation
…663-nodal-fibers # Conflicts: # Code/Source/solver/CMakeLists.txt # Code/Source/solver/post.cpp
| nfd >= 2, | ||
| "Applying active stress along normals (eta_n > 0) requires both a " | ||
| "fiber and a sheet direction, but only " + | ||
| nfd >= 3, |
There was a problem hiding this comment.
Non-blocking: if someone uses a non-cardiac model with active stress, e.g., CANN with fiber + sheet, then nfd == 2 in 3D, and sheet-normal active stress throws here, while main computes n = f × s / |f × s|. Fine to leave for now, but it may be worth a follow-up issue or a code comment noting that active stress combined with a non-cardiac material can hit this. The error message could also be clearer, e.g., tell users to supply a third Fiber_direction_*.
There was a problem hiding this comment.
If I remember correctly the discussion with @javijv4, we settled on requiring the user to explicitly provide all the fiber directions that they intend to use. That is to say: if they want to apply active stress in sheet normal direction they need to provide it, but if they don't want to do that (eta_n == 0, Tna == 0) then this error would not trigger.
Would that be a reasonable behavior for CANN/vascular models, or does this need to be made more flexible?
michelebucelli
left a comment
There was a problem hiding this comment.
Thanks @javijv4! I left a few comments below, sorry for the delay. Bonus points for renaming fN to fibers everywhere 😄
| double Df = dmn.cep.Diso; | ||
| if (dmn.cep.D.size() != 0) { | ||
| Df += dmn.cep.D[0]; | ||
| } |
There was a problem hiding this comment.
Minor suggestion:
| double Df = dmn.cep.Diso; | |
| if (dmn.cep.D.size() != 0) { | |
| Df += dmn.cep.D[0]; | |
| } | |
| const double Df = dmn.cep.Diso + (dmn.cep.D.size() != 0 ? dmn.cep.D[0] : 0.0); |
| const auto D = diffusion_tensor(2, dmn.cep.Diso, dmn.cep.D, fibers); | ||
|
|
||
| i = eq.s; | ||
| int i = eq.s; |
There was a problem hiding this comment.
Minor, and somewhat unrelated: I think this could be made const, but also I think that it might be better to just use eq.s when this is needed, to avoid adding an extra layer of indirection which might complicate the interpretation of the code below.
| const auto D = diffusion_tensor(3, dmn.cep.Diso, dmn.cep.D, fibers); | ||
|
|
||
| i = eq.s; | ||
| int i = eq.s; |
| if (fiber_conductivities.size() > insd) { | ||
| throw std::runtime_error( | ||
| "[construct_cep] CEP domain " + | ||
| std::to_string(eq.dmn[cDmn].Id) + " defines " + | ||
| std::to_string(fiber_conductivities.size()) + " directional conductivity " | ||
| "value(s), but mesh '" + lM.name + "' supports at most " + | ||
| std::to_string(insd) + "."); | ||
| } | ||
| if (fiber_conductivities.size() != 0 && !lM.lFib && | ||
| !lM.fibers.has_fibers()) { | ||
| throw std::runtime_error( | ||
| "[construct_cep] Directional conductivity requires fiber " | ||
| "directions on mesh '" + lM.name + "'."); | ||
| } |
There was a problem hiding this comment.
For these two errors, I suggest using the svmp exception classes, thrown through one of the methods svmp::check or svmp::throw_if.
Additionally, is there any way we could check these sooner? It seems to me that these could be caught somewhere before the construction of the EP system (ideally right after reading the parameters or after loading the mesh).
|
|
||
| if (insd == 3) { | ||
| cep_3d(com_mod, cep_mod, eNoN, nFn, w, N, Nx, al, yl, dl, fN, lR, lK); | ||
| Array<double> fibers; |
There was a problem hiding this comment.
I suggest allocating this out of the loop over Gauss points (since the Array data is heap-allocated this might make a bit of performance difference). The same variable could then be reused for the call below for insd == 2.
| if (basis.cols() != nsd || | ||
| (num_input_fibers != nsd - 1 && num_input_fibers != nsd)) { | ||
| svmp::raise<svmp::FE::InvalidArgumentException>( | ||
| "Fiber basis completion requires an nsd-by-nsd matrix containing " | ||
| "nsd - 1 or nsd directions."); | ||
| } |
There was a problem hiding this comment.
Minor: I suggest using svmp::throw_if instead. The same suggestion applies to other functions below.
|
|
||
| namespace { | ||
|
|
||
| void complete_fiber_basis(Eigen::MatrixXd& basis, |
There was a problem hiding this comment.
Even if this is in the anonymous namespace, I suggest adding a documentation comment to help the reader understand the intent of this function. The same applies to other functions below.
| MPI_Bcast(quadrature_fibers.data(), quadrature_components, cm_mod::mpreal, | ||
| cm_mod.master, cm.com()); |
There was a problem hiding this comment.
Is there no cm.bcast overload for Array3? If not, it might be a good idea to add one and use it here.
| if (FILE* file = fopen(file_name.c_str(), "r")) { | ||
| fclose(file); | ||
| } else { | ||
| svmp::raise<svmp::FileNotFoundException>(file_name); | ||
| } |
There was a problem hiding this comment.
I think it would be better to use std::filesystem::exists (and I'm sure there's a similar utility to check that the path actually points to a file, not a directory).
Current situation
Resolves #663
Fiber directions could only be set as constant or by pointing to a
.vtuwith fiber directions defined at the cells. This PR refactors the handling of the fibers across the code by creating aFibersclass and allows for nodal fibers.Raw storage and optional orthonormalization
Fibers have different requirements depending on the application. Cardiac mechanics materials such as HO and Guccione, and CEP with more than one directional conductivity, need an orthonormal basis. HGO, CANN, fiber-only CEP, and other raw-family consumers do not.
The
Fibersclass reads the input fibers and checks that vectors are unitary. Then, it stores and distributes only these input fibers directions at the quadrature points. The fibers at quadrature points are accessed throughget_quadrature_fibers(). For physics that needs an orthonormal basis, this function has an inputbool orthonormalize:false, the accessor returns the raw directions.true, it builds and returns annsd × nsdbasis fromnsd - 1ornsdmutually orthogonal source directions.Fiber input changes
This PR replaces the upstream
Fiber_directionandFiber_direction_file_pathtags with three association-specific inputs:Constant —
<Fiber_direction_constant>Cell (element) data from VTU —
<Fiber_direction_from_file_cells>FIB_DIR.Point (nodal) data from VTU —
<Fiber_direction_from_file_points>FIB_DIR.∑ Nₐ f⊗f, then principal eigenvector with sign chosen against the heaviest shape-function weight), so opposite nodal directions do not cancel.All raw directions are checked for unit length while reading. Mutual orthogonality at the original constant, cell, or point locations is recorded as validation metadata and enforced only if a consumer later requests an orthonormal basis.
The upstream tags
Fiber_directionandFiber_direction_file_pathare rejected at parse time with an error that points to the replacement tags.Fiber handling flow chart
flowchart TD A[Read fiber input] --> B[Check unit length] B --> O[Record source orthogonality] O --> C{Input association?} C -->|constant| C1[Reuse one set at all QPs] C -->|cell| C2[Copy per element to QPs] C -->|point| C3[Dyadic interpolate nodes to QPs] C1 --> Q[Raw quadrature fibers] C2 --> Q C3 --> Q Q --> H[Distribute raw arrays] H --> D{Accessor requests<br/>orthonormalization?} D -->|no| G[Return raw fibers] D -->|yes| E[Require nsd-1 or nsd directions<br/>and orthogonal source input] E --> P{Point data?} P -->|yes| F[Modified Gram-Schmidt at requested QP] P -->|no| K[Preserve constant or cell directions] F --> FB[Complete right-handed basis if needed] K --> FB FB --> T[Return owned temporary Array]Physics changes
Anisotropic_conductivityvector with explicitIsotropic_conductivity,Fiber_conductivity,Sheet_conductivity, andSheet_normal_conductivityterms. Isotropic CEP is allowed without fibers. Fiber-only anisotropy consumes the raw fiber family; sheet or sheet-normal conductivity requests a completed orthonormal basis. Conductivity is assembled asD = D_iso I + D_f f⊗f + ….mat_models::requires_orthonormal_fiber_basis()whether the material needs a basis, pass that boolean toget_quadrature_fibers(), retain the returnedArray, and create an Eigen view locally.get_quadrature_fibers()and use zero fibers when none are provided.Output
Fiber_direction1…Fiber_directionN, whereNis the raw input count:Release Notes
Fiber_direction/Fiber_direction_file_pathwith one of:Fiber_direction_constantFiber_direction_from_file_cellsFiber_direction_from_file_pointsmain, constant directions are not silently normalized. Every raw direction must be a unit vector and is otherwise preserved as supplied.nsd - 1ornsdmutually orthogonal source directions. Constant and cell directions are preserved and completed directly; interpolated point directions additionally go through modified Gram–Schmidt. The final direction is auto-completed fornsd - 1inputs.Anisotropic_conductivitywithIsotropic_conductivityplus optionalFiber_/Sheet_/Sheet_normal_conductivity. Isotropic CEP does not require fibers. Fiber-only conductivity uses raw directions; sheet / sheet-normal conductivity requires a completed basis (nsd - 1ornsdorthogonal inputs).Fiber_direction*fields (point or cell data depending on input mode).Documentation
Documentation will need to be updated to cover:
<Add_mesh>fiber tags and mutual exclusivityFIB_DIRassociation rules (point vs cell arrays) and the on-demand basis direction-count / orthogonality requirementsFiber_direction*VTK outputTesting
tests/unitTests/test_fibers.cpp(VTU fixtures undertests/unitTests/reference_data/fibers_{cell,point}_data*.vtu). Most of these were AI suggestions, so happy to drop/add if needed. Coverage includes:tests/cases/{struct,ustruct,cep,electromechanics}/were migrated to the new tags.mainat machine precision, confirming the previous behavior remains unchanged.Code of Conduct & Contributing Guidelines