forked from 1b-agent/fieldcraft-upstream
Nastran bulk data writer (SOL 101/103) and deck.write steps (M3) (#24)
* Nastran bulk data writer: fixed-format entries, exact large-field reals, SOL 101/103 decks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Nastran writer tests: golden decks, a re-reader of our output, node order against Nastran's conventions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * docs: Nastran writer in the roadmap, parity table and deck notes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Nastran writer: simplify planning (subcases, one property table), stream cards into the deck, write through the VFS ElementKind::corner_count/is_quadratic and Dofs::bits/from_bits join the model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Nastran joins the shared deck formats; deck.write chooses steps for every format nastran::write_resolved is the FORMATS row; Format::write_steps and with_steps write a subset of steps (a Nastran run is static or frequency steps); problems go through the shared unsupported(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm --------- Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
14 files changed
+2266
-5
No files matched your search
@@ -32,6 +32,16 @@ impl Dofs {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// The degrees of freedom as bits: bit `d - 1` for degree of freedom `d`.
|
||||
pub fn bits(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// The degrees of freedom of bits (bits above the sixth are ignored).
|
||||
pub fn from_bits(bits: u8) -> Dofs {
|
||||
Dofs(bits & 0b111_111)
|
||||
}
|
||||
|
||||
/// The degrees of freedom in order.
|
||||
pub fn list(self) -> Vec<u8> {
|
||||
(1..=6).filter(|d| self.contains(*d)).collect()
|
||||
|
||||
@@ -76,6 +76,22 @@ impl ElementKind {
|
||||
}
|
||||
}
|
||||
|
||||
/// Corner nodes: the first ones in the node list (the rest are mid-side nodes).
|
||||
pub fn corner_count(self) -> usize {
|
||||
match self {
|
||||
ElementKind::Rod2 | ElementKind::Beam2 => 2,
|
||||
ElementKind::Tri3 | ElementKind::Tri6 => 3,
|
||||
ElementKind::Quad4 | ElementKind::Quad8 | ElementKind::Tet4 | ElementKind::Tet10 => 4,
|
||||
ElementKind::Wedge6 => 6,
|
||||
ElementKind::Hex8 | ElementKind::Hex20 => 8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the element has mid-side nodes.
|
||||
pub fn is_quadratic(self) -> bool {
|
||||
self.node_count() > self.corner_count()
|
||||
}
|
||||
|
||||
/// Topological dimension: 1 for line elements, 2 for shells, 3 for solids.
|
||||
pub fn dimension(self) -> u8 {
|
||||
match self {
|
||||
@@ -387,6 +403,10 @@ mod tests {
|
||||
assert_eq!(ElementKind::Tet4.faces().len(), 4);
|
||||
assert_eq!(ElementKind::Wedge6.faces().len(), 5);
|
||||
assert_eq!(ElementKind::Hex20.edges().len(), 12);
|
||||
for kind in ElementKind::ALL {
|
||||
let mid_sides = kind.edges().iter().filter(|e| e.len() == 3).count();
|
||||
assert_eq!(kind.corner_count() + mid_sides, kind.node_count(), "{kind:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user