ANYfeSolver 0.5.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anyfesolver/__init__.py +1075 -0
- anyfesolver/_b2_attachment_elimination_dev.py +261 -0
- anyfesolver/_b2_f10_f11_point_attachment.py +163 -0
- anyfesolver/_b2_loaded_point_trace_dev.py +173 -0
- anyfesolver/_b2_physical_flexibility_linear.py +707 -0
- anyfesolver/_b2_physical_linear_batch_dev.py +491 -0
- anyfesolver/_b2_physical_linear_definition.py +415 -0
- anyfesolver/_b2_physical_load_bubbles.py +282 -0
- anyfesolver/_compensated_contractions.py +497 -0
- anyfesolver/_compensated_native.py +437 -0
- anyfesolver/_corotational_batch.py +219 -0
- anyfesolver/_director_trace.py +214 -0
- anyfesolver/_dyadic_bilinear.py +76 -0
- anyfesolver/_dyadic_factor_chain.py +25 -0
- anyfesolver/_ge_beam3_adaptive_arc_program.py +286 -0
- anyfesolver/_ge_beam3_analysis_combined.py +102 -0
- anyfesolver/_ge_beam3_analysis_fibre_translation.py +130 -0
- anyfesolver/_ge_beam3_analysis_modal_snapshot.py +27 -0
- anyfesolver/_ge_beam3_analysis_nodal_program.py +140 -0
- anyfesolver/_ge_beam3_analysis_translation.py +147 -0
- anyfesolver/_ge_beam3_analysis_translation_modal.py +84 -0
- anyfesolver/_ge_beam3_arc_geometry.py +89 -0
- anyfesolver/_ge_beam3_arc_program.py +304 -0
- anyfesolver/_ge_beam3_authoritative_cell_seed.py +25 -0
- anyfesolver/_ge_beam3_cell_identity.py +52 -0
- anyfesolver/_ge_beam3_centered_batched_finite_inertia.py +132 -0
- anyfesolver/_ge_beam3_centered_finite_inertia.py +171 -0
- anyfesolver/_ge_beam3_centered_mixed.py +104 -0
- anyfesolver/_ge_beam3_centered_reference.py +187 -0
- anyfesolver/_ge_beam3_centered_so3_strain.py +76 -0
- anyfesolver/_ge_beam3_complementary_section.py +122 -0
- anyfesolver/_ge_beam3_compliance_snapshot_guard.py +20 -0
- anyfesolver/_ge_beam3_connected_dynamic.py +486 -0
- anyfesolver/_ge_beam3_connected_trajectory.py +258 -0
- anyfesolver/_ge_beam3_conservative_backtrack.py +26 -0
- anyfesolver/_ge_beam3_controlled_fibre_modes.py +105 -0
- anyfesolver/_ge_beam3_coupled_beam_subdomain.py +128 -0
- anyfesolver/_ge_beam3_coupled_modes.py +256 -0
- anyfesolver/_ge_beam3_deterministic_reduction.py +264 -0
- anyfesolver/_ge_beam3_displacement_program.py +255 -0
- anyfesolver/_ge_beam3_durable_coupled.py +251 -0
- anyfesolver/_ge_beam3_dynamic_elastic_state.py +461 -0
- anyfesolver/_ge_beam3_effective_deadline.py +58 -0
- anyfesolver/_ge_beam3_elastic_seed_continuation.py +223 -0
- anyfesolver/_ge_beam3_elastic_seed_modal.py +139 -0
- anyfesolver/_ge_beam3_equilibrium_line_program.py +98 -0
- anyfesolver/_ge_beam3_equilibrium_schur_solver.py +96 -0
- anyfesolver/_ge_beam3_factor_chain_modes.py +103 -0
- anyfesolver/_ge_beam3_fibre_cell.py +408 -0
- anyfesolver/_ge_beam3_fibre_distributed_couples.py +191 -0
- anyfesolver/_ge_beam3_fibre_kinematic_cell.py +75 -0
- anyfesolver/_ge_beam3_fibre_line_program.py +157 -0
- anyfesolver/_ge_beam3_fibre_line_work.py +87 -0
- anyfesolver/_ge_beam3_fibre_reference_modal.py +104 -0
- anyfesolver/_ge_beam3_fibre_schur_solver.py +134 -0
- anyfesolver/_ge_beam3_fibre_section.py +286 -0
- anyfesolver/_ge_beam3_fibre_static_boundary.py +113 -0
- anyfesolver/_ge_beam3_g1_analysis.py +307 -0
- anyfesolver/_ge_beam3_g1_elastic.py +161 -0
- anyfesolver/_ge_beam3_g1_element.py +185 -0
- anyfesolver/_ge_beam3_g1_operator.py +137 -0
- anyfesolver/_ge_beam3_g2_analysis.py +176 -0
- anyfesolver/_ge_beam3_g2_constraints.py +162 -0
- anyfesolver/_ge_beam3_g3_analysis.py +385 -0
- anyfesolver/_ge_beam3_g3_constraints.py +168 -0
- anyfesolver/_ge_beam3_g3b_owner.py +296 -0
- anyfesolver/_ge_beam3_g3b_q4_reference.py +186 -0
- anyfesolver/_ge_beam3_g3b_reference.py +206 -0
- anyfesolver/_ge_beam3_g3b_result_schema.py +107 -0
- anyfesolver/_ge_beam3_g3b_s3_reference.py +185 -0
- anyfesolver/_ge_beam3_g3b_weighted_reference.py +193 -0
- anyfesolver/_ge_beam3_g3c_affine_q4_chart.py +58 -0
- anyfesolver/_ge_beam3_g3c_affine_q4_increment_chart.py +257 -0
- anyfesolver/_ge_beam3_g3c_affine_q4_recovery.py +434 -0
- anyfesolver/_ge_beam3_g3c_affine_q4_registry.py +127 -0
- anyfesolver/_ge_beam3_g3c_b2_physical.py +153 -0
- anyfesolver/_ge_beam3_g3c_b2_physical_adapter.py +160 -0
- anyfesolver/_ge_beam3_g3c_definition.py +117 -0
- anyfesolver/_ge_beam3_g3c_local_beam.py +188 -0
- anyfesolver/_ge_beam3_g3c_local_shell.py +371 -0
- anyfesolver/_ge_beam3_g3c_owner.py +400 -0
- anyfesolver/_ge_beam3_g3c_physical_authority.py +203 -0
- anyfesolver/_ge_beam3_g3c_physical_owner.py +556 -0
- anyfesolver/_ge_beam3_g3c_recovery.py +91 -0
- anyfesolver/_ge_beam3_g3c_so3_numerics.py +81 -0
- anyfesolver/_ge_beam3_g3c_stable/__init__.py +1 -0
- anyfesolver/_ge_beam3_g3c_stable/authority.py +152 -0
- anyfesolver/_ge_beam3_g3c_stable/beam.py +196 -0
- anyfesolver/_ge_beam3_g3c_stable/boundary.py +38 -0
- anyfesolver/_ge_beam3_g3c_stable/chart.py +23 -0
- anyfesolver/_ge_beam3_g3c_stable/compensated.py +46 -0
- anyfesolver/_ge_beam3_g3c_stable/element.py +190 -0
- anyfesolver/_ge_beam3_g3c_stable/fit.py +54 -0
- anyfesolver/_ge_beam3_g3c_stable/joint.py +255 -0
- anyfesolver/_ge_beam3_g3c_stable/line_work.py +92 -0
- anyfesolver/_ge_beam3_g3c_stable/operator.py +147 -0
- anyfesolver/_ge_beam3_g3c_stable/owner.py +415 -0
- anyfesolver/_ge_beam3_g3c_stable/shell.py +358 -0
- anyfesolver/_ge_beam3_g4_completion.py +186 -0
- anyfesolver/_ge_beam3_g4a_station_transaction.py +385 -0
- anyfesolver/_ge_beam3_g5_completion.py +68 -0
- anyfesolver/_ge_beam3_g6_domain.py +354 -0
- anyfesolver/_ge_beam3_generalized_cell.py +172 -0
- anyfesolver/_ge_beam3_generalized_ellipsoid_section.py +215 -0
- anyfesolver/_ge_beam3_generalized_static_boundary.py +233 -0
- anyfesolver/_ge_beam3_lifted_kinetic_factor.py +26 -0
- anyfesolver/_ge_beam3_line_fibre_modes.py +121 -0
- anyfesolver/_ge_beam3_load_program.py +275 -0
- anyfesolver/_ge_beam3_loaded_modal.py +225 -0
- anyfesolver/_ge_beam3_mixed_ad.py +286 -0
- anyfesolver/_ge_beam3_native_analysis.py +484 -0
- anyfesolver/_ge_beam3_native_arc.py +269 -0
- anyfesolver/_ge_beam3_native_arc_adaptive.py +251 -0
- anyfesolver/_ge_beam3_native_arc_restart.py +88 -0
- anyfesolver/_ge_beam3_native_arc_source.py +62 -0
- anyfesolver/_ge_beam3_native_buckling.py +206 -0
- anyfesolver/_ge_beam3_native_combined_couples.py +179 -0
- anyfesolver/_ge_beam3_native_combined_restart.py +205 -0
- anyfesolver/_ge_beam3_native_definition.py +238 -0
- anyfesolver/_ge_beam3_native_distributed_element.py +220 -0
- anyfesolver/_ge_beam3_native_distributed_loading.py +92 -0
- anyfesolver/_ge_beam3_native_distributed_program.py +165 -0
- anyfesolver/_ge_beam3_native_distributed_recovery.py +34 -0
- anyfesolver/_ge_beam3_native_distributed_restart.py +221 -0
- anyfesolver/_ge_beam3_native_dynamic_analysis.py +149 -0
- anyfesolver/_ge_beam3_native_dynamic_restart.py +261 -0
- anyfesolver/_ge_beam3_native_fibre_current_modal.py +104 -0
- anyfesolver/_ge_beam3_native_fibre_recovery.py +34 -0
- anyfesolver/_ge_beam3_native_fibre_restart.py +211 -0
- anyfesolver/_ge_beam3_native_fibre_static_element.py +193 -0
- anyfesolver/_ge_beam3_native_force_adaptation.py +56 -0
- anyfesolver/_ge_beam3_native_generalized_combined_couples.py +188 -0
- anyfesolver/_ge_beam3_native_generalized_combined_restart.py +207 -0
- anyfesolver/_ge_beam3_native_generalized_element.py +222 -0
- anyfesolver/_ge_beam3_native_generalized_factor_modal.py +122 -0
- anyfesolver/_ge_beam3_native_generalized_loading.py +92 -0
- anyfesolver/_ge_beam3_native_generalized_modal.py +274 -0
- anyfesolver/_ge_beam3_native_generalized_paired_modal.py +13 -0
- anyfesolver/_ge_beam3_native_generalized_parameter.py +89 -0
- anyfesolver/_ge_beam3_native_generalized_program.py +196 -0
- anyfesolver/_ge_beam3_native_generalized_recovery.py +34 -0
- anyfesolver/_ge_beam3_native_generalized_restart.py +243 -0
- anyfesolver/_ge_beam3_native_history_profile.py +53 -0
- anyfesolver/_ge_beam3_native_line_loading.py +93 -0
- anyfesolver/_ge_beam3_native_line_program.py +150 -0
- anyfesolver/_ge_beam3_native_line_recovery.py +34 -0
- anyfesolver/_ge_beam3_native_line_restart.py +197 -0
- anyfesolver/_ge_beam3_native_line_static_element.py +220 -0
- anyfesolver/_ge_beam3_native_load_admission.py +37 -0
- anyfesolver/_ge_beam3_native_midpoint.py +1004 -0
- anyfesolver/_ge_beam3_native_multi_shell_contact.py +539 -0
- anyfesolver/_ge_beam3_native_pair_contact.py +236 -0
- anyfesolver/_ge_beam3_native_shell_contact.py +357 -0
- anyfesolver/_ge_beam3_native_spatial_couples.py +176 -0
- anyfesolver/_ge_beam3_native_spatial_restart.py +204 -0
- anyfesolver/_ge_beam3_native_transient.py +467 -0
- anyfesolver/_ge_beam3_native_translation.py +225 -0
- anyfesolver/_ge_beam3_native_translation_restart.py +54 -0
- anyfesolver/_ge_beam3_operator_validation_scope.py +53 -0
- anyfesolver/_ge_beam3_p5/__init__.py +11 -0
- anyfesolver/_ge_beam3_p5/algebra.py +91 -0
- anyfesolver/_ge_beam3_p5/arrays.py +29 -0
- anyfesolver/_ge_beam3_p5/chart.py +45 -0
- anyfesolver/_ge_beam3_p5/codec.py +82 -0
- anyfesolver/_ge_beam3_p5/committed_modal.py +191 -0
- anyfesolver/_ge_beam3_p5/compensated.py +118 -0
- anyfesolver/_ge_beam3_p5/compensated_coordinates.py +44 -0
- anyfesolver/_ge_beam3_p5/core.py +159 -0
- anyfesolver/_ge_beam3_p5/element.py +207 -0
- anyfesolver/_ge_beam3_p5/factors.py +61 -0
- anyfesolver/_ge_beam3_p5/inertia.py +120 -0
- anyfesolver/_ge_beam3_p5/mass.py +70 -0
- anyfesolver/_ge_beam3_p5/mixed.py +256 -0
- anyfesolver/_ge_beam3_p5/reference_modal.py +73 -0
- anyfesolver/_ge_beam3_p5/section.py +139 -0
- anyfesolver/_ge_beam3_p5/typed_schema.py +111 -0
- anyfesolver/_ge_beam3_p5_centered/__init__.py +6 -0
- anyfesolver/_ge_beam3_p5_centered/codec.py +84 -0
- anyfesolver/_ge_beam3_p5_centered/committed_modal.py +192 -0
- anyfesolver/_ge_beam3_p5_centered/core.py +171 -0
- anyfesolver/_ge_beam3_p5_centered/element.py +216 -0
- anyfesolver/_ge_beam3_p5_centered/mass.py +99 -0
- anyfesolver/_ge_beam3_p5_centered/positions.py +34 -0
- anyfesolver/_ge_beam3_p5_centered/reference_modal.py +73 -0
- anyfesolver/_ge_beam3_p5_coordinates/__init__.py +6 -0
- anyfesolver/_ge_beam3_p5_coordinates/codec.py +84 -0
- anyfesolver/_ge_beam3_p5_coordinates/committed_modal.py +192 -0
- anyfesolver/_ge_beam3_p5_coordinates/core.py +163 -0
- anyfesolver/_ge_beam3_p5_coordinates/element.py +213 -0
- anyfesolver/_ge_beam3_p5_coordinates/positions.py +102 -0
- anyfesolver/_ge_beam3_p5_coordinates/reference_modal.py +73 -0
- anyfesolver/_ge_beam3_p5_loads/__init__.py +7 -0
- anyfesolver/_ge_beam3_p5_loads/codec.py +85 -0
- anyfesolver/_ge_beam3_p5_loads/core.py +192 -0
- anyfesolver/_ge_beam3_p5_loads/element.py +243 -0
- anyfesolver/_ge_beam3_p5_loads/state.py +120 -0
- anyfesolver/_ge_beam3_p5_loads/work.py +45 -0
- anyfesolver/_ge_beam3_p5_seeded/__init__.py +9 -0
- anyfesolver/_ge_beam3_p5_seeded/codec.py +87 -0
- anyfesolver/_ge_beam3_p5_seeded/core.py +197 -0
- anyfesolver/_ge_beam3_p5_seeded/element.py +245 -0
- anyfesolver/_ge_beam3_p5_seeded/state.py +122 -0
- anyfesolver/_ge_beam3_p5_seeded/work.py +47 -0
- anyfesolver/_ge_beam3_pose_joint.py +268 -0
- anyfesolver/_ge_beam3_precise_geometric_work.py +147 -0
- anyfesolver/_ge_beam3_reassembled_signed_modes.py +101 -0
- anyfesolver/_ge_beam3_reference_identity.py +92 -0
- anyfesolver/_ge_beam3_refined_fibre_schur_solver.py +104 -0
- anyfesolver/_ge_beam3_refinement_capacity.py +19 -0
- anyfesolver/_ge_beam3_relative_virgin_reference_modes.py +60 -0
- anyfesolver/_ge_beam3_retained_arc.py +350 -0
- anyfesolver/_ge_beam3_retained_arc_geometry.py +66 -0
- anyfesolver/_ge_beam3_retained_arc_modal.py +125 -0
- anyfesolver/_ge_beam3_retained_elastic.py +134 -0
- anyfesolver/_ge_beam3_retained_fibre.py +209 -0
- anyfesolver/_ge_beam3_retained_fibre_control.py +270 -0
- anyfesolver/_ge_beam3_retained_fibre_element.py +58 -0
- anyfesolver/_ge_beam3_retained_fibre_modal.py +155 -0
- anyfesolver/_ge_beam3_retained_fibre_modes.py +189 -0
- anyfesolver/_ge_beam3_retained_fibre_program.py +68 -0
- anyfesolver/_ge_beam3_retained_fibre_state.py +268 -0
- anyfesolver/_ge_beam3_retained_force_program.py +72 -0
- anyfesolver/_ge_beam3_retained_generalized.py +186 -0
- anyfesolver/_ge_beam3_retained_generalized_modal.py +171 -0
- anyfesolver/_ge_beam3_retained_generalized_program.py +63 -0
- anyfesolver/_ge_beam3_retained_generalized_state.py +281 -0
- anyfesolver/_ge_beam3_retained_nodal_loading.py +111 -0
- anyfesolver/_ge_beam3_retained_plastic.py +116 -0
- anyfesolver/_ge_beam3_retained_plastic_modes.py +144 -0
- anyfesolver/_ge_beam3_retained_plastic_program.py +69 -0
- anyfesolver/_ge_beam3_retained_plastic_state.py +165 -0
- anyfesolver/_ge_beam3_retained_state.py +194 -0
- anyfesolver/_ge_beam3_retained_translation_control.py +345 -0
- anyfesolver/_ge_beam3_retained_translation_modal.py +137 -0
- anyfesolver/_ge_beam3_schur_line_program.py +98 -0
- anyfesolver/_ge_beam3_seeded_fibre_control.py +175 -0
- anyfesolver/_ge_beam3_seeded_load_program.py +301 -0
- anyfesolver/_ge_beam3_seeded_loaded_modal.py +226 -0
- anyfesolver/_ge_beam3_seeded_signed_modes.py +150 -0
- anyfesolver/_ge_beam3_shared_kinematic_split.py +53 -0
- anyfesolver/_ge_beam3_shell_joint_state.py +396 -0
- anyfesolver/_ge_beam3_shell_joint_trial.py +217 -0
- anyfesolver/_ge_beam3_signed_loaded_modes.py +140 -0
- anyfesolver/_ge_beam3_spatial_fibre_program.py +113 -0
- anyfesolver/_ge_beam3_spatial_nodal_moments.py +38 -0
- anyfesolver/_ge_beam3_station_resultant_cell.py +235 -0
- anyfesolver/_ge_beam3_variational_shell.py +211 -0
- anyfesolver/_ge_beam3_virgin_reference_modes.py +60 -0
- anyfesolver/_impact_coupling.py +162 -0
- anyfesolver/_native_authority_dag.py +77 -0
- anyfesolver/_native_dyadic_interval_inertia.py +64 -0
- anyfesolver/_native_exact_shift_chain_modes.py +140 -0
- anyfesolver/_native_exact_shift_inertia.py +86 -0
- anyfesolver/_native_factor_chain_modes.py +192 -0
- anyfesolver/_native_guard.cpp +828 -0
- anyfesolver/_native_guard_dispatch.py +140 -0
- anyfesolver/_native_large_factor_chain.py +55 -0
- anyfesolver/_native_material_protocol.py +51 -0
- anyfesolver/_native_midpoint_controller.py +90 -0
- anyfesolver/_native_modal_capacity.py +26 -0
- anyfesolver/_native_paired_factor_chain_modes.py +244 -0
- anyfesolver/_native_reassembled_factor_modes.py +185 -0
- anyfesolver/_native_reference_factor_spectrum.py +102 -0
- anyfesolver/_native_reference_modal.py +180 -0
- anyfesolver/_native_relative_factor_chain_modes.py +204 -0
- anyfesolver/_native_relative_reference_factor_spectrum.py +110 -0
- anyfesolver/_native_retained_step.py +15 -0
- anyfesolver/_native_rotation_state.py +797 -0
- anyfesolver/_native_signed_factor_modes.py +257 -0
- anyfesolver/_native_signed_kinetic_factor_modes.py +279 -0
- anyfesolver/_native_stationary_spectrum.py +79 -0
- anyfesolver/_nonlinear_events.py +150 -0
- anyfesolver/_nonlinear_seed.py +80 -0
- anyfesolver/_paired_modal_arithmetic.py +71 -0
- anyfesolver/_plate_stability_continuation.py +171 -0
- anyfesolver/_q4_assumed_strain_plasticity.py +502 -0
- anyfesolver/_qualified_authority_epoch.py +561 -0
- anyfesolver/_relative_reference_svd.py +44 -0
- anyfesolver/_stiffened_shell_affine_endpoint.py +232 -0
- anyfesolver/_stiffened_shell_algorithmic_pullback.py +38 -0
- anyfesolver/_stiffened_shell_arc_checkpoint.py +422 -0
- anyfesolver/_stiffened_shell_arc_control.py +108 -0
- anyfesolver/_stiffened_shell_arc_metric.py +222 -0
- anyfesolver/_stiffened_shell_arc_publication.py +153 -0
- anyfesolver/_stiffened_shell_arc_trial.py +310 -0
- anyfesolver/_stiffened_shell_b2_affine_resolution.py +101 -0
- anyfesolver/_stiffened_shell_b2_assembly_diagnostics.py +172 -0
- anyfesolver/_stiffened_shell_b2_definition_ownership.py +189 -0
- anyfesolver/_stiffened_shell_b2_experimental.py +153 -0
- anyfesolver/_stiffened_shell_b2_nodal_footprint.py +273 -0
- anyfesolver/_stiffened_shell_b2_physical_rigid_footprint.py +237 -0
- anyfesolver/_stiffened_shell_b2_q4_panel_experimental.py +2447 -0
- anyfesolver/_stiffened_shell_b2_q4_point_experimental.py +251 -0
- anyfesolver/_stiffened_shell_b2_reference_buckling.py +413 -0
- anyfesolver/_stiffened_shell_b3_chart.py +70 -0
- anyfesolver/_stiffened_shell_b3_local_adapter.py +141 -0
- anyfesolver/_stiffened_shell_beam_history.py +107 -0
- anyfesolver/_stiffened_shell_bl5_local_experimental.py +173 -0
- anyfesolver/_stiffened_shell_bl5_native_elastic.py +28 -0
- anyfesolver/_stiffened_shell_branch_event_trial.py +58 -0
- anyfesolver/_stiffened_shell_consumer_capabilities.py +124 -0
- anyfesolver/_stiffened_shell_coordinate_support.py +110 -0
- anyfesolver/_stiffened_shell_displacement_adaptive.py +327 -0
- anyfesolver/_stiffened_shell_displacement_checkpoint.py +360 -0
- anyfesolver/_stiffened_shell_displacement_control.py +326 -0
- anyfesolver/_stiffened_shell_displacement_corrector.py +293 -0
- anyfesolver/_stiffened_shell_displacement_increment.py +121 -0
- anyfesolver/_stiffened_shell_distributing_footprint.py +354 -0
- anyfesolver/_stiffened_shell_distributing_pose.py +267 -0
- anyfesolver/_stiffened_shell_f06_increment_arithmetic.py +205 -0
- anyfesolver/_stiffened_shell_ge_body_load.py +300 -0
- anyfesolver/_stiffened_shell_ge_checkpoint_binary.py +472 -0
- anyfesolver/_stiffened_shell_ge_distributed_load.py +210 -0
- anyfesolver/_stiffened_shell_ge_distributed_programme.py +175 -0
- anyfesolver/_stiffened_shell_ge_distributing_finite.py +242 -0
- anyfesolver/_stiffened_shell_ge_distributing_reference.py +296 -0
- anyfesolver/_stiffened_shell_ge_experimental.py +2454 -0
- anyfesolver/_stiffened_shell_ge_finite_experimental.py +2714 -0
- anyfesolver/_stiffened_shell_ge_mixed_modal_mass.py +33 -0
- anyfesolver/_stiffened_shell_ge_point_buckling.py +723 -0
- anyfesolver/_stiffened_shell_ge_pressure_load.py +366 -0
- anyfesolver/_stiffened_shell_ge_reference_modal.py +580 -0
- anyfesolver/_stiffened_shell_generalized_recovery.py +150 -0
- anyfesolver/_stiffened_shell_increment_control.py +217 -0
- anyfesolver/_stiffened_shell_l1_assembly_experimental.py +420 -0
- anyfesolver/_stiffened_shell_l1_local_experimental.py +284 -0
- anyfesolver/_stiffened_shell_l1_supported_work_experimental.py +146 -0
- anyfesolver/_stiffened_shell_native_distributed_load.py +120 -0
- anyfesolver/_stiffened_shell_native_gravity.py +236 -0
- anyfesolver/_stiffened_shell_nodal_footprint.py +255 -0
- anyfesolver/_stiffened_shell_physical_q4_mass.py +98 -0
- anyfesolver/_stiffened_shell_physical_s3_mass.py +53 -0
- anyfesolver/_stiffened_shell_point_recovery.py +49 -0
- anyfesolver/_stiffened_shell_q4_algorithmic_pullback.py +88 -0
- anyfesolver/_stiffened_shell_q4_material_event.py +114 -0
- anyfesolver/_stiffened_shell_q4_reference.py +65 -0
- anyfesolver/_stiffened_shell_rotation_charts.py +58 -0
- anyfesolver/_stiffened_shell_s3_algorithmic_pullback.py +59 -0
- anyfesolver/_stiffened_shell_s3_conservative_buckling.py +28 -0
- anyfesolver/_stiffened_shell_s3_dead_work_candidate.py +107 -0
- anyfesolver/_stiffened_shell_s3_material_event.py +30 -0
- anyfesolver/_stiffened_shell_same_parent_trial.py +224 -0
- anyfesolver/_stiffened_shell_selected_recovery.py +113 -0
- anyfesolver/_stiffened_shell_staged_load_experimental.py +392 -0
- anyfesolver/_stiffened_shell_stress_free_reference.py +204 -0
- anyfesolver/_wire_types.py +17 -0
- anyfesolver/activity.py +1537 -0
- anyfesolver/algebraic_dynamics.py +2864 -0
- anyfesolver/analysis_session.py +897 -0
- anyfesolver/anystructure_fem_mode.py +1637 -0
- anyfesolver/arc_length.py +2587 -0
- anyfesolver/assembly.py +1830 -0
- anyfesolver/b3_ge.py +179 -0
- anyfesolver/baselines.py +303 -0
- anyfesolver/beam_sections.py +292 -0
- anyfesolver/beam_shell_verification.py +4685 -0
- anyfesolver/beam_validity.py +110 -0
- anyfesolver/benchmarks.py +495 -0
- anyfesolver/boundary.py +1680 -0
- anyfesolver/buckling.py +2136 -0
- anyfesolver/buckling_validity.py +91 -0
- anyfesolver/capacity_workflow.py +431 -0
- anyfesolver/capsule_runtime_guard.py +99 -0
- anyfesolver/cases.py +173 -0
- anyfesolver/composite_strip_verification.py +297 -0
- anyfesolver/concentrated_mass.py +84 -0
- anyfesolver/constraint_audit.py +553 -0
- anyfesolver/contact.py +4785 -0
- anyfesolver/contact_performance.py +301 -0
- anyfesolver/control.py +206 -0
- anyfesolver/corotational.py +1935 -0
- anyfesolver/corotational_performance.py +157 -0
- anyfesolver/current_state_tangent.py +2780 -0
- anyfesolver/cylinder_benchmarks.py +369 -0
- anyfesolver/damage_matrix_performance.py +617 -0
- anyfesolver/dynamics.py +2833 -0
- anyfesolver/e4_pl_element.py +8265 -0
- anyfesolver/e4_pl_enriched.py +194 -0
- anyfesolver/e4_pl_enriched_nonlinear.py +159 -0
- anyfesolver/e4_pl_s3_element.py +9752 -0
- anyfesolver/e4_pl_s3_initial_fields.py +101 -0
- anyfesolver/e4_pl_s3_state.py +4723 -0
- anyfesolver/e4_pl_s3_v2_element.py +1844 -0
- anyfesolver/e4_pl_s3_v2b_element.py +1936 -0
- anyfesolver/e4_pl_s3_v2c_element.py +2196 -0
- anyfesolver/e4_pl_s3_v2d_element.py +2621 -0
- anyfesolver/e4_pl_s3_v2d_state.py +376 -0
- anyfesolver/element_capabilities.py +159 -0
- anyfesolver/element_qualification.py +432 -0
- anyfesolver/elements.py +6327 -0
- anyfesolver/experimental_csr_assembly.py +238 -0
- anyfesolver/external_references.py +1253 -0
- anyfesolver/fe_core.py +832 -0
- anyfesolver/floating_pressure_regions.py +119 -0
- anyfesolver/floating_response.py +486 -0
- anyfesolver/floating_transient.py +1281 -0
- anyfesolver/fracture.py +621 -0
- anyfesolver/ge_beam3_curved_reference.py +725 -0
- anyfesolver/ge_beam3_element.py +885 -0
- anyfesolver/ge_beam3_mixed_element.py +1216 -0
- anyfesolver/ge_beam3_mixed_state.py +814 -0
- anyfesolver/ge_beam3_native.py +129 -0
- anyfesolver/ge_beam3_native_connected_dynamics.py +24 -0
- anyfesolver/ge_beam3_native_dynamics.py +70 -0
- anyfesolver/ge_beam3_state.py +672 -0
- anyfesolver/general_contact/__init__.py +151 -0
- anyfesolver/general_contact/_enriched_multitarget_empty.py +202 -0
- anyfesolver/general_contact/_enriched_multitarget_trial.py +183 -0
- anyfesolver/general_contact/_results_archive_common.py +65 -0
- anyfesolver/general_contact/algebraic_path.py +206 -0
- anyfesolver/general_contact/arc_contact.py +237 -0
- anyfesolver/general_contact/arc_results_archive.py +158 -0
- anyfesolver/general_contact/augmentation.py +157 -0
- anyfesolver/general_contact/automatic_domain.py +621 -0
- anyfesolver/general_contact/beam_chain_consistent.py +357 -0
- anyfesolver/general_contact/beam_chain_end_geometry.py +237 -0
- anyfesolver/general_contact/beam_chain_ends.py +220 -0
- anyfesolver/general_contact/beam_chain_pair.py +416 -0
- anyfesolver/general_contact/beam_curve_end_geometry.py +254 -0
- anyfesolver/general_contact/beam_curve_ends.py +237 -0
- anyfesolver/general_contact/beam_curve_pair.py +794 -0
- anyfesolver/general_contact/beam_curve_pair_static.py +303 -0
- anyfesolver/general_contact/beam_end_radial_geometry.py +221 -0
- anyfesolver/general_contact/beam_end_topology.py +15 -0
- anyfesolver/general_contact/beam_hemisphere_coupled_tool.py +61 -0
- anyfesolver/general_contact/beam_hemisphere_cylinder.py +134 -0
- anyfesolver/general_contact/beam_hemisphere_cylinder_motion.py +89 -0
- anyfesolver/general_contact/beam_hemisphere_cylinder_work.py +216 -0
- anyfesolver/general_contact/beam_hemisphere_plane.py +175 -0
- anyfesolver/general_contact/beam_hemisphere_s3_support.py +152 -0
- anyfesolver/general_contact/beam_hemisphere_sphere.py +135 -0
- anyfesolver/general_contact/beam_hemisphere_sphere_work.py +114 -0
- anyfesolver/general_contact/beam_hemisphere_support.py +211 -0
- anyfesolver/general_contact/beam_pair.py +523 -0
- anyfesolver/general_contact/beam_pair_end_geometry.py +269 -0
- anyfesolver/general_contact/beam_pair_ends.py +222 -0
- anyfesolver/general_contact/beam_plane.py +425 -0
- anyfesolver/general_contact/beam_radial.py +500 -0
- anyfesolver/general_contact/beam_radial_ends.py +223 -0
- anyfesolver/general_contact/beam_s3_directional_rates.py +196 -0
- anyfesolver/general_contact/beam_s3_rotational_transient.py +126 -0
- anyfesolver/general_contact/beam_s3_transient.py +139 -0
- anyfesolver/general_contact/beam_shell.py +648 -0
- anyfesolver/general_contact/beam_shell_patch.py +395 -0
- anyfesolver/general_contact/beam_temporal_bounds.py +230 -0
- anyfesolver/general_contact/capability.py +101 -0
- anyfesolver/general_contact/capped_cylinder.py +618 -0
- anyfesolver/general_contact/contact_search.py +95 -0
- anyfesolver/general_contact/core.py +1769 -0
- anyfesolver/general_contact/curved_s3_q4_work.py +238 -0
- anyfesolver/general_contact/curved_shell_skin.py +520 -0
- anyfesolver/general_contact/d1_interior_profile.py +427 -0
- anyfesolver/general_contact/deformable_beam_s3_caps.py +143 -0
- anyfesolver/general_contact/deformable_beam_s3_line.py +211 -0
- anyfesolver/general_contact/deformable_beam_s3_static.py +152 -0
- anyfesolver/general_contact/descriptor_rates.py +61 -0
- anyfesolver/general_contact/enriched_cold_predictor.py +161 -0
- anyfesolver/general_contact/enriched_moment_batch.py +337 -0
- anyfesolver/general_contact/enriched_multitarget.py +308 -0
- anyfesolver/general_contact/enriched_multitarget_gap.py +156 -0
- anyfesolver/general_contact/enriched_multitarget_moments.py +194 -0
- anyfesolver/general_contact/enriched_multitarget_topology.py +171 -0
- anyfesolver/general_contact/enriched_nonlinear.py +440 -0
- anyfesolver/general_contact/enriched_overlap.py +299 -0
- anyfesolver/general_contact/enriched_path.py +253 -0
- anyfesolver/general_contact/enriched_small_cut.py +310 -0
- anyfesolver/general_contact/enriched_static.py +481 -0
- anyfesolver/general_contact/enriched_union.py +320 -0
- anyfesolver/general_contact/equilibrated_pressure_pivot.py +140 -0
- anyfesolver/general_contact/event_subdivision.py +273 -0
- anyfesolver/general_contact/finite_mixed_state.py +133 -0
- anyfesolver/general_contact/finite_rigid_patch.py +196 -0
- anyfesolver/general_contact/finite_rigid_support.py +129 -0
- anyfesolver/general_contact/finite_rigid_sweep.py +53 -0
- anyfesolver/general_contact/finite_rotation_predictor.py +171 -0
- anyfesolver/general_contact/finite_rotation_skin.py +212 -0
- anyfesolver/general_contact/finite_rotation_state.py +106 -0
- anyfesolver/general_contact/finite_rotation_sweep.py +161 -0
- anyfesolver/general_contact/finite_v2d_state.py +104 -0
- anyfesolver/general_contact/fixed_projection_graph.py +205 -0
- anyfesolver/general_contact/fixed_projection_s3_events.py +235 -0
- anyfesolver/general_contact/free_rigid.py +134 -0
- anyfesolver/general_contact/friction_point.py +378 -0
- anyfesolver/general_contact/gap_extrema.py +119 -0
- anyfesolver/general_contact/guided_pair_transient_geometry.py +138 -0
- anyfesolver/general_contact/initial_contact_seed.py +368 -0
- anyfesolver/general_contact/linearized_pivot.py +595 -0
- anyfesolver/general_contact/material_work_diagnostics.py +159 -0
- anyfesolver/general_contact/moment_batch_ad.py +129 -0
- anyfesolver/general_contact/moment_batch_points.py +75 -0
- anyfesolver/general_contact/moving.py +1031 -0
- anyfesolver/general_contact/multiple_beam_rigid.py +248 -0
- anyfesolver/general_contact/multiple_beam_s3_caps.py +199 -0
- anyfesolver/general_contact/multiple_beam_s3_transient.py +223 -0
- anyfesolver/general_contact/multiple_projected_pairs.py +196 -0
- anyfesolver/general_contact/multiple_shell_planes.py +226 -0
- anyfesolver/general_contact/native_activation_static.py +143 -0
- anyfesolver/general_contact/native_checkpoint_results.py +213 -0
- anyfesolver/general_contact/native_curve_union.py +268 -0
- anyfesolver/general_contact/native_curved_geometry.py +374 -0
- anyfesolver/general_contact/native_curved_pair_partition.py +590 -0
- anyfesolver/general_contact/native_curved_plane_work.py +864 -0
- anyfesolver/general_contact/native_curved_s3_work.py +472 -0
- anyfesolver/general_contact/native_event_polynomial.py +164 -0
- anyfesolver/general_contact/native_fibre_context.py +173 -0
- anyfesolver/general_contact/native_fibre_results.py +74 -0
- anyfesolver/general_contact/native_initial_guess.py +122 -0
- anyfesolver/general_contact/native_material_activation.py +108 -0
- anyfesolver/general_contact/native_material_plane.py +111 -0
- anyfesolver/general_contact/native_material_s3.py +209 -0
- anyfesolver/general_contact/native_multi_s3_static.py +722 -0
- anyfesolver/general_contact/native_normal_angles.py +233 -0
- anyfesolver/general_contact/native_pair_error_budget.py +49 -0
- anyfesolver/general_contact/native_replay_arithmetic.py +103 -0
- anyfesolver/general_contact/native_replay_diagnostics.py +90 -0
- anyfesolver/general_contact/native_retained_body_junction.py +99 -0
- anyfesolver/general_contact/native_retained_body_pair.py +220 -0
- anyfesolver/general_contact/native_retained_body_projection.py +160 -0
- anyfesolver/general_contact/native_retained_body_static.py +62 -0
- anyfesolver/general_contact/native_retained_cell_pair.py +185 -0
- anyfesolver/general_contact/native_retained_curve_cell.py +102 -0
- anyfesolver/general_contact/native_retained_line_plane.py +210 -0
- anyfesolver/general_contact/native_retained_line_s3.py +440 -0
- anyfesolver/general_contact/native_retained_line_s3_work.py +91 -0
- anyfesolver/general_contact/native_retained_pair_static.py +845 -0
- anyfesolver/general_contact/native_retained_s3_static.py +554 -0
- anyfesolver/general_contact/native_retained_static.py +478 -0
- anyfesolver/general_contact/native_retained_station.py +222 -0
- anyfesolver/general_contact/native_retained_whole_pair.py +2120 -0
- anyfesolver/general_contact/native_s3_swept.py +149 -0
- anyfesolver/general_contact/native_shell_static_driver.py +144 -0
- anyfesolver/general_contact/native_surface_activation.py +391 -0
- anyfesolver/general_contact/native_surface_bounds.py +198 -0
- anyfesolver/general_contact/native_surface_contributor.py +121 -0
- anyfesolver/general_contact/native_surface_crossing.py +81 -0
- anyfesolver/general_contact/native_surface_event_sequence.py +320 -0
- anyfesolver/general_contact/native_surface_extrema.py +72 -0
- anyfesolver/general_contact/native_surface_friction_integral.py +1724 -0
- anyfesolver/general_contact/native_surface_front.py +742 -0
- anyfesolver/general_contact/native_surface_front_boundary.py +160 -0
- anyfesolver/general_contact/native_surface_front_precision.py +78 -0
- anyfesolver/general_contact/native_surface_front_work.py +118 -0
- anyfesolver/general_contact/native_surface_history.py +334 -0
- anyfesolver/general_contact/native_surface_normal.py +324 -0
- anyfesolver/general_contact/native_surface_patch.py +213 -0
- anyfesolver/general_contact/native_surface_path.py +348 -0
- anyfesolver/general_contact/native_surface_quadrature.py +212 -0
- anyfesolver/general_contact/native_surface_s3_friction.py +340 -0
- anyfesolver/general_contact/native_surface_support.py +776 -0
- anyfesolver/general_contact/native_surface_transaction.py +198 -0
- anyfesolver/general_contact/native_transient_multi_shell.py +108 -0
- anyfesolver/general_contact/native_transient_pair.py +61 -0
- anyfesolver/general_contact/native_transient_shell.py +44 -0
- anyfesolver/general_contact/normal_s3_skin_path.py +163 -0
- anyfesolver/general_contact/overlap.py +443 -0
- anyfesolver/general_contact/pair.py +466 -0
- anyfesolver/general_contact/patch_broadphase.py +111 -0
- anyfesolver/general_contact/patch_mortar.py +593 -0
- anyfesolver/general_contact/patch_sliding.py +644 -0
- anyfesolver/general_contact/plane_newmark_events.py +278 -0
- anyfesolver/general_contact/pointwise_patch.py +288 -0
- anyfesolver/general_contact/polynomial_roots.py +312 -0
- anyfesolver/general_contact/pressure_basis.py +241 -0
- anyfesolver/general_contact/pressure_diagnostics.py +155 -0
- anyfesolver/general_contact/pressure_overlay.py +182 -0
- anyfesolver/general_contact/pressure_space.py +51 -0
- anyfesolver/general_contact/q4_material_checkpoint.py +276 -0
- anyfesolver/general_contact/q4_overlap_local_bound.py +1111 -0
- anyfesolver/general_contact/q4_q4_overlap_derivatives.py +234 -0
- anyfesolver/general_contact/q4_q4_swept_bound.py +58 -0
- anyfesolver/general_contact/q4_s3_overlap_derivatives.py +1159 -0
- anyfesolver/general_contact/q4_s3_tiny_wedge.py +180 -0
- anyfesolver/general_contact/q4_skin_work.py +87 -0
- anyfesolver/general_contact/q4_source_mortar_energy.py +181 -0
- anyfesolver/general_contact/q4_source_overlap.py +401 -0
- anyfesolver/general_contact/q4_target_overlap.py +181 -0
- anyfesolver/general_contact/request_identity.py +192 -0
- anyfesolver/general_contact/rigid_translation.py +199 -0
- anyfesolver/general_contact/runtime.py +27 -0
- anyfesolver/general_contact/s3_friction_chart_interval.py +104 -0
- anyfesolver/general_contact/s3_friction_coverage.py +165 -0
- anyfesolver/general_contact/s3_friction_dyadic_interval.py +39 -0
- anyfesolver/general_contact/s3_friction_history_path.py +111 -0
- anyfesolver/general_contact/s3_friction_mesh_coverage.py +145 -0
- anyfesolver/general_contact/s3_friction_mesh_gap.py +74 -0
- anyfesolver/general_contact/s3_friction_mesh_monolithic.py +150 -0
- anyfesolver/general_contact/s3_friction_monolithic.py +121 -0
- anyfesolver/general_contact/s3_friction_moving_coverage.py +114 -0
- anyfesolver/general_contact/s3_friction_pair.py +237 -0
- anyfesolver/general_contact/s3_friction_point.py +129 -0
- anyfesolver/general_contact/s3_friction_prepared_path.py +254 -0
- anyfesolver/general_contact/s3_friction_replay.py +1094 -0
- anyfesolver/general_contact/s3_friction_rim_path.py +69 -0
- anyfesolver/general_contact/s3_friction_rim_transaction.py +23 -0
- anyfesolver/general_contact/s3_friction_static.py +267 -0
- anyfesolver/general_contact/s3_friction_swept_gap.py +112 -0
- anyfesolver/general_contact/s3_friction_touch.py +94 -0
- anyfesolver/general_contact/s3_friction_weak_pressure.py +66 -0
- anyfesolver/general_contact/s3_overlap_derivatives.py +220 -0
- anyfesolver/general_contact/s3_overlap_hermite_gap.py +180 -0
- anyfesolver/general_contact/s3_pair_static.py +343 -0
- anyfesolver/general_contact/s3_patch_augmented.py +2056 -0
- anyfesolver/general_contact/s3_patch_mortar_energy.py +545 -0
- anyfesolver/general_contact/s3_patch_static.py +1725 -0
- anyfesolver/general_contact/s3_pressure_hermite_bounds.py +395 -0
- anyfesolver/general_contact/s3_pressure_polygon_bounds.py +73 -0
- anyfesolver/general_contact/s3_q4_gap_resolution.py +191 -0
- anyfesolver/general_contact/s3_q4_overlap_derivatives.py +252 -0
- anyfesolver/general_contact/s3_q4_swept.py +56 -0
- anyfesolver/general_contact/s3_skin_work.py +101 -0
- anyfesolver/general_contact/s3_sliding_friction.py +640 -0
- anyfesolver/general_contact/s3_stable_plane_gap.py +43 -0
- anyfesolver/general_contact/seed_path_vertex_interval.py +323 -0
- anyfesolver/general_contact/semismooth.py +133 -0
- anyfesolver/general_contact/semismooth_block.py +147 -0
- anyfesolver/general_contact/semismooth_path.py +871 -0
- anyfesolver/general_contact/shell_pair_hermite_sweep.py +336 -0
- anyfesolver/general_contact/shell_sphere_temporal_bounds.py +305 -0
- anyfesolver/general_contact/skin_geometry.py +106 -0
- anyfesolver/general_contact/sliding.py +349 -0
- anyfesolver/general_contact/sphere.py +691 -0
- anyfesolver/general_contact/sphere_temporal_bounds.py +241 -0
- anyfesolver/general_contact/static_path.py +2608 -0
- anyfesolver/general_contact/static_results_archive.py +140 -0
- anyfesolver/general_contact/structural_state_bridge.py +196 -0
- anyfesolver/general_contact/supported_schur.py +594 -0
- anyfesolver/general_contact/swept_patch_broadphase.py +160 -0
- anyfesolver/general_contact/transient.py +2921 -0
- anyfesolver/general_contact/transient_results_archive.py +491 -0
- anyfesolver/general_contact/triangle_mortar_energy.py +280 -0
- anyfesolver/general_contact/triangle_pair_energy.py +211 -0
- anyfesolver/general_contact/triangulated_rigid.py +94 -0
- anyfesolver/general_contact/triangulated_rigid_static.py +399 -0
- anyfesolver/general_contact/warped_q4_plane.py +185 -0
- anyfesolver/general_contact/warped_q4_skin.py +302 -0
- anyfesolver/general_contact/warped_q4_static.py +331 -0
- anyfesolver/impact_performance.py +273 -0
- anyfesolver/impact_reduced_assembly.py +294 -0
- anyfesolver/imperfections.py +397 -0
- anyfesolver/inertia_relief.py +676 -0
- anyfesolver/initial_field_state.py +54 -0
- anyfesolver/jit_compiler.py +135 -0
- anyfesolver/kernel_warmup.py +228 -0
- anyfesolver/linalg.py +1002 -0
- anyfesolver/marine_contributions.py +634 -0
- anyfesolver/marine_coupled_equilibrium.py +292 -0
- anyfesolver/marine_equilibrium.py +405 -0
- anyfesolver/marine_hydro_coordinates.py +346 -0
- anyfesolver/marine_linear.py +549 -0
- anyfesolver/marine_link.py +306 -0
- anyfesolver/marine_mechanical.py +923 -0
- anyfesolver/marine_mooring.py +533 -0
- anyfesolver/marine_morison.py +331 -0
- anyfesolver/marine_potential_flow.py +403 -0
- anyfesolver/marine_rigid.py +397 -0
- anyfesolver/marine_seabed.py +122 -0
- anyfesolver/mass_properties.py +352 -0
- anyfesolver/material_curves.py +52 -0
- anyfesolver/materials.py +43 -0
- anyfesolver/matrix_assembly.py +7330 -0
- anyfesolver/mesh_gen.py +904 -0
- anyfesolver/mesh_load_bc_verification.py +612 -0
- anyfesolver/modal.py +2494 -0
- anyfesolver/nonlinear.py +341 -0
- anyfesolver/nonlinear_analysis_diagnostics.py +466 -0
- anyfesolver/nonlinear_element_evaluation.py +126 -0
- anyfesolver/nonlinear_performance.py +2094 -0
- anyfesolver/nonlinear_performance_batch_b.py +1173 -0
- anyfesolver/nonlinear_performance_batch_c.py +850 -0
- anyfesolver/nonlinear_performance_bootstrap.py +164 -0
- anyfesolver/nonlinear_reduced_assembly.py +806 -0
- anyfesolver/nonlinear_restart.py +1708 -0
- anyfesolver/nonlinear_state.py +2800 -0
- anyfesolver/nonlinear_static.py +9407 -0
- anyfesolver/outcomes.py +694 -0
- anyfesolver/plasticity.py +2087 -0
- anyfesolver/plasticity_qualification.py +758 -0
- anyfesolver/prepared_static.py +1129 -0
- anyfesolver/prepared_static_envelopes.py +389 -0
- anyfesolver/prepared_static_loads.py +1180 -0
- anyfesolver/prepared_static_measures.py +18 -0
- anyfesolver/prepared_static_numeric.py +247 -0
- anyfesolver/prepared_static_q4.py +297 -0
- anyfesolver/prepared_static_recovery.py +2068 -0
- anyfesolver/prepared_static_response.py +1013 -0
- anyfesolver/prepared_static_shared.py +226 -0
- anyfesolver/prepared_static_snapshot.py +323 -0
- anyfesolver/prepared_static_vectorized.py +866 -0
- anyfesolver/pressure_regions.py +287 -0
- anyfesolver/production_readiness.py +343 -0
- anyfesolver/quality_control.py +1487 -0
- anyfesolver/quantities.py +1289 -0
- anyfesolver/recovery.py +4217 -0
- anyfesolver/recovery_batches.py +1985 -0
- anyfesolver/recovery_policy.py +205 -0
- anyfesolver/recovery_s4.py +289 -0
- anyfesolver/recovery_shell_frames.py +102 -0
- anyfesolver/reference_cases.py +366 -0
- anyfesolver/result_capsule.py +2175 -0
- anyfesolver/results.py +752 -0
- anyfesolver/runtime.py +9819 -0
- anyfesolver/s3_reference_batch.py +1338 -0
- anyfesolver/s3_v2c_fast_assembly.py +544 -0
- anyfesolver/s3_v2d_fast_assembly.py +447 -0
- anyfesolver/s4_validity.py +354 -0
- anyfesolver/sections.py +67 -0
- anyfesolver/sesam_fem/__init__.py +55 -0
- anyfesolver/sesam_fem/__main__.py +80 -0
- anyfesolver/sesam_fem/diagnostics.py +5 -0
- anyfesolver/sesam_fem/document.py +39 -0
- anyfesolver/sesam_fem/exporter.py +9 -0
- anyfesolver/sesam_fem/importer.py +228 -0
- anyfesolver/sesam_fem/records.py +21 -0
- anyfesolver/sesam_fem/schema.py +35 -0
- anyfesolver/sesam_fem/sif_importer.py +9 -0
- anyfesolver/sesam_fem/validation.py +5 -0
- anyfesolver/sestra_deep_diagnostics.py +173 -0
- anyfesolver/sestra_mixed_verification.py +146 -0
- anyfesolver/sestra_structure_benchmarks.py +270 -0
- anyfesolver/sestra_three_case.py +215 -0
- anyfesolver/sestra_verification.py +523 -0
- anyfesolver/shell_benchmarks.py +367 -0
- anyfesolver/shell_formulations/director_field.py +799 -0
- anyfesolver/shell_formulations/geometry_provenance.py +921 -0
- anyfesolver/shell_formulations/mitc4_plus_d_quality.py +318 -0
- anyfesolver/shell_formulations/mitc4_plus_d_reference.py +837 -0
- anyfesolver/shell_formulations/mitc4_plus_d_scalar.py +209 -0
- anyfesolver/shell_formulations/protocol.py +222 -0
- anyfesolver/shell_formulations/q4_common.py +271 -0
- anyfesolver/shell_formulations/s4_restricted_policy.py +78 -0
- anyfesolver/shell_sections.py +394 -0
- anyfesolver/solid_submodel.py +930 -0
- anyfesolver/static_batch.py +662 -0
- anyfesolver/static_envelopes.py +250 -0
- anyfesolver/stiffened_shell.py +388 -0
- anyfesolver/submodel_transfer.py +1306 -0
- anyfesolver/test_cases.py +610 -0
- anyfesolver/threading_policy.py +431 -0
- anyfesolver/validation.py +719 -0
- anyfesolver/vectorized_beam.py +139 -0
- anyfesolver/vectorized_generalized_shell.py +448 -0
- anyfesolver/vectorized_hill48.py +956 -0
- anyfesolver/vectorized_nonlinear.py +360 -0
- anyfesolver/vectorized_stiffness.py +694 -0
- anyfesolver-0.5.0a1.dist-info/METADATA +374 -0
- anyfesolver-0.5.0a1.dist-info/RECORD +751 -0
- anyfesolver-0.5.0a1.dist-info/WHEEL +5 -0
- anyfesolver-0.5.0a1.dist-info/licenses/COPYRIGHT +1 -0
- anyfesolver-0.5.0a1.dist-info/licenses/LICENSE +373 -0
- anyfesolver-0.5.0a1.dist-info/licenses/THIRD_PARTY_NOTICES.md +32 -0
- anyfesolver-0.5.0a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1936 @@
|
|
|
1
|
+
"""Strict flat-linear E4-PL S3 V2B production candidate.
|
|
2
|
+
|
|
3
|
+
This module deliberately implements only the bounded flat-linear V5B surface:
|
|
4
|
+
a flat, small-strain, homogeneous isotropic elastic triangle with CST membrane,
|
|
5
|
+
source-authorized relaxed MIN3 bending/shear, exact three-point Hammer
|
|
6
|
+
integration and the barycentric PL drill completion. Its only mixed-model admission is an exact,
|
|
7
|
+
globally coplanar qualified-Q4/V2B mesh with one positively aligned physical
|
|
8
|
+
director. Every inherited shell capability outside that surface fails closed
|
|
9
|
+
rather than falling back to legacy TRI3 mechanics.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import math
|
|
15
|
+
import threading
|
|
16
|
+
import weakref
|
|
17
|
+
from types import MappingProxyType
|
|
18
|
+
from typing import Any, Dict, Mapping, Optional, Sequence, Tuple
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from .element_capabilities import ElementCapabilityError
|
|
23
|
+
from .elements import ShellElement
|
|
24
|
+
from .e4_pl_element import (
|
|
25
|
+
FORMULATION_ID as _QUALIFIED_Q4_FORMULATION_ID,
|
|
26
|
+
QualifiedE4PLShellElement,
|
|
27
|
+
)
|
|
28
|
+
from .fe_core import FEMesh, Material, Node
|
|
29
|
+
from .e4_pl_s3_state import require_exact_numpy_runtime_authority
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
SELECTOR = "e4-pl-s3-v2b"
|
|
33
|
+
FORMULATION_ID = "CANDIDATE_E4_PL_S3_V2B_FLAT_LINEAR_V1"
|
|
34
|
+
IMPLEMENTATION_ID = "E4_PL_S3_V2B_MIN3_RELAXED_UHM_CST_PL_HAMMER3_V1"
|
|
35
|
+
FORMULATION_SCHEMA = "anysolver.e4-pl-s3-v2b-strict-flat-linear-element-v1"
|
|
36
|
+
SOURCE_CONTRACT_SCHEMA = "anysolver.e4-pl-s3-v5b-relaxation-source-selection-v1"
|
|
37
|
+
SOURCE_CONTRACT_SHA256 = (
|
|
38
|
+
"0AE9DAA05B63A43D456423BCDC676E7421AB3583F152EE5DB3D0E36FE60A17A0"
|
|
39
|
+
)
|
|
40
|
+
EQUATION_MAP_SHA256 = (
|
|
41
|
+
"C322711E0B395F578EEBB8DEE8D48DFF955DCBF0EC4A12ED2B975F2B4F87C789"
|
|
42
|
+
)
|
|
43
|
+
PRIMARY_SOURCE_SHA256 = (
|
|
44
|
+
"54A310C93E7BF140684B7B48D7C6416D99758804E648FB62B2C954150E853174"
|
|
45
|
+
)
|
|
46
|
+
RELAXATION_AUTHORITY_SHA256 = (
|
|
47
|
+
"0AE9DAA05B63A43D456423BCDC676E7421AB3583F152EE5DB3D0E36FE60A17A0"
|
|
48
|
+
)
|
|
49
|
+
QUADRATURE_AUTHORITY_ID = "S3_V2B_MIN3_HAMMER3_DEGREE2_EXACT_V1"
|
|
50
|
+
PL_COMPLETION_POLICY_ID = "S3_V2_BARYCENTRIC_EXACT_SCHUR_KD_EQUALS_A66_V1"
|
|
51
|
+
RESULTANT_POLICY_ID = "SHELL_VARIATIONAL_RESULTANTS_V1"
|
|
52
|
+
DIRECTOR_POLICY_ID = "S3_V2_FIXED_PHYSICAL_DIRECTOR_D3_BLOCK_TRANSPORT_V1"
|
|
53
|
+
SECTION_POLICY_ID = "S3_V2_HOMOGENEOUS_ISOTROPIC_UNCOUPLED_ZERO_OFFSET_V1"
|
|
54
|
+
SHEAR_CORRECTION = 5.0 / 6.0
|
|
55
|
+
CBMIN3 = 2.0
|
|
56
|
+
_ROTATIONAL_INDICES = np.frombuffer(
|
|
57
|
+
np.asarray((3, 4, 9, 10, 15, 16), dtype=np.intp).tobytes(),
|
|
58
|
+
dtype=np.intp,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
_NORMAL_TOLERANCE = 1.0e-10
|
|
62
|
+
_DEGENERACY_FACTOR = 64.0 * np.finfo(np.float64).eps
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class StrictFlatLinearCapabilityError(ElementCapabilityError):
|
|
66
|
+
"""The requested operation is outside the authorized S3 V2 surface."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _immutable_float64_array(value: Any) -> np.ndarray:
|
|
70
|
+
contiguous = np.ascontiguousarray(value, dtype=np.float64)
|
|
71
|
+
return np.frombuffer(
|
|
72
|
+
contiguous.tobytes(order="C"),
|
|
73
|
+
dtype=np.float64,
|
|
74
|
+
).reshape(contiguous.shape)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# Natural coordinates are (xi, eta), with lambda = 1 - xi - eta. The same
|
|
78
|
+
# three positive physical weights A/3 are used for membrane, bending and shear.
|
|
79
|
+
HAMMER_POINTS = _immutable_float64_array(
|
|
80
|
+
(
|
|
81
|
+
(1.0 / 6.0, 1.0 / 6.0),
|
|
82
|
+
(2.0 / 3.0, 1.0 / 6.0),
|
|
83
|
+
(1.0 / 6.0, 2.0 / 3.0),
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
HAMMER_REFERENCE_WEIGHTS = _immutable_float64_array(
|
|
87
|
+
(1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0)
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
SUPPORTED_OPERATIONS = frozenset(
|
|
91
|
+
{
|
|
92
|
+
"linear_stiffness",
|
|
93
|
+
"linear_internal_force",
|
|
94
|
+
"raw_variational_resultants",
|
|
95
|
+
"dead_transverse_pressure",
|
|
96
|
+
"flat_qualified_q4_v2a_mixed_mesh",
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
BLOCKED_OPERATIONS = frozenset(
|
|
100
|
+
{
|
|
101
|
+
"consistent_mass",
|
|
102
|
+
"geometric_stiffness",
|
|
103
|
+
"nonlinear_geometry",
|
|
104
|
+
"material_nonlinearity",
|
|
105
|
+
"unqualified_mixed_element_mesh",
|
|
106
|
+
"nonlinear_state",
|
|
107
|
+
"qualified_recovery",
|
|
108
|
+
"serialization",
|
|
109
|
+
"restart",
|
|
110
|
+
"follower_pressure",
|
|
111
|
+
"distributed_couple",
|
|
112
|
+
"offset_load",
|
|
113
|
+
"generalized_section",
|
|
114
|
+
"curved_shell",
|
|
115
|
+
"nonplanar_mixed_shell_mesh",
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
CAPABILITY_MATRIX = MappingProxyType(
|
|
119
|
+
{
|
|
120
|
+
**{name: "SUPPORTED_STRICT_FLAT_LINEAR" for name in SUPPORTED_OPERATIONS},
|
|
121
|
+
**{name: "BLOCKED_OUTSIDE_STAGE1_AUTHORITY" for name in BLOCKED_OPERATIONS},
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _real_scalar(value: Any, label: str) -> float:
|
|
127
|
+
if isinstance(value, (bool, np.bool_)) or not isinstance(
|
|
128
|
+
value,
|
|
129
|
+
(int, float, np.integer, np.floating),
|
|
130
|
+
):
|
|
131
|
+
raise TypeError(f"strict-flat S3 V2 {label} must be a finite real scalar")
|
|
132
|
+
made = float(value)
|
|
133
|
+
if not math.isfinite(made):
|
|
134
|
+
raise ValueError(f"strict-flat S3 V2 {label} must be finite")
|
|
135
|
+
return made
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _unit_vector(value: Any, label: str) -> np.ndarray:
|
|
139
|
+
if type(value) in {list, tuple} and any(
|
|
140
|
+
isinstance(component, (bool, np.bool_))
|
|
141
|
+
or not isinstance(component, (int, float, np.integer, np.floating))
|
|
142
|
+
for component in value
|
|
143
|
+
):
|
|
144
|
+
raise ValueError(f"strict-flat S3 V2 {label} must be a finite 3-vector")
|
|
145
|
+
try:
|
|
146
|
+
vector = np.asarray(value, dtype=np.float64).reshape(-1)
|
|
147
|
+
except (TypeError, ValueError) as exc:
|
|
148
|
+
raise ValueError(
|
|
149
|
+
f"strict-flat S3 V2 {label} must be a finite 3-vector"
|
|
150
|
+
) from exc
|
|
151
|
+
if vector.size != 3 or not np.all(np.isfinite(vector)):
|
|
152
|
+
raise ValueError(f"strict-flat S3 V2 {label} must be a finite 3-vector")
|
|
153
|
+
norm = float(np.linalg.norm(vector))
|
|
154
|
+
if not math.isfinite(norm) or norm <= 0.0:
|
|
155
|
+
raise ValueError(f"strict-flat S3 V2 {label} must be nonzero")
|
|
156
|
+
return _immutable_float64_array(vector / norm)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _block_permutation(order: Tuple[int, int, int]) -> np.ndarray:
|
|
160
|
+
permutation = np.zeros((18, 18), dtype=np.float64)
|
|
161
|
+
identity = np.eye(6, dtype=np.float64)
|
|
162
|
+
for internal, external in enumerate(order):
|
|
163
|
+
permutation[
|
|
164
|
+
6 * internal : 6 * internal + 6,
|
|
165
|
+
6 * external : 6 * external + 6,
|
|
166
|
+
] = identity
|
|
167
|
+
return permutation
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _local_component_transform(frame: np.ndarray) -> np.ndarray:
|
|
171
|
+
transform = np.zeros((18, 18), dtype=np.float64)
|
|
172
|
+
local_from_global = np.asarray(frame, dtype=np.float64).T
|
|
173
|
+
for node in range(3):
|
|
174
|
+
base = 6 * node
|
|
175
|
+
transform[base : base + 3, base : base + 3] = local_from_global
|
|
176
|
+
transform[base + 3 : base + 6, base + 3 : base + 6] = (
|
|
177
|
+
local_from_global
|
|
178
|
+
)
|
|
179
|
+
return transform
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _plate_embedding() -> np.ndarray:
|
|
183
|
+
"""Map local shell q to printed [w,beta_x,beta_y] node blocks."""
|
|
184
|
+
|
|
185
|
+
embedding = np.zeros((9, 18), dtype=np.float64)
|
|
186
|
+
for node in range(3):
|
|
187
|
+
shell = 6 * node
|
|
188
|
+
plate = 3 * node
|
|
189
|
+
embedding[plate, shell + 2] = 1.0
|
|
190
|
+
embedding[plate + 1, shell + 4] = 1.0
|
|
191
|
+
embedding[plate + 2, shell + 3] = -1.0
|
|
192
|
+
return embedding
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
_PLATE_EMBEDDING = _immutable_float64_array(_plate_embedding())
|
|
196
|
+
_LAYOUT_SENTINEL = object()
|
|
197
|
+
_MIXED_SCOPE_CACHE_SENTINEL = object()
|
|
198
|
+
_MIXED_SCOPE_CACHE_NAME = "_strict_flat_v2b_mixed_scope_cache_v1"
|
|
199
|
+
_COMPONENT_CACHE_SCHEMA = "strict-flat-s3-v2b-exact-component-cache-v1"
|
|
200
|
+
_COMPONENT_CACHE_ENTRY_SCHEMA = (
|
|
201
|
+
"strict-flat-s3-v2b-exact-component-cache-entry-v1"
|
|
202
|
+
)
|
|
203
|
+
_COMPONENT_CACHE_CAPACITY = 512
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _make_exact_component_compute_wrapper() -> Any:
|
|
207
|
+
"""Create the sole cache surface with all mutable state closure-owned."""
|
|
208
|
+
|
|
209
|
+
entries: Dict[Tuple[Any, ...], Tuple[Any, ...]] = {}
|
|
210
|
+
missing = object()
|
|
211
|
+
lock = threading.RLock()
|
|
212
|
+
float64 = np.dtype(np.float64)
|
|
213
|
+
float64_name = float64.str
|
|
214
|
+
matrix_names = (
|
|
215
|
+
"membrane",
|
|
216
|
+
"bending",
|
|
217
|
+
"shear",
|
|
218
|
+
"physical",
|
|
219
|
+
"pl",
|
|
220
|
+
"total",
|
|
221
|
+
)
|
|
222
|
+
packed_shapes = {
|
|
223
|
+
**{name: (18, 18) for name in matrix_names},
|
|
224
|
+
"frame": (3, 3),
|
|
225
|
+
"phi": (3,),
|
|
226
|
+
}
|
|
227
|
+
packed_names = (*matrix_names, "frame", "phi")
|
|
228
|
+
output_names = (
|
|
229
|
+
"membrane",
|
|
230
|
+
"bending",
|
|
231
|
+
"shear",
|
|
232
|
+
"physical",
|
|
233
|
+
"pl",
|
|
234
|
+
"numerical",
|
|
235
|
+
"hourglass",
|
|
236
|
+
"total",
|
|
237
|
+
"frame",
|
|
238
|
+
"area",
|
|
239
|
+
"phi",
|
|
240
|
+
"phi_squared",
|
|
241
|
+
"quadrature_authority_id",
|
|
242
|
+
"pl_completion_policy_id",
|
|
243
|
+
"relaxation_authority_sha256",
|
|
244
|
+
)
|
|
245
|
+
zero_matrix_payload = np.zeros((18, 18), dtype=np.float64).tobytes(order="C")
|
|
246
|
+
|
|
247
|
+
def fail(label: str) -> None:
|
|
248
|
+
raise StrictFlatLinearCapabilityError(
|
|
249
|
+
f"strict-flat S3 V2 exact component cache {label}"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def array_identity(
|
|
253
|
+
value: Any,
|
|
254
|
+
shape: Tuple[int, ...],
|
|
255
|
+
label: str,
|
|
256
|
+
) -> Tuple[Any, ...]:
|
|
257
|
+
if (
|
|
258
|
+
type(value) is not np.ndarray
|
|
259
|
+
or value.dtype != float64
|
|
260
|
+
or value.shape != shape
|
|
261
|
+
or not np.all(np.isfinite(value))
|
|
262
|
+
):
|
|
263
|
+
fail(f"{label} array is malformed")
|
|
264
|
+
return (float64_name, shape, value.tobytes(order="C"))
|
|
265
|
+
|
|
266
|
+
def scalar_identity(value: Any, label: str) -> bytes:
|
|
267
|
+
if type(value) is not float or not math.isfinite(value) or value <= 0.0:
|
|
268
|
+
fail(f"{label} scalar is malformed")
|
|
269
|
+
return np.float64(value).tobytes()
|
|
270
|
+
|
|
271
|
+
def cache_key(
|
|
272
|
+
geometry: Mapping[str, Any],
|
|
273
|
+
constitutive: Mapping[str, Any],
|
|
274
|
+
) -> Tuple[Any, ...]:
|
|
275
|
+
return (
|
|
276
|
+
_COMPONENT_CACHE_SCHEMA,
|
|
277
|
+
FORMULATION_ID,
|
|
278
|
+
IMPLEMENTATION_ID,
|
|
279
|
+
SOURCE_CONTRACT_SHA256,
|
|
280
|
+
RELAXATION_AUTHORITY_SHA256,
|
|
281
|
+
EQUATION_MAP_SHA256,
|
|
282
|
+
QUADRATURE_AUTHORITY_ID,
|
|
283
|
+
PL_COMPLETION_POLICY_ID,
|
|
284
|
+
array_identity(
|
|
285
|
+
geometry["local_coordinates"], (3, 2), "local-coordinate"
|
|
286
|
+
),
|
|
287
|
+
array_identity(
|
|
288
|
+
geometry["shape_gradients"], (3, 2), "shape-gradient"
|
|
289
|
+
),
|
|
290
|
+
scalar_identity(geometry["area"], "area"),
|
|
291
|
+
array_identity(geometry["frame"], (3, 3), "frame"),
|
|
292
|
+
array_identity(
|
|
293
|
+
geometry["local_from_external"],
|
|
294
|
+
(18, 18),
|
|
295
|
+
"external transform",
|
|
296
|
+
),
|
|
297
|
+
array_identity(constitutive["A"], (3, 3), "membrane constitutive"),
|
|
298
|
+
array_identity(constitutive["D"], (3, 3), "bending constitutive"),
|
|
299
|
+
array_identity(constitutive["H"], (2, 2), "shear constitutive"),
|
|
300
|
+
scalar_identity(constitutive["bending_scalar"], "bending"),
|
|
301
|
+
scalar_identity(constitutive["shear_scalar"], "shear"),
|
|
302
|
+
scalar_identity(constitutive["drill_scale"], "drill"),
|
|
303
|
+
array_identity(HAMMER_POINTS, (3, 2), "Hammer point"),
|
|
304
|
+
array_identity(HAMMER_REFERENCE_WEIGHTS, (3,), "Hammer weight"),
|
|
305
|
+
array_identity(_PLATE_EMBEDDING, (9, 18), "plate embedding"),
|
|
306
|
+
scalar_identity(CBMIN3, "CBMIN3"),
|
|
307
|
+
tuple(int(value) for value in _ROTATIONAL_INDICES),
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def pack_result(result: Mapping[str, Any]) -> Tuple[Any, ...]:
|
|
311
|
+
if type(result) is not dict or tuple(result) != output_names:
|
|
312
|
+
fail("result schema or order differs")
|
|
313
|
+
packed = tuple(
|
|
314
|
+
(
|
|
315
|
+
name,
|
|
316
|
+
*array_identity(result[name], packed_shapes[name], name),
|
|
317
|
+
)
|
|
318
|
+
for name in packed_names
|
|
319
|
+
)
|
|
320
|
+
pl_payload = packed[packed_names.index("pl")][-1]
|
|
321
|
+
numerical = array_identity(result["numerical"], (18, 18), "numerical")
|
|
322
|
+
hourglass = array_identity(result["hourglass"], (18, 18), "hourglass")
|
|
323
|
+
if numerical[-1] != pl_payload or hourglass[-1] != zero_matrix_payload:
|
|
324
|
+
fail("numerical component identity differs")
|
|
325
|
+
if (
|
|
326
|
+
result["quadrature_authority_id"] != QUADRATURE_AUTHORITY_ID
|
|
327
|
+
or result["pl_completion_policy_id"] != PL_COMPLETION_POLICY_ID
|
|
328
|
+
or result["relaxation_authority_sha256"]
|
|
329
|
+
!= RELAXATION_AUTHORITY_SHA256
|
|
330
|
+
):
|
|
331
|
+
fail("policy identity differs")
|
|
332
|
+
return (
|
|
333
|
+
_COMPONENT_CACHE_ENTRY_SCHEMA,
|
|
334
|
+
packed,
|
|
335
|
+
scalar_identity(result["area"], "result area"),
|
|
336
|
+
scalar_identity(result["phi_squared"], "MIN3 relaxation"),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def unpack_result(record: Tuple[Any, ...]) -> Dict[str, Any]:
|
|
340
|
+
if (
|
|
341
|
+
type(record) is not tuple
|
|
342
|
+
or len(record) != 4
|
|
343
|
+
or record[0] != _COMPONENT_CACHE_ENTRY_SCHEMA
|
|
344
|
+
or type(record[1]) is not tuple
|
|
345
|
+
or len(record[1]) != len(packed_names)
|
|
346
|
+
or type(record[2]) is not bytes
|
|
347
|
+
or len(record[2]) != float64.itemsize
|
|
348
|
+
or type(record[3]) is not bytes
|
|
349
|
+
or len(record[3]) != float64.itemsize
|
|
350
|
+
):
|
|
351
|
+
fail("packed entry schema differs")
|
|
352
|
+
arrays: Dict[str, np.ndarray] = {}
|
|
353
|
+
for expected_name, expected_shape, packed in zip(
|
|
354
|
+
packed_names,
|
|
355
|
+
(packed_shapes[name] for name in packed_names),
|
|
356
|
+
record[1],
|
|
357
|
+
):
|
|
358
|
+
if (
|
|
359
|
+
type(packed) is not tuple
|
|
360
|
+
or len(packed) != 4
|
|
361
|
+
or packed[0] != expected_name
|
|
362
|
+
or packed[1] != float64_name
|
|
363
|
+
or packed[2] != expected_shape
|
|
364
|
+
or type(packed[3]) is not bytes
|
|
365
|
+
or len(packed[3]) != math.prod(expected_shape) * float64.itemsize
|
|
366
|
+
):
|
|
367
|
+
fail("packed array schema differs")
|
|
368
|
+
array = np.frombuffer(packed[3], dtype=float64).reshape(expected_shape)
|
|
369
|
+
if not np.all(np.isfinite(array)):
|
|
370
|
+
fail("packed array is nonfinite")
|
|
371
|
+
arrays[expected_name] = array.copy()
|
|
372
|
+
area = float(np.frombuffer(record[2], dtype=float64, count=1)[0])
|
|
373
|
+
if not math.isfinite(area) or area <= 0.0:
|
|
374
|
+
fail("packed area is malformed")
|
|
375
|
+
phi_squared = float(np.frombuffer(record[3], dtype=float64, count=1)[0])
|
|
376
|
+
if not math.isfinite(phi_squared) or not 0.0 < phi_squared <= 1.0:
|
|
377
|
+
fail("packed MIN3 relaxation is malformed")
|
|
378
|
+
pl = arrays["pl"]
|
|
379
|
+
return {
|
|
380
|
+
"membrane": arrays["membrane"],
|
|
381
|
+
"bending": arrays["bending"],
|
|
382
|
+
"shear": arrays["shear"],
|
|
383
|
+
"physical": arrays["physical"],
|
|
384
|
+
"pl": pl,
|
|
385
|
+
"numerical": pl.copy(),
|
|
386
|
+
"hourglass": np.zeros((18, 18), dtype=np.float64),
|
|
387
|
+
"total": arrays["total"],
|
|
388
|
+
"frame": arrays["frame"],
|
|
389
|
+
"area": area,
|
|
390
|
+
"phi": arrays["phi"],
|
|
391
|
+
"phi_squared": phi_squared,
|
|
392
|
+
"quadrature_authority_id": QUADRATURE_AUTHORITY_ID,
|
|
393
|
+
"pl_completion_policy_id": PL_COMPLETION_POLICY_ID,
|
|
394
|
+
"relaxation_authority_sha256": RELAXATION_AUTHORITY_SHA256,
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
def calculate(
|
|
398
|
+
self: Any,
|
|
399
|
+
geometry: Mapping[str, Any],
|
|
400
|
+
constitutive: Mapping[str, Any],
|
|
401
|
+
) -> Dict[str, Any]:
|
|
402
|
+
operators = self._operators(geometry, constitutive)
|
|
403
|
+
area = float(geometry["area"])
|
|
404
|
+
physical_weight = area / 3.0
|
|
405
|
+
membrane_local = (
|
|
406
|
+
operators["B_m"].T
|
|
407
|
+
@ constitutive["A"]
|
|
408
|
+
@ operators["B_m"]
|
|
409
|
+
* area
|
|
410
|
+
)
|
|
411
|
+
# MIN3 curvature is constant for the linear triangle. Form it once,
|
|
412
|
+
# matching the accepted V5A/V5B source equation and avoiding a
|
|
413
|
+
# numerically different three-way accumulation of identical terms.
|
|
414
|
+
bending_operator = operators["B_b"][0]
|
|
415
|
+
bending_local = (
|
|
416
|
+
bending_operator.T
|
|
417
|
+
@ constitutive["D"]
|
|
418
|
+
@ bending_operator
|
|
419
|
+
* area
|
|
420
|
+
)
|
|
421
|
+
shear_local = np.zeros((18, 18), dtype=np.float64)
|
|
422
|
+
for shear_operator in operators["B_s"]:
|
|
423
|
+
shear_local += (
|
|
424
|
+
shear_operator.T
|
|
425
|
+
@ constitutive["H"]
|
|
426
|
+
@ shear_operator
|
|
427
|
+
* physical_weight
|
|
428
|
+
)
|
|
429
|
+
bending_sum = float(
|
|
430
|
+
sum(bending_local[index, index] for index in _ROTATIONAL_INDICES)
|
|
431
|
+
)
|
|
432
|
+
unrelaxed_shear_sum = float(
|
|
433
|
+
sum(shear_local[index, index] for index in _ROTATIONAL_INDICES)
|
|
434
|
+
)
|
|
435
|
+
if (
|
|
436
|
+
not math.isfinite(bending_sum)
|
|
437
|
+
or not math.isfinite(unrelaxed_shear_sum)
|
|
438
|
+
or bending_sum <= 0.0
|
|
439
|
+
or unrelaxed_shear_sum <= 0.0
|
|
440
|
+
):
|
|
441
|
+
fail("MIN3 relaxation diagonal sums are invalid")
|
|
442
|
+
psi_hat = bending_sum / unrelaxed_shear_sum
|
|
443
|
+
phi_squared = CBMIN3 * psi_hat / (1.0 + CBMIN3 * psi_hat)
|
|
444
|
+
if not math.isfinite(phi_squared) or not 0.0 < phi_squared <= 1.0:
|
|
445
|
+
fail("MIN3 relaxation factor is invalid")
|
|
446
|
+
shear_local *= phi_squared
|
|
447
|
+
barycentric_mass = (area / 12.0) * np.asarray(
|
|
448
|
+
((2.0, 1.0, 1.0), (1.0, 2.0, 1.0), (1.0, 1.0, 2.0)),
|
|
449
|
+
dtype=np.float64,
|
|
450
|
+
)
|
|
451
|
+
constraint = operators["C"]
|
|
452
|
+
pl_local = (
|
|
453
|
+
float(constitutive["drill_scale"])
|
|
454
|
+
* constraint.T
|
|
455
|
+
@ barycentric_mass
|
|
456
|
+
@ constraint
|
|
457
|
+
)
|
|
458
|
+
physical_local = membrane_local + bending_local + shear_local
|
|
459
|
+
total_local = physical_local + pl_local
|
|
460
|
+
transform = np.asarray(geometry["local_from_external"], dtype=np.float64)
|
|
461
|
+
|
|
462
|
+
def globalize(matrix: np.ndarray) -> np.ndarray:
|
|
463
|
+
made = transform.T @ matrix @ transform
|
|
464
|
+
return 0.5 * (made + made.T)
|
|
465
|
+
|
|
466
|
+
membrane = globalize(membrane_local)
|
|
467
|
+
bending = globalize(bending_local)
|
|
468
|
+
shear = globalize(shear_local)
|
|
469
|
+
physical = globalize(physical_local)
|
|
470
|
+
pl = globalize(pl_local)
|
|
471
|
+
total = globalize(total_local)
|
|
472
|
+
return {
|
|
473
|
+
"membrane": membrane,
|
|
474
|
+
"bending": bending,
|
|
475
|
+
"shear": shear,
|
|
476
|
+
"physical": physical,
|
|
477
|
+
"pl": pl,
|
|
478
|
+
"numerical": pl.copy(),
|
|
479
|
+
"hourglass": np.zeros((18, 18), dtype=np.float64),
|
|
480
|
+
"total": total,
|
|
481
|
+
"frame": np.asarray(geometry["frame"], dtype=np.float64).copy(),
|
|
482
|
+
"area": area,
|
|
483
|
+
"phi": np.asarray(operators["phi"], dtype=np.float64).copy(),
|
|
484
|
+
"phi_squared": phi_squared,
|
|
485
|
+
"quadrature_authority_id": QUADRATURE_AUTHORITY_ID,
|
|
486
|
+
"pl_completion_policy_id": PL_COMPLETION_POLICY_ID,
|
|
487
|
+
"relaxation_authority_sha256": RELAXATION_AUTHORITY_SHA256,
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
def compute_stiffness_components(
|
|
491
|
+
self: Any,
|
|
492
|
+
mesh: FEMesh,
|
|
493
|
+
material: Material,
|
|
494
|
+
) -> Mapping[str, Any]:
|
|
495
|
+
# Admission and all live instance/model/material guards deliberately run
|
|
496
|
+
# before any cache observation. Only exact validated numeric inputs
|
|
497
|
+
# enter the closure-owned key.
|
|
498
|
+
geometry = self._geometry(mesh)
|
|
499
|
+
constitutive = self._constitutive(material)
|
|
500
|
+
key = cache_key(geometry, constitutive)
|
|
501
|
+
with lock:
|
|
502
|
+
cached = entries.get(key, missing)
|
|
503
|
+
if cached is not missing:
|
|
504
|
+
return unpack_result(cached)
|
|
505
|
+
|
|
506
|
+
components = calculate(self, geometry, constitutive)
|
|
507
|
+
packed = pack_result(components)
|
|
508
|
+
with lock:
|
|
509
|
+
existing = entries.get(key, missing)
|
|
510
|
+
if existing is not missing:
|
|
511
|
+
if existing != packed:
|
|
512
|
+
fail("concurrent result disagreement")
|
|
513
|
+
else:
|
|
514
|
+
entries[key] = packed
|
|
515
|
+
if len(entries) > _COMPONENT_CACHE_CAPACITY:
|
|
516
|
+
# Retain the lexicographically smallest exact content keys;
|
|
517
|
+
# the final cache set is independent of insertion/thread order.
|
|
518
|
+
del entries[max(entries)]
|
|
519
|
+
return components
|
|
520
|
+
|
|
521
|
+
compute_stiffness_components.__qualname__ = (
|
|
522
|
+
"StrictFlatLinearE4PLS3V2BShellElement.compute_stiffness_components"
|
|
523
|
+
)
|
|
524
|
+
return compute_stiffness_components
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _validate_module_authority(
|
|
528
|
+
_expected_hammer_points: np.ndarray = HAMMER_POINTS,
|
|
529
|
+
_expected_hammer_weights: np.ndarray = HAMMER_REFERENCE_WEIGHTS,
|
|
530
|
+
_expected_plate_embedding: np.ndarray = _PLATE_EMBEDDING,
|
|
531
|
+
_expected_rotational_indices: np.ndarray = _ROTATIONAL_INDICES,
|
|
532
|
+
_expected_supported: frozenset[str] = SUPPORTED_OPERATIONS,
|
|
533
|
+
_expected_blocked: frozenset[str] = BLOCKED_OPERATIONS,
|
|
534
|
+
_expected_capabilities: Mapping[str, str] = CAPABILITY_MATRIX,
|
|
535
|
+
_expected_shear_correction: float = SHEAR_CORRECTION,
|
|
536
|
+
_expected_cbmin3: float = CBMIN3,
|
|
537
|
+
_expected_normal_tolerance: float = _NORMAL_TOLERANCE,
|
|
538
|
+
_expected_degeneracy_factor: float = _DEGENERACY_FACTOR,
|
|
539
|
+
_expected_selector: str = SELECTOR,
|
|
540
|
+
_expected_formulation_id: str = FORMULATION_ID,
|
|
541
|
+
_expected_implementation_id: str = IMPLEMENTATION_ID,
|
|
542
|
+
_expected_formulation_schema: str = FORMULATION_SCHEMA,
|
|
543
|
+
_expected_source_schema: str = SOURCE_CONTRACT_SCHEMA,
|
|
544
|
+
_expected_source_hash: str = SOURCE_CONTRACT_SHA256,
|
|
545
|
+
_expected_equation_hash: str = EQUATION_MAP_SHA256,
|
|
546
|
+
_expected_primary_hash: str = PRIMARY_SOURCE_SHA256,
|
|
547
|
+
_expected_relaxation_hash: str = RELAXATION_AUTHORITY_SHA256,
|
|
548
|
+
_expected_quadrature_id: str = QUADRATURE_AUTHORITY_ID,
|
|
549
|
+
_expected_pl_id: str = PL_COMPLETION_POLICY_ID,
|
|
550
|
+
_expected_resultant_id: str = RESULTANT_POLICY_ID,
|
|
551
|
+
_expected_director_id: str = DIRECTOR_POLICY_ID,
|
|
552
|
+
_expected_section_id: str = SECTION_POLICY_ID,
|
|
553
|
+
_expected_numpy: Any = np,
|
|
554
|
+
_expected_math: Any = math,
|
|
555
|
+
_expected_threading: Any = threading,
|
|
556
|
+
_expected_shell_class: Any = ShellElement,
|
|
557
|
+
_expected_mesh_class: Any = FEMesh,
|
|
558
|
+
_expected_material_class: Any = Material,
|
|
559
|
+
_expected_capability_error: Any = ElementCapabilityError,
|
|
560
|
+
_expected_candidate_error: Any = StrictFlatLinearCapabilityError,
|
|
561
|
+
_expected_real_scalar: Any = _real_scalar,
|
|
562
|
+
_expected_immutable_array: Any = _immutable_float64_array,
|
|
563
|
+
_expected_unit_vector: Any = _unit_vector,
|
|
564
|
+
_expected_block_permutation: Any = _block_permutation,
|
|
565
|
+
_expected_component_transform: Any = _local_component_transform,
|
|
566
|
+
_expected_numpy_guard: Any = require_exact_numpy_runtime_authority,
|
|
567
|
+
_expected_layout_sentinel: Any = _LAYOUT_SENTINEL,
|
|
568
|
+
_expected_mixed_scope_cache_sentinel: Any = _MIXED_SCOPE_CACHE_SENTINEL,
|
|
569
|
+
_expected_mixed_scope_cache_name: str = _MIXED_SCOPE_CACHE_NAME,
|
|
570
|
+
_expected_component_cache_schema: str = _COMPONENT_CACHE_SCHEMA,
|
|
571
|
+
_expected_component_cache_entry_schema: str = _COMPONENT_CACHE_ENTRY_SCHEMA,
|
|
572
|
+
_expected_component_cache_capacity: int = _COMPONENT_CACHE_CAPACITY,
|
|
573
|
+
_expected_qualified_q4_formulation_id: str = _QUALIFIED_Q4_FORMULATION_ID,
|
|
574
|
+
_expected_qualified_q4_class: Any = QualifiedE4PLShellElement,
|
|
575
|
+
_expected_node_class: Any = Node,
|
|
576
|
+
) -> None:
|
|
577
|
+
"""Fail closed if a frozen Stage-1 module binding was rebound at runtime."""
|
|
578
|
+
|
|
579
|
+
identity_bindings = (
|
|
580
|
+
(HAMMER_POINTS, _expected_hammer_points),
|
|
581
|
+
(HAMMER_REFERENCE_WEIGHTS, _expected_hammer_weights),
|
|
582
|
+
(_PLATE_EMBEDDING, _expected_plate_embedding),
|
|
583
|
+
(_ROTATIONAL_INDICES, _expected_rotational_indices),
|
|
584
|
+
(SUPPORTED_OPERATIONS, _expected_supported),
|
|
585
|
+
(BLOCKED_OPERATIONS, _expected_blocked),
|
|
586
|
+
(CAPABILITY_MATRIX, _expected_capabilities),
|
|
587
|
+
(np, _expected_numpy),
|
|
588
|
+
(math, _expected_math),
|
|
589
|
+
(threading, _expected_threading),
|
|
590
|
+
(ShellElement, _expected_shell_class),
|
|
591
|
+
(QualifiedE4PLShellElement, _expected_qualified_q4_class),
|
|
592
|
+
(FEMesh, _expected_mesh_class),
|
|
593
|
+
(Material, _expected_material_class),
|
|
594
|
+
(Node, _expected_node_class),
|
|
595
|
+
(ElementCapabilityError, _expected_capability_error),
|
|
596
|
+
(StrictFlatLinearCapabilityError, _expected_candidate_error),
|
|
597
|
+
(_real_scalar, _expected_real_scalar),
|
|
598
|
+
(_immutable_float64_array, _expected_immutable_array),
|
|
599
|
+
(_unit_vector, _expected_unit_vector),
|
|
600
|
+
(_block_permutation, _expected_block_permutation),
|
|
601
|
+
(_local_component_transform, _expected_component_transform),
|
|
602
|
+
(require_exact_numpy_runtime_authority, _expected_numpy_guard),
|
|
603
|
+
(_LAYOUT_SENTINEL, _expected_layout_sentinel),
|
|
604
|
+
(_MIXED_SCOPE_CACHE_SENTINEL, _expected_mixed_scope_cache_sentinel),
|
|
605
|
+
)
|
|
606
|
+
scalar_bindings = (
|
|
607
|
+
(SHEAR_CORRECTION, _expected_shear_correction),
|
|
608
|
+
(CBMIN3, _expected_cbmin3),
|
|
609
|
+
(_NORMAL_TOLERANCE, _expected_normal_tolerance),
|
|
610
|
+
(_DEGENERACY_FACTOR, _expected_degeneracy_factor),
|
|
611
|
+
(SELECTOR, _expected_selector),
|
|
612
|
+
(FORMULATION_ID, _expected_formulation_id),
|
|
613
|
+
(IMPLEMENTATION_ID, _expected_implementation_id),
|
|
614
|
+
(FORMULATION_SCHEMA, _expected_formulation_schema),
|
|
615
|
+
(SOURCE_CONTRACT_SCHEMA, _expected_source_schema),
|
|
616
|
+
(SOURCE_CONTRACT_SHA256, _expected_source_hash),
|
|
617
|
+
(EQUATION_MAP_SHA256, _expected_equation_hash),
|
|
618
|
+
(PRIMARY_SOURCE_SHA256, _expected_primary_hash),
|
|
619
|
+
(RELAXATION_AUTHORITY_SHA256, _expected_relaxation_hash),
|
|
620
|
+
(QUADRATURE_AUTHORITY_ID, _expected_quadrature_id),
|
|
621
|
+
(PL_COMPLETION_POLICY_ID, _expected_pl_id),
|
|
622
|
+
(RESULTANT_POLICY_ID, _expected_resultant_id),
|
|
623
|
+
(DIRECTOR_POLICY_ID, _expected_director_id),
|
|
624
|
+
(SECTION_POLICY_ID, _expected_section_id),
|
|
625
|
+
(_MIXED_SCOPE_CACHE_NAME, _expected_mixed_scope_cache_name),
|
|
626
|
+
(_COMPONENT_CACHE_SCHEMA, _expected_component_cache_schema),
|
|
627
|
+
(_COMPONENT_CACHE_ENTRY_SCHEMA, _expected_component_cache_entry_schema),
|
|
628
|
+
(_COMPONENT_CACHE_CAPACITY, _expected_component_cache_capacity),
|
|
629
|
+
(
|
|
630
|
+
_QUALIFIED_Q4_FORMULATION_ID,
|
|
631
|
+
_expected_qualified_q4_formulation_id,
|
|
632
|
+
),
|
|
633
|
+
)
|
|
634
|
+
if any(actual is not expected for actual, expected in identity_bindings) or any(
|
|
635
|
+
type(actual) is not type(expected) or actual != expected
|
|
636
|
+
for actual, expected in scalar_bindings
|
|
637
|
+
):
|
|
638
|
+
raise StrictFlatLinearCapabilityError(
|
|
639
|
+
"strict-flat S3 V2 frozen module authority was modified"
|
|
640
|
+
)
|
|
641
|
+
_expected_numpy_guard(context="strict-flat S3 V2")
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
_FROZEN_INSTANCE_STATE = frozenset(
|
|
645
|
+
{
|
|
646
|
+
"_internal_forces",
|
|
647
|
+
"_is_3node",
|
|
648
|
+
"_is_4node",
|
|
649
|
+
"_is_6node",
|
|
650
|
+
"_is_8node",
|
|
651
|
+
"_is_quadrilateral",
|
|
652
|
+
"_is_triangular",
|
|
653
|
+
"_mass_matrix",
|
|
654
|
+
"_stiffness_matrix",
|
|
655
|
+
"_strict_flat_v2_frozen",
|
|
656
|
+
"drilling_stabilization",
|
|
657
|
+
"element_id",
|
|
658
|
+
"hourglass_stabilization",
|
|
659
|
+
"material_angle_deg",
|
|
660
|
+
"material_direction",
|
|
661
|
+
"material_name",
|
|
662
|
+
"node_ids",
|
|
663
|
+
"reduced_integration",
|
|
664
|
+
"reference_normal",
|
|
665
|
+
"shell_section",
|
|
666
|
+
"thickness",
|
|
667
|
+
}
|
|
668
|
+
)
|
|
669
|
+
_MESH_AUTHORITY_STATE = frozenset(
|
|
670
|
+
{"_qualified_direct_state_token", "_qualified_direct_state_tokens"}
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def _make_instance_authority_registry() -> Tuple[Any, Any]:
|
|
675
|
+
registry: "weakref.WeakKeyDictionary[Any, Tuple[Any, ...]]" = (
|
|
676
|
+
weakref.WeakKeyDictionary()
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
def register(element: Any, signature: Tuple[Any, ...]) -> None:
|
|
680
|
+
registry[element] = signature
|
|
681
|
+
|
|
682
|
+
def require(element: Any, signature: Tuple[Any, ...]) -> None:
|
|
683
|
+
expected = registry.get(element)
|
|
684
|
+
if expected is None or expected != signature:
|
|
685
|
+
raise StrictFlatLinearCapabilityError(
|
|
686
|
+
"strict-flat S3 V2 construction authority changed"
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
return register, require
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
(
|
|
693
|
+
_register_instance_authority,
|
|
694
|
+
_require_instance_authority,
|
|
695
|
+
) = _make_instance_authority_registry()
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def _make_final_class_authority() -> Tuple[Any, Any]:
|
|
699
|
+
authority: Optional[Dict[str, Any]] = None
|
|
700
|
+
|
|
701
|
+
def initialize(cls: type) -> None:
|
|
702
|
+
nonlocal authority
|
|
703
|
+
if authority is not None:
|
|
704
|
+
raise RuntimeError("strict-flat S3 V2 class authority already initialized")
|
|
705
|
+
authority = dict(type.__getattribute__(cls, "__dict__"))
|
|
706
|
+
|
|
707
|
+
def require(cls: type) -> None:
|
|
708
|
+
if authority is None:
|
|
709
|
+
raise RuntimeError("strict-flat S3 V2 class authority is not initialized")
|
|
710
|
+
namespace = type.__getattribute__(cls, "__dict__")
|
|
711
|
+
if set(namespace) != set(authority) or any(
|
|
712
|
+
namespace[name] is not expected for name, expected in authority.items()
|
|
713
|
+
):
|
|
714
|
+
raise StrictFlatLinearCapabilityError(
|
|
715
|
+
"strict-flat S3 V2 candidate class namespace changed"
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
return initialize, require
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
(
|
|
722
|
+
_initialize_final_class_authority,
|
|
723
|
+
_require_final_class_authority,
|
|
724
|
+
) = _make_final_class_authority()
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
class _StrictFlatCandidateMeta(type(ShellElement)):
|
|
728
|
+
"""Prevent normal runtime mutation of the reviewed candidate class."""
|
|
729
|
+
|
|
730
|
+
def __setattr__(cls, name: str, value: Any) -> None:
|
|
731
|
+
namespace = type.__getattribute__(cls, "__dict__")
|
|
732
|
+
if namespace.get("_strict_flat_v2_class_frozen") is True:
|
|
733
|
+
raise StrictFlatLinearCapabilityError(
|
|
734
|
+
"strict-flat S3 V2 candidate class authority is frozen"
|
|
735
|
+
)
|
|
736
|
+
super().__setattr__(name, value)
|
|
737
|
+
|
|
738
|
+
def __delattr__(cls, name: str) -> None:
|
|
739
|
+
namespace = type.__getattribute__(cls, "__dict__")
|
|
740
|
+
if namespace.get("_strict_flat_v2_class_frozen") is True:
|
|
741
|
+
raise StrictFlatLinearCapabilityError(
|
|
742
|
+
"strict-flat S3 V2 candidate class authority is frozen"
|
|
743
|
+
)
|
|
744
|
+
super().__delattr__(name)
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
class StrictFlatLinearE4PLS3V2BShellElement(
|
|
748
|
+
ShellElement, metaclass=_StrictFlatCandidateMeta
|
|
749
|
+
):
|
|
750
|
+
"""Opt-in S3 V2 for the strict flat, linear, isotropic Stage-1 scope."""
|
|
751
|
+
|
|
752
|
+
__slots__ = ("_strict_flat_v2_layout_sentinel",)
|
|
753
|
+
|
|
754
|
+
formulation_id = FORMULATION_ID
|
|
755
|
+
selector = SELECTOR
|
|
756
|
+
supported_operations = SUPPORTED_OPERATIONS
|
|
757
|
+
blocked_operations = BLOCKED_OPERATIONS
|
|
758
|
+
_legacy_shell_dispatch_forbidden = FORMULATION_ID
|
|
759
|
+
_strict_flat_v2_class_frozen = False
|
|
760
|
+
_register_instance_authority = staticmethod(_register_instance_authority)
|
|
761
|
+
_require_instance_authority = staticmethod(_require_instance_authority)
|
|
762
|
+
_module_authority_guard = staticmethod(_validate_module_authority)
|
|
763
|
+
|
|
764
|
+
# Do not inherit the legacy S3 one-point shear rule. Both physical blocks
|
|
765
|
+
# use the published three-point Hammer rule.
|
|
766
|
+
TRI_GAUSS_POINTS_3 = HAMMER_POINTS
|
|
767
|
+
TRI_GAUSS_WEIGHTS_3 = HAMMER_REFERENCE_WEIGHTS
|
|
768
|
+
|
|
769
|
+
def __getattribute__(
|
|
770
|
+
self,
|
|
771
|
+
name: str,
|
|
772
|
+
_frozen_state: frozenset[str] = _FROZEN_INSTANCE_STATE,
|
|
773
|
+
_mesh_state: frozenset[str] = _MESH_AUTHORITY_STATE,
|
|
774
|
+
) -> Any:
|
|
775
|
+
# The inherited Element hierarchy owns an instance ``__dict__``. A
|
|
776
|
+
# caller could otherwise shadow a frozen operator (for example
|
|
777
|
+
# ``_constitutive``) without touching the reviewed class. Reject such
|
|
778
|
+
# shadows before Python dispatches them; the exact state-set check in
|
|
779
|
+
# ``_validate_configuration`` catches every other injected name.
|
|
780
|
+
if name not in {"__dict__", "__class__"}:
|
|
781
|
+
namespace = object.__getattribute__(self, "__dict__")
|
|
782
|
+
if (
|
|
783
|
+
namespace.get("_strict_flat_v2_frozen") is True
|
|
784
|
+
and name in namespace
|
|
785
|
+
and name not in (_frozen_state | _mesh_state)
|
|
786
|
+
):
|
|
787
|
+
raise StrictFlatLinearCapabilityError(
|
|
788
|
+
"strict-flat S3 V2 forbids instance operator shadowing"
|
|
789
|
+
)
|
|
790
|
+
return super().__getattribute__(name)
|
|
791
|
+
|
|
792
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
793
|
+
namespace = object.__getattribute__(self, "__dict__")
|
|
794
|
+
if namespace.get("_strict_flat_v2_frozen") is True:
|
|
795
|
+
raise StrictFlatLinearCapabilityError(
|
|
796
|
+
"strict-flat S3 V2 candidate state is immutable after construction"
|
|
797
|
+
)
|
|
798
|
+
super().__setattr__(name, value)
|
|
799
|
+
|
|
800
|
+
def __init__(
|
|
801
|
+
self,
|
|
802
|
+
element_id: int,
|
|
803
|
+
node_ids: Sequence[int],
|
|
804
|
+
material_name: str = "default",
|
|
805
|
+
*,
|
|
806
|
+
thickness: float = 0.01,
|
|
807
|
+
reference_normal: Sequence[float],
|
|
808
|
+
) -> None:
|
|
809
|
+
__class__._module_authority_guard()
|
|
810
|
+
if type(element_id) is not int:
|
|
811
|
+
raise TypeError("strict-flat S3 V2 element_id must be an exact integer")
|
|
812
|
+
if type(material_name) is not str:
|
|
813
|
+
raise TypeError("strict-flat S3 V2 material_name must be a string")
|
|
814
|
+
owned_node_ids = tuple(node_ids)
|
|
815
|
+
if len(owned_node_ids) != 3:
|
|
816
|
+
raise ValueError(
|
|
817
|
+
"StrictFlatLinearE4PLS3V2BShellElement requires exactly three nodes"
|
|
818
|
+
)
|
|
819
|
+
if not all(type(node_id) is int for node_id in owned_node_ids):
|
|
820
|
+
raise TypeError(
|
|
821
|
+
"strict-flat S3 V2 node_ids must contain exact non-boolean integers"
|
|
822
|
+
)
|
|
823
|
+
if len(set(owned_node_ids)) != 3:
|
|
824
|
+
raise ValueError("strict-flat S3 V2 node_ids must be distinct")
|
|
825
|
+
thickness_value = _real_scalar(thickness, "thickness")
|
|
826
|
+
if thickness_value <= 0.0:
|
|
827
|
+
raise ValueError("strict-flat S3 V2 thickness must be strictly positive")
|
|
828
|
+
normal = _unit_vector(reference_normal, "reference_normal")
|
|
829
|
+
|
|
830
|
+
super().__init__(
|
|
831
|
+
element_id,
|
|
832
|
+
owned_node_ids,
|
|
833
|
+
material_name,
|
|
834
|
+
thickness=thickness_value,
|
|
835
|
+
drilling_stabilization=0.0,
|
|
836
|
+
reduced_integration=False,
|
|
837
|
+
hourglass_stabilization=0.0,
|
|
838
|
+
material_direction=None,
|
|
839
|
+
material_angle_deg=0.0,
|
|
840
|
+
shell_section=None,
|
|
841
|
+
)
|
|
842
|
+
self.reference_normal = normal
|
|
843
|
+
object.__setattr__(self, "_strict_flat_v2_layout_sentinel", _LAYOUT_SENTINEL)
|
|
844
|
+
__class__._register_instance_authority(
|
|
845
|
+
self,
|
|
846
|
+
(
|
|
847
|
+
element_id,
|
|
848
|
+
owned_node_ids,
|
|
849
|
+
material_name,
|
|
850
|
+
thickness_value,
|
|
851
|
+
normal.tobytes(order="C"),
|
|
852
|
+
),
|
|
853
|
+
)
|
|
854
|
+
object.__setattr__(self, "_strict_flat_v2_frozen", True)
|
|
855
|
+
|
|
856
|
+
@property
|
|
857
|
+
def gauss_points(self) -> np.ndarray:
|
|
858
|
+
self._validate_configuration()
|
|
859
|
+
return HAMMER_POINTS
|
|
860
|
+
|
|
861
|
+
@property
|
|
862
|
+
def gauss_weights(self) -> np.ndarray:
|
|
863
|
+
self._validate_configuration()
|
|
864
|
+
return HAMMER_REFERENCE_WEIGHTS
|
|
865
|
+
|
|
866
|
+
@property
|
|
867
|
+
def shear_gauss_points(self) -> np.ndarray:
|
|
868
|
+
self._validate_configuration()
|
|
869
|
+
return HAMMER_POINTS
|
|
870
|
+
|
|
871
|
+
@property
|
|
872
|
+
def shear_gauss_weights(self) -> np.ndarray:
|
|
873
|
+
self._validate_configuration()
|
|
874
|
+
return HAMMER_REFERENCE_WEIGHTS
|
|
875
|
+
|
|
876
|
+
@property
|
|
877
|
+
def physical_reference_director(self) -> np.ndarray:
|
|
878
|
+
self._validate_configuration()
|
|
879
|
+
return np.asarray(self.reference_normal, dtype=np.float64).copy()
|
|
880
|
+
|
|
881
|
+
def capability_matrix(self) -> Dict[str, str]:
|
|
882
|
+
self._validate_configuration()
|
|
883
|
+
return dict(CAPABILITY_MATRIX)
|
|
884
|
+
|
|
885
|
+
@staticmethod
|
|
886
|
+
def _unsupported(operation: str) -> None:
|
|
887
|
+
raise StrictFlatLinearCapabilityError(
|
|
888
|
+
f"strict-flat S3 V2 capability {operation!r} is outside the "
|
|
889
|
+
"authorized Stage-1 flat-linear surface"
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
def _validate_configuration(
|
|
893
|
+
self,
|
|
894
|
+
_module_guard: Any = _validate_module_authority,
|
|
895
|
+
_class_guard: Any = _require_final_class_authority,
|
|
896
|
+
_frozen_state: frozenset[str] = _FROZEN_INSTANCE_STATE,
|
|
897
|
+
_mesh_state: frozenset[str] = _MESH_AUTHORITY_STATE,
|
|
898
|
+
_layout_sentinel: Any = _LAYOUT_SENTINEL,
|
|
899
|
+
) -> None:
|
|
900
|
+
_module_guard()
|
|
901
|
+
_class_guard(__class__)
|
|
902
|
+
if type(self) is not __class__:
|
|
903
|
+
raise StrictFlatLinearCapabilityError(
|
|
904
|
+
"strict-flat S3 V2 mechanics require the exact production class"
|
|
905
|
+
)
|
|
906
|
+
if (
|
|
907
|
+
__class__.formulation_id != FORMULATION_ID
|
|
908
|
+
or __class__.selector != SELECTOR
|
|
909
|
+
or __class__.supported_operations is not SUPPORTED_OPERATIONS
|
|
910
|
+
or __class__.blocked_operations is not BLOCKED_OPERATIONS
|
|
911
|
+
or __class__._legacy_shell_dispatch_forbidden != FORMULATION_ID
|
|
912
|
+
or __class__.TRI_GAUSS_POINTS_3 is not HAMMER_POINTS
|
|
913
|
+
or __class__.TRI_GAUSS_WEIGHTS_3 is not HAMMER_REFERENCE_WEIGHTS
|
|
914
|
+
):
|
|
915
|
+
raise StrictFlatLinearCapabilityError(
|
|
916
|
+
"strict-flat S3 V2 frozen class authority was modified"
|
|
917
|
+
)
|
|
918
|
+
namespace = object.__getattribute__(self, "__dict__")
|
|
919
|
+
state_names = set(namespace)
|
|
920
|
+
if state_names != set(_frozen_state) and state_names != set(
|
|
921
|
+
_frozen_state | _mesh_state
|
|
922
|
+
):
|
|
923
|
+
raise StrictFlatLinearCapabilityError(
|
|
924
|
+
"strict-flat S3 V2 instance authority contains unregistered state"
|
|
925
|
+
)
|
|
926
|
+
if namespace.get("_strict_flat_v2_frozen") is not True:
|
|
927
|
+
raise StrictFlatLinearCapabilityError(
|
|
928
|
+
"strict-flat S3 V2 instance authority is not frozen"
|
|
929
|
+
)
|
|
930
|
+
if (
|
|
931
|
+
object.__getattribute__(self, "_strict_flat_v2_layout_sentinel")
|
|
932
|
+
is not _layout_sentinel
|
|
933
|
+
):
|
|
934
|
+
raise StrictFlatLinearCapabilityError(
|
|
935
|
+
"strict-flat S3 V2 class-layout authority changed"
|
|
936
|
+
)
|
|
937
|
+
if _mesh_state <= state_names:
|
|
938
|
+
token = namespace["_qualified_direct_state_token"]
|
|
939
|
+
tokens = namespace["_qualified_direct_state_tokens"]
|
|
940
|
+
if (
|
|
941
|
+
not isinstance(token, list)
|
|
942
|
+
or len(token) != 1
|
|
943
|
+
or type(token[0]) is not int
|
|
944
|
+
or type(tokens) is not list
|
|
945
|
+
or not tokens
|
|
946
|
+
or not any(candidate is token for candidate in tokens)
|
|
947
|
+
or any(
|
|
948
|
+
not isinstance(candidate, list)
|
|
949
|
+
or len(candidate) != 1
|
|
950
|
+
or type(candidate[0]) is not int
|
|
951
|
+
for candidate in tokens
|
|
952
|
+
)
|
|
953
|
+
):
|
|
954
|
+
raise StrictFlatLinearCapabilityError(
|
|
955
|
+
"strict-flat S3 V2 mesh authority token is malformed"
|
|
956
|
+
)
|
|
957
|
+
if type(self.element_id) is not int:
|
|
958
|
+
raise ValueError("strict-flat S3 V2 element_id authority changed")
|
|
959
|
+
if type(self.material_name) is not str:
|
|
960
|
+
raise ValueError("strict-flat S3 V2 material_name authority changed")
|
|
961
|
+
if (
|
|
962
|
+
type(self.node_ids) is not tuple
|
|
963
|
+
or len(self.node_ids) != 3
|
|
964
|
+
or not all(type(node_id) is int for node_id in self.node_ids)
|
|
965
|
+
or len(set(self.node_ids)) != 3
|
|
966
|
+
):
|
|
967
|
+
raise ValueError("strict-flat S3 V2 connectivity authority changed")
|
|
968
|
+
if (
|
|
969
|
+
type(self.thickness) is not float
|
|
970
|
+
or not math.isfinite(self.thickness)
|
|
971
|
+
or self.thickness <= 0.0
|
|
972
|
+
):
|
|
973
|
+
raise ValueError("strict-flat S3 V2 thickness authority changed")
|
|
974
|
+
if (
|
|
975
|
+
self.drilling_stabilization != 0.0
|
|
976
|
+
or self.hourglass_stabilization != 0.0
|
|
977
|
+
or type(self.reduced_integration) is not bool
|
|
978
|
+
or self.reduced_integration
|
|
979
|
+
or self.material_direction is not None
|
|
980
|
+
or self.material_angle_deg != 0.0
|
|
981
|
+
or self.shell_section is not None
|
|
982
|
+
):
|
|
983
|
+
raise StrictFlatLinearCapabilityError(
|
|
984
|
+
"strict-flat S3 V2 forbids legacy integration, stabilization, "
|
|
985
|
+
"orientation and generalized-section controls"
|
|
986
|
+
)
|
|
987
|
+
if (
|
|
988
|
+
self._stiffness_matrix is not None
|
|
989
|
+
or self._mass_matrix is not None
|
|
990
|
+
or self._internal_forces is not None
|
|
991
|
+
or self._is_3node is not True
|
|
992
|
+
or self._is_triangular is not True
|
|
993
|
+
or self._is_4node is not False
|
|
994
|
+
or self._is_6node is not False
|
|
995
|
+
or self._is_8node is not False
|
|
996
|
+
or self._is_quadrilateral is not False
|
|
997
|
+
):
|
|
998
|
+
raise StrictFlatLinearCapabilityError(
|
|
999
|
+
"strict-flat S3 V2 inherited runtime state changed"
|
|
1000
|
+
)
|
|
1001
|
+
__class__._require_instance_authority(
|
|
1002
|
+
self,
|
|
1003
|
+
(
|
|
1004
|
+
self.element_id,
|
|
1005
|
+
self.node_ids,
|
|
1006
|
+
self.material_name,
|
|
1007
|
+
self.thickness,
|
|
1008
|
+
self.reference_normal.tobytes(order="C"),
|
|
1009
|
+
),
|
|
1010
|
+
)
|
|
1011
|
+
normal = self.reference_normal
|
|
1012
|
+
if (
|
|
1013
|
+
type(normal) is not np.ndarray
|
|
1014
|
+
or normal.shape != (3,)
|
|
1015
|
+
or normal.dtype != np.dtype(np.float64)
|
|
1016
|
+
or normal.flags.writeable
|
|
1017
|
+
or not np.all(np.isfinite(normal))
|
|
1018
|
+
or not math.isclose(
|
|
1019
|
+
float(np.linalg.norm(normal)),
|
|
1020
|
+
1.0,
|
|
1021
|
+
rel_tol=0.0,
|
|
1022
|
+
abs_tol=8.0 * np.finfo(np.float64).eps,
|
|
1023
|
+
)
|
|
1024
|
+
):
|
|
1025
|
+
raise ValueError("strict-flat S3 V2 reference_normal authority changed")
|
|
1026
|
+
|
|
1027
|
+
def _coordinates(self, mesh: FEMesh) -> np.ndarray:
|
|
1028
|
+
if type(mesh) is not FEMesh:
|
|
1029
|
+
raise TypeError("strict-flat S3 V2 requires an exact FEMesh")
|
|
1030
|
+
coordinates = np.empty((3, 3), dtype=np.float64)
|
|
1031
|
+
for index, node_id in enumerate(self.node_ids):
|
|
1032
|
+
node = mesh.get_node(node_id)
|
|
1033
|
+
if node is None:
|
|
1034
|
+
raise ValueError(
|
|
1035
|
+
f"strict-flat S3 V2 references missing node {node_id}"
|
|
1036
|
+
)
|
|
1037
|
+
value = np.asarray(node.coords(), dtype=np.float64)
|
|
1038
|
+
if value.shape != (3,) or not np.all(np.isfinite(value)):
|
|
1039
|
+
raise ValueError(
|
|
1040
|
+
f"strict-flat S3 V2 node {node_id} has invalid coordinates"
|
|
1041
|
+
)
|
|
1042
|
+
coordinates[index] = value
|
|
1043
|
+
return coordinates
|
|
1044
|
+
|
|
1045
|
+
def _validate_model_scope(self, mesh: FEMesh) -> None:
|
|
1046
|
+
"""Admit only one exact, coplanar qualified-Q4/V2B model boundary.
|
|
1047
|
+
|
|
1048
|
+
The complete registry is checked once per mesh mutation epoch. A
|
|
1049
|
+
successful check is cached with the mesh's solver-owned direct-state
|
|
1050
|
+
token, mapping identities and topology/geometry revisions. This keeps
|
|
1051
|
+
formal mixed-mesh assembly linear in the element count while any node,
|
|
1052
|
+
element or registry mutation forces a complete revalidation.
|
|
1053
|
+
"""
|
|
1054
|
+
|
|
1055
|
+
if type(mesh) is not FEMesh:
|
|
1056
|
+
raise TypeError("strict-flat S3 V2 requires an exact FEMesh")
|
|
1057
|
+
|
|
1058
|
+
mesh_namespace = object.__getattribute__(mesh, "__dict__")
|
|
1059
|
+
elements = dict.get(mesh_namespace, "elements")
|
|
1060
|
+
nodes = dict.get(mesh_namespace, "nodes")
|
|
1061
|
+
token = dict.get(mesh_namespace, "_qualified_direct_state_token")
|
|
1062
|
+
revisions = dict.get(mesh_namespace, "revisions")
|
|
1063
|
+
if (
|
|
1064
|
+
not isinstance(elements, dict)
|
|
1065
|
+
or not isinstance(nodes, dict)
|
|
1066
|
+
or not isinstance(token, list)
|
|
1067
|
+
or len(token) != 1
|
|
1068
|
+
or type(token[0]) is not int
|
|
1069
|
+
or type(revisions) is not dict
|
|
1070
|
+
or type(revisions.get("topology")) is not int
|
|
1071
|
+
or type(revisions.get("geometry")) is not int
|
|
1072
|
+
):
|
|
1073
|
+
raise StrictFlatLinearCapabilityError(
|
|
1074
|
+
"strict-flat S3 V2 mesh registry authority is malformed"
|
|
1075
|
+
)
|
|
1076
|
+
if not elements:
|
|
1077
|
+
# Standalone element evaluation remains part of the local
|
|
1078
|
+
# formulation gate. A nonempty model registry, however, must own
|
|
1079
|
+
# the exact element instance evaluated below.
|
|
1080
|
+
return
|
|
1081
|
+
if elements.get(self.element_id) is not self:
|
|
1082
|
+
raise StrictFlatLinearCapabilityError(
|
|
1083
|
+
"strict-flat S3 V2 element registry identity must register the "
|
|
1084
|
+
"evaluated V2B instance exactly"
|
|
1085
|
+
)
|
|
1086
|
+
|
|
1087
|
+
q4_class_namespace = type.__getattribute__(
|
|
1088
|
+
QualifiedE4PLShellElement,
|
|
1089
|
+
"__dict__",
|
|
1090
|
+
)
|
|
1091
|
+
if (
|
|
1092
|
+
q4_class_namespace.get("formulation_id")
|
|
1093
|
+
!= _QUALIFIED_Q4_FORMULATION_ID
|
|
1094
|
+
):
|
|
1095
|
+
raise StrictFlatLinearCapabilityError(
|
|
1096
|
+
"strict-flat S3 V2 qualified Q4 formulation identity changed"
|
|
1097
|
+
)
|
|
1098
|
+
|
|
1099
|
+
common_normal = np.asarray(self.reference_normal, dtype=np.float64)
|
|
1100
|
+
cache = dict.get(mesh_namespace, _MIXED_SCOPE_CACHE_NAME)
|
|
1101
|
+
if (
|
|
1102
|
+
type(cache) is tuple
|
|
1103
|
+
and len(cache) == 8
|
|
1104
|
+
and cache[0] is _MIXED_SCOPE_CACHE_SENTINEL
|
|
1105
|
+
and cache[1] is token
|
|
1106
|
+
and cache[2] == token[0]
|
|
1107
|
+
and cache[3] is nodes
|
|
1108
|
+
and cache[4] is elements
|
|
1109
|
+
and cache[5] == revisions["topology"]
|
|
1110
|
+
and cache[6] == revisions["geometry"]
|
|
1111
|
+
and cache[7] == common_normal.tobytes(order="C")
|
|
1112
|
+
):
|
|
1113
|
+
return
|
|
1114
|
+
|
|
1115
|
+
try:
|
|
1116
|
+
registered_items = list(elements.items())
|
|
1117
|
+
registered_nodes = list(nodes.items())
|
|
1118
|
+
except (AttributeError, RuntimeError, TypeError, ValueError) as exc:
|
|
1119
|
+
raise StrictFlatLinearCapabilityError(
|
|
1120
|
+
"strict-flat S3 V2 mesh registry authority is malformed"
|
|
1121
|
+
) from exc
|
|
1122
|
+
|
|
1123
|
+
allowed_types = (__class__, QualifiedE4PLShellElement)
|
|
1124
|
+
registered_elements: list[Any] = []
|
|
1125
|
+
for key, element in registered_items:
|
|
1126
|
+
if type(key) is not int or type(element) not in allowed_types:
|
|
1127
|
+
raise StrictFlatLinearCapabilityError(
|
|
1128
|
+
"strict-flat S3 V2 mixed element registry permits only exact "
|
|
1129
|
+
"qualified Q4 and V2B shell elements"
|
|
1130
|
+
)
|
|
1131
|
+
namespace = object.__getattribute__(element, "__dict__")
|
|
1132
|
+
element_id = dict.get(namespace, "element_id")
|
|
1133
|
+
if type(element_id) is not int or key != element_id:
|
|
1134
|
+
raise StrictFlatLinearCapabilityError(
|
|
1135
|
+
"strict-flat S3 V2 element registry identity is malformed"
|
|
1136
|
+
)
|
|
1137
|
+
registered_elements.append(element)
|
|
1138
|
+
if (
|
|
1139
|
+
len({id(element) for element in registered_elements})
|
|
1140
|
+
!= len(registered_elements)
|
|
1141
|
+
or len(
|
|
1142
|
+
{
|
|
1143
|
+
dict.get(object.__getattribute__(element, "__dict__"), "element_id")
|
|
1144
|
+
for element in registered_elements
|
|
1145
|
+
}
|
|
1146
|
+
)
|
|
1147
|
+
!= len(registered_elements)
|
|
1148
|
+
):
|
|
1149
|
+
raise StrictFlatLinearCapabilityError(
|
|
1150
|
+
"strict-flat S3 V2 element registry contains duplicate identities"
|
|
1151
|
+
)
|
|
1152
|
+
|
|
1153
|
+
node_coordinates: dict[int, np.ndarray] = {}
|
|
1154
|
+
observed_nodes: list[Node] = []
|
|
1155
|
+
for key, node in registered_nodes:
|
|
1156
|
+
if type(key) is not int or type(node) is not Node:
|
|
1157
|
+
raise StrictFlatLinearCapabilityError(
|
|
1158
|
+
"strict-flat S3 V2 node registry identity is malformed"
|
|
1159
|
+
)
|
|
1160
|
+
namespace = object.__getattribute__(node, "__dict__")
|
|
1161
|
+
if type(dict.get(namespace, "id")) is not int or namespace["id"] != key:
|
|
1162
|
+
raise StrictFlatLinearCapabilityError(
|
|
1163
|
+
"strict-flat S3 V2 node registry identity is malformed"
|
|
1164
|
+
)
|
|
1165
|
+
try:
|
|
1166
|
+
coordinate = np.asarray(
|
|
1167
|
+
(
|
|
1168
|
+
_real_scalar(dict.get(namespace, "x"), f"node {key} x"),
|
|
1169
|
+
_real_scalar(dict.get(namespace, "y"), f"node {key} y"),
|
|
1170
|
+
_real_scalar(dict.get(namespace, "z"), f"node {key} z"),
|
|
1171
|
+
),
|
|
1172
|
+
dtype=np.float64,
|
|
1173
|
+
)
|
|
1174
|
+
except (TypeError, ValueError) as exc:
|
|
1175
|
+
raise StrictFlatLinearCapabilityError(
|
|
1176
|
+
f"strict-flat S3 V2 node {key} coordinates are invalid"
|
|
1177
|
+
) from exc
|
|
1178
|
+
node_coordinates[key] = coordinate
|
|
1179
|
+
observed_nodes.append(node)
|
|
1180
|
+
if len({id(node) for node in observed_nodes}) != len(observed_nodes):
|
|
1181
|
+
raise StrictFlatLinearCapabilityError(
|
|
1182
|
+
"strict-flat S3 V2 node registry contains duplicate identities"
|
|
1183
|
+
)
|
|
1184
|
+
|
|
1185
|
+
for element in registered_elements:
|
|
1186
|
+
if type(element) is __class__:
|
|
1187
|
+
element._validate_configuration()
|
|
1188
|
+
director = np.asarray(element.reference_normal, dtype=np.float64)
|
|
1189
|
+
element_coordinates = element._coordinates(mesh)
|
|
1190
|
+
else:
|
|
1191
|
+
namespace = object.__getattribute__(element, "__dict__")
|
|
1192
|
+
if "formulation_id" in namespace:
|
|
1193
|
+
raise StrictFlatLinearCapabilityError(
|
|
1194
|
+
"strict-flat S3 V2 qualified Q4 formulation identity is "
|
|
1195
|
+
"shadowed"
|
|
1196
|
+
)
|
|
1197
|
+
# This accessor runs the independently frozen Q4 runtime and
|
|
1198
|
+
# instance guards before returning any coordinate observation.
|
|
1199
|
+
element_coordinates = np.asarray(
|
|
1200
|
+
element.get_node_coordinates(mesh),
|
|
1201
|
+
dtype=np.float64,
|
|
1202
|
+
)
|
|
1203
|
+
reference_normal = dict.get(namespace, "reference_normal")
|
|
1204
|
+
polarity = dict.get(namespace, "director_polarity")
|
|
1205
|
+
if reference_normal is None:
|
|
1206
|
+
raise StrictFlatLinearCapabilityError(
|
|
1207
|
+
"strict-flat S3 V2 mixed qualified Q4 requires an "
|
|
1208
|
+
"authoritative reference_normal"
|
|
1209
|
+
)
|
|
1210
|
+
if type(polarity) is not int or polarity not in {-1, 1}:
|
|
1211
|
+
raise StrictFlatLinearCapabilityError(
|
|
1212
|
+
"strict-flat S3 V2 qualified Q4 physical-director "
|
|
1213
|
+
"authority is malformed"
|
|
1214
|
+
)
|
|
1215
|
+
director = float(polarity) * np.asarray(
|
|
1216
|
+
reference_normal,
|
|
1217
|
+
dtype=np.float64,
|
|
1218
|
+
)
|
|
1219
|
+
if (
|
|
1220
|
+
director.shape != (3,)
|
|
1221
|
+
or not np.all(np.isfinite(director))
|
|
1222
|
+
or not math.isclose(
|
|
1223
|
+
float(np.linalg.norm(director)),
|
|
1224
|
+
1.0,
|
|
1225
|
+
rel_tol=0.0,
|
|
1226
|
+
abs_tol=8.0 * np.finfo(np.float64).eps,
|
|
1227
|
+
)
|
|
1228
|
+
or float(director @ common_normal)
|
|
1229
|
+
< 1.0 - _NORMAL_TOLERANCE
|
|
1230
|
+
):
|
|
1231
|
+
raise StrictFlatLinearCapabilityError(
|
|
1232
|
+
"strict-flat S3 V2 mixed shell physical directors are not "
|
|
1233
|
+
"positively aligned"
|
|
1234
|
+
)
|
|
1235
|
+
if (
|
|
1236
|
+
element_coordinates.shape != (len(element.node_ids), 3)
|
|
1237
|
+
or not np.all(np.isfinite(element_coordinates))
|
|
1238
|
+
or any(node_id not in node_coordinates for node_id in element.node_ids)
|
|
1239
|
+
):
|
|
1240
|
+
raise StrictFlatLinearCapabilityError(
|
|
1241
|
+
"strict-flat S3 V2 mixed shell connectivity is malformed"
|
|
1242
|
+
)
|
|
1243
|
+
|
|
1244
|
+
if not node_coordinates:
|
|
1245
|
+
raise StrictFlatLinearCapabilityError(
|
|
1246
|
+
"strict-flat S3 V2 nonempty element registry has no nodes"
|
|
1247
|
+
)
|
|
1248
|
+
all_coordinates = np.asarray(
|
|
1249
|
+
[node_coordinates[key] for key in sorted(node_coordinates)],
|
|
1250
|
+
dtype=np.float64,
|
|
1251
|
+
)
|
|
1252
|
+
common_origin = all_coordinates[0]
|
|
1253
|
+
relative = all_coordinates - common_origin
|
|
1254
|
+
scale = max(1.0, float(np.max(np.linalg.norm(relative, axis=1))))
|
|
1255
|
+
offsets = relative @ common_normal
|
|
1256
|
+
if float(np.max(np.abs(offsets))) > _NORMAL_TOLERANCE * scale:
|
|
1257
|
+
raise StrictFlatLinearCapabilityError(
|
|
1258
|
+
"strict-flat S3 V2 shell nodes are not globally coplanar"
|
|
1259
|
+
)
|
|
1260
|
+
|
|
1261
|
+
object.__setattr__(
|
|
1262
|
+
mesh,
|
|
1263
|
+
_MIXED_SCOPE_CACHE_NAME,
|
|
1264
|
+
(
|
|
1265
|
+
_MIXED_SCOPE_CACHE_SENTINEL,
|
|
1266
|
+
token,
|
|
1267
|
+
token[0],
|
|
1268
|
+
nodes,
|
|
1269
|
+
elements,
|
|
1270
|
+
revisions["topology"],
|
|
1271
|
+
revisions["geometry"],
|
|
1272
|
+
common_normal.tobytes(order="C"),
|
|
1273
|
+
),
|
|
1274
|
+
)
|
|
1275
|
+
|
|
1276
|
+
def _geometry(self, mesh: FEMesh) -> Dict[str, Any]:
|
|
1277
|
+
self._validate_configuration()
|
|
1278
|
+
self._validate_model_scope(mesh)
|
|
1279
|
+
external = self._coordinates(mesh)
|
|
1280
|
+
normal = np.asarray(self.reference_normal, dtype=np.float64)
|
|
1281
|
+
edge_12 = external[1] - external[0]
|
|
1282
|
+
edge_13 = external[2] - external[0]
|
|
1283
|
+
edge_scale = max(
|
|
1284
|
+
float(edge_12 @ edge_12),
|
|
1285
|
+
float(edge_13 @ edge_13),
|
|
1286
|
+
float((external[2] - external[1]) @ (external[2] - external[1])),
|
|
1287
|
+
np.finfo(np.float64).tiny,
|
|
1288
|
+
)
|
|
1289
|
+
cross = np.cross(edge_12, edge_13)
|
|
1290
|
+
doubled_area = float(np.linalg.norm(cross))
|
|
1291
|
+
if (
|
|
1292
|
+
not math.isfinite(doubled_area)
|
|
1293
|
+
or doubled_area <= _DEGENERACY_FACTOR * edge_scale
|
|
1294
|
+
):
|
|
1295
|
+
raise ValueError(
|
|
1296
|
+
"strict-flat S3 V2 requires a numerically nondegenerate triangle"
|
|
1297
|
+
)
|
|
1298
|
+
tangency = max(abs(float(edge_12 @ normal)), abs(float(edge_13 @ normal)))
|
|
1299
|
+
if tangency > _NORMAL_TOLERANCE * math.sqrt(edge_scale):
|
|
1300
|
+
raise StrictFlatLinearCapabilityError(
|
|
1301
|
+
"strict-flat S3 V2 reference_normal is not normal to the flat facet"
|
|
1302
|
+
)
|
|
1303
|
+
signed = float(cross @ normal)
|
|
1304
|
+
if abs(signed) < (1.0 - _NORMAL_TOLERANCE) * doubled_area:
|
|
1305
|
+
raise StrictFlatLinearCapabilityError(
|
|
1306
|
+
"strict-flat S3 V2 reference_normal is incompatible with the facet"
|
|
1307
|
+
)
|
|
1308
|
+
order: Tuple[int, int, int] = (0, 1, 2) if signed > 0.0 else (0, 2, 1)
|
|
1309
|
+
coordinates = external[np.asarray(order, dtype=np.intp)]
|
|
1310
|
+
|
|
1311
|
+
x_axis = coordinates[1] - coordinates[0]
|
|
1312
|
+
x_axis /= float(np.linalg.norm(x_axis))
|
|
1313
|
+
y_axis = np.cross(normal, x_axis)
|
|
1314
|
+
y_axis /= float(np.linalg.norm(y_axis))
|
|
1315
|
+
frame = np.column_stack((x_axis, y_axis, normal))
|
|
1316
|
+
relative = coordinates - coordinates[0]
|
|
1317
|
+
local = np.column_stack((relative @ x_axis, relative @ y_axis))
|
|
1318
|
+
jacobian = np.array(
|
|
1319
|
+
(
|
|
1320
|
+
(local[1, 0] - local[0, 0], local[2, 0] - local[0, 0]),
|
|
1321
|
+
(local[1, 1] - local[0, 1], local[2, 1] - local[0, 1]),
|
|
1322
|
+
),
|
|
1323
|
+
dtype=np.float64,
|
|
1324
|
+
)
|
|
1325
|
+
determinant = float(np.linalg.det(jacobian))
|
|
1326
|
+
if not math.isfinite(determinant) or determinant <= 0.0:
|
|
1327
|
+
raise RuntimeError(
|
|
1328
|
+
"strict-flat S3 V2 failed to construct a positive element chart"
|
|
1329
|
+
)
|
|
1330
|
+
natural_gradients = np.asarray(
|
|
1331
|
+
((-1.0, -1.0), (1.0, 0.0), (0.0, 1.0)),
|
|
1332
|
+
dtype=np.float64,
|
|
1333
|
+
)
|
|
1334
|
+
gradients = natural_gradients @ np.linalg.inv(jacobian)
|
|
1335
|
+
permutation = _block_permutation(order)
|
|
1336
|
+
local_from_external = _local_component_transform(frame) @ permutation
|
|
1337
|
+
return {
|
|
1338
|
+
"external_coordinates": external,
|
|
1339
|
+
"internal_order": order,
|
|
1340
|
+
"local_coordinates": local,
|
|
1341
|
+
"shape_gradients": gradients,
|
|
1342
|
+
"area": 0.5 * determinant,
|
|
1343
|
+
"frame": frame,
|
|
1344
|
+
"local_from_external": local_from_external,
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
def _constitutive(self, material: Material) -> Dict[str, Any]:
|
|
1348
|
+
if type(material) is not Material:
|
|
1349
|
+
raise StrictFlatLinearCapabilityError(
|
|
1350
|
+
"strict-flat S3 V2 requires the exact homogeneous isotropic Material"
|
|
1351
|
+
)
|
|
1352
|
+
if getattr(material, "hardening_curve", None) is not None:
|
|
1353
|
+
raise StrictFlatLinearCapabilityError(
|
|
1354
|
+
"strict-flat S3 V2 material nonlinearity is outside Stage-1 authority"
|
|
1355
|
+
)
|
|
1356
|
+
yield_stress = getattr(material, "yield_stress", 0.0)
|
|
1357
|
+
if yield_stress is not None and _real_scalar(yield_stress, "yield_stress") != 0.0:
|
|
1358
|
+
raise StrictFlatLinearCapabilityError(
|
|
1359
|
+
"strict-flat S3 V2 requires a purely elastic material descriptor"
|
|
1360
|
+
)
|
|
1361
|
+
elastic_modulus = _real_scalar(
|
|
1362
|
+
getattr(material, "elastic_modulus", None),
|
|
1363
|
+
"elastic_modulus",
|
|
1364
|
+
)
|
|
1365
|
+
poisson_ratio = _real_scalar(
|
|
1366
|
+
getattr(material, "poisson_ratio", None),
|
|
1367
|
+
"poisson_ratio",
|
|
1368
|
+
)
|
|
1369
|
+
if elastic_modulus <= 0.0:
|
|
1370
|
+
raise ValueError("strict-flat S3 V2 elastic_modulus must be positive")
|
|
1371
|
+
if not -1.0 < poisson_ratio < 0.5:
|
|
1372
|
+
raise ValueError(
|
|
1373
|
+
"strict-flat S3 V2 poisson_ratio must satisfy -1 < nu < 0.5"
|
|
1374
|
+
)
|
|
1375
|
+
membrane_scale = (
|
|
1376
|
+
elastic_modulus * self.thickness / (1.0 - poisson_ratio**2)
|
|
1377
|
+
)
|
|
1378
|
+
bending_scale = (
|
|
1379
|
+
elastic_modulus
|
|
1380
|
+
* self.thickness**3
|
|
1381
|
+
/ (12.0 * (1.0 - poisson_ratio**2))
|
|
1382
|
+
)
|
|
1383
|
+
isotropic = np.asarray(
|
|
1384
|
+
(
|
|
1385
|
+
(1.0, poisson_ratio, 0.0),
|
|
1386
|
+
(poisson_ratio, 1.0, 0.0),
|
|
1387
|
+
(0.0, 0.0, 0.5 * (1.0 - poisson_ratio)),
|
|
1388
|
+
),
|
|
1389
|
+
dtype=np.float64,
|
|
1390
|
+
)
|
|
1391
|
+
membrane = membrane_scale * isotropic
|
|
1392
|
+
bending = bending_scale * isotropic
|
|
1393
|
+
# Preserve the accepted source-equation operation order. These are
|
|
1394
|
+
# binary64 calculations, so the algebraically equivalent staged shear
|
|
1395
|
+
# modulus expression is not byte-identical to the V5B authority.
|
|
1396
|
+
shear_scalar = (
|
|
1397
|
+
SHEAR_CORRECTION
|
|
1398
|
+
* elastic_modulus
|
|
1399
|
+
* self.thickness
|
|
1400
|
+
/ (2.0 * (1.0 + poisson_ratio))
|
|
1401
|
+
)
|
|
1402
|
+
drill_scale = (
|
|
1403
|
+
self.thickness
|
|
1404
|
+
* elastic_modulus
|
|
1405
|
+
/ (2.0 * (1.0 + poisson_ratio))
|
|
1406
|
+
)
|
|
1407
|
+
shear = shear_scalar * np.eye(2, dtype=np.float64)
|
|
1408
|
+
if not all(
|
|
1409
|
+
np.all(np.isfinite(matrix)) for matrix in (membrane, bending, shear)
|
|
1410
|
+
) or shear_scalar <= 0.0:
|
|
1411
|
+
raise ValueError("strict-flat S3 V2 constitutive data are invalid")
|
|
1412
|
+
return {
|
|
1413
|
+
"A": membrane,
|
|
1414
|
+
"D": bending,
|
|
1415
|
+
"H": shear,
|
|
1416
|
+
"bending_scalar": bending_scale,
|
|
1417
|
+
"shear_scalar": shear_scalar,
|
|
1418
|
+
"drill_scale": drill_scale,
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
@staticmethod
|
|
1422
|
+
def _membrane_operator(gradients: np.ndarray) -> np.ndarray:
|
|
1423
|
+
operator = np.zeros((3, 18), dtype=np.float64)
|
|
1424
|
+
for node, (derivative_x, derivative_y) in enumerate(gradients):
|
|
1425
|
+
base = 6 * node
|
|
1426
|
+
operator[0, base] = derivative_x
|
|
1427
|
+
operator[1, base + 1] = derivative_y
|
|
1428
|
+
operator[2, base] = derivative_y
|
|
1429
|
+
operator[2, base + 1] = derivative_x
|
|
1430
|
+
return operator
|
|
1431
|
+
|
|
1432
|
+
@staticmethod
|
|
1433
|
+
def _pl_constraint(gradients: np.ndarray) -> np.ndarray:
|
|
1434
|
+
constraint = np.zeros((3, 18), dtype=np.float64)
|
|
1435
|
+
for row in range(3):
|
|
1436
|
+
for node, (derivative_x, derivative_y) in enumerate(gradients):
|
|
1437
|
+
base = 6 * node
|
|
1438
|
+
constraint[row, base] = 0.5 * derivative_y
|
|
1439
|
+
constraint[row, base + 1] = -0.5 * derivative_x
|
|
1440
|
+
constraint[row, 6 * row + 5] = 1.0
|
|
1441
|
+
return constraint
|
|
1442
|
+
|
|
1443
|
+
@staticmethod
|
|
1444
|
+
def _edge_data(local: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
1445
|
+
directed = ((0, 1), (1, 2), (2, 0))
|
|
1446
|
+
cosine = np.empty(3, dtype=np.float64)
|
|
1447
|
+
sine = np.empty(3, dtype=np.float64)
|
|
1448
|
+
length = np.empty(3, dtype=np.float64)
|
|
1449
|
+
for edge, (first, second) in enumerate(directed):
|
|
1450
|
+
delta = local[second] - local[first]
|
|
1451
|
+
edge_length = float(np.linalg.norm(delta))
|
|
1452
|
+
if not math.isfinite(edge_length) or edge_length <= 0.0:
|
|
1453
|
+
raise ValueError("strict-flat S3 V2 has an invalid directed edge")
|
|
1454
|
+
length[edge] = edge_length
|
|
1455
|
+
cosine[edge] = delta[0] / edge_length
|
|
1456
|
+
sine[edge] = delta[1] / edge_length
|
|
1457
|
+
return cosine, sine, length
|
|
1458
|
+
|
|
1459
|
+
@staticmethod
|
|
1460
|
+
def _side_kinematics(
|
|
1461
|
+
cosine: np.ndarray,
|
|
1462
|
+
sine: np.ndarray,
|
|
1463
|
+
length: np.ndarray,
|
|
1464
|
+
) -> np.ndarray:
|
|
1465
|
+
operator = np.zeros((3, 9), dtype=np.float64)
|
|
1466
|
+
for edge, (first, second) in enumerate(((0, 1), (1, 2), (2, 0))):
|
|
1467
|
+
first_base = 3 * first
|
|
1468
|
+
second_base = 3 * second
|
|
1469
|
+
operator[edge, first_base] = -1.0 / length[edge]
|
|
1470
|
+
operator[edge, second_base] = 1.0 / length[edge]
|
|
1471
|
+
operator[edge, first_base + 1] = 0.5 * cosine[edge]
|
|
1472
|
+
operator[edge, first_base + 2] = 0.5 * sine[edge]
|
|
1473
|
+
operator[edge, second_base + 1] = 0.5 * cosine[edge]
|
|
1474
|
+
operator[edge, second_base + 2] = 0.5 * sine[edge]
|
|
1475
|
+
return operator
|
|
1476
|
+
|
|
1477
|
+
@staticmethod
|
|
1478
|
+
def _side_shear_projection(
|
|
1479
|
+
shape: np.ndarray,
|
|
1480
|
+
cosine: np.ndarray,
|
|
1481
|
+
sine: np.ndarray,
|
|
1482
|
+
) -> np.ndarray:
|
|
1483
|
+
c12, c23, c31 = cosine
|
|
1484
|
+
s12, s23, s31 = sine
|
|
1485
|
+
a1 = c12 * s31 - c31 * s12
|
|
1486
|
+
a2 = c23 * s12 - c12 * s23
|
|
1487
|
+
a3 = c31 * s23 - c23 * s31
|
|
1488
|
+
scale = max(abs(a1), abs(a2), abs(a3), 1.0)
|
|
1489
|
+
if min(abs(a1), abs(a2), abs(a3)) <= _DEGENERACY_FACTOR * scale:
|
|
1490
|
+
raise ValueError(
|
|
1491
|
+
"strict-flat S3 V2 side-shear projection is singular"
|
|
1492
|
+
)
|
|
1493
|
+
n1, n2, n3 = shape
|
|
1494
|
+
return np.asarray(
|
|
1495
|
+
(
|
|
1496
|
+
(
|
|
1497
|
+
s31 * n1 / a1 - s23 * n2 / a2,
|
|
1498
|
+
s12 * n2 / a2 - s31 * n3 / a3,
|
|
1499
|
+
s23 * n3 / a3 - s12 * n1 / a1,
|
|
1500
|
+
),
|
|
1501
|
+
(
|
|
1502
|
+
-(c31 * n1 / a1 - c23 * n2 / a2),
|
|
1503
|
+
-(c12 * n2 / a2 - c31 * n3 / a3),
|
|
1504
|
+
-(c23 * n3 / a3 - c12 * n1 / a1),
|
|
1505
|
+
),
|
|
1506
|
+
),
|
|
1507
|
+
dtype=np.float64,
|
|
1508
|
+
)
|
|
1509
|
+
|
|
1510
|
+
@staticmethod
|
|
1511
|
+
def _bending_beta_operator(gradients: np.ndarray) -> np.ndarray:
|
|
1512
|
+
operator = np.zeros((3, 9), dtype=np.float64)
|
|
1513
|
+
for node, (derivative_x, derivative_y) in enumerate(gradients):
|
|
1514
|
+
base = 3 * node
|
|
1515
|
+
operator[0, base + 1] = derivative_x
|
|
1516
|
+
operator[1, base + 2] = derivative_y
|
|
1517
|
+
operator[2, base + 1] = derivative_y
|
|
1518
|
+
operator[2, base + 2] = derivative_x
|
|
1519
|
+
return operator
|
|
1520
|
+
|
|
1521
|
+
@staticmethod
|
|
1522
|
+
def _hierarchical_gradients(
|
|
1523
|
+
shape: np.ndarray,
|
|
1524
|
+
gradients: np.ndarray,
|
|
1525
|
+
) -> np.ndarray:
|
|
1526
|
+
pairs = ((0, 1), (1, 2), (2, 0))
|
|
1527
|
+
made = np.empty((3, 2), dtype=np.float64)
|
|
1528
|
+
for edge, (first, second) in enumerate(pairs):
|
|
1529
|
+
made[edge] = 4.0 * (
|
|
1530
|
+
shape[first] * gradients[second]
|
|
1531
|
+
+ shape[second] * gradients[first]
|
|
1532
|
+
)
|
|
1533
|
+
return made
|
|
1534
|
+
|
|
1535
|
+
@staticmethod
|
|
1536
|
+
def _bending_delta_operator(
|
|
1537
|
+
hierarchical_gradients: np.ndarray,
|
|
1538
|
+
cosine: np.ndarray,
|
|
1539
|
+
sine: np.ndarray,
|
|
1540
|
+
) -> np.ndarray:
|
|
1541
|
+
operator = np.empty((3, 3), dtype=np.float64)
|
|
1542
|
+
for edge, (derivative_x, derivative_y) in enumerate(
|
|
1543
|
+
hierarchical_gradients
|
|
1544
|
+
):
|
|
1545
|
+
operator[:, edge] = (
|
|
1546
|
+
derivative_x * cosine[edge],
|
|
1547
|
+
derivative_y * sine[edge],
|
|
1548
|
+
derivative_y * cosine[edge] + derivative_x * sine[edge],
|
|
1549
|
+
)
|
|
1550
|
+
return operator
|
|
1551
|
+
|
|
1552
|
+
def _operators(
|
|
1553
|
+
self,
|
|
1554
|
+
geometry: Mapping[str, Any],
|
|
1555
|
+
constitutive: Mapping[str, Any],
|
|
1556
|
+
) -> Dict[str, Any]:
|
|
1557
|
+
del constitutive
|
|
1558
|
+
local = np.asarray(geometry["local_coordinates"], dtype=np.float64)
|
|
1559
|
+
gradients = np.asarray(geometry["shape_gradients"], dtype=np.float64)
|
|
1560
|
+
membrane = self._membrane_operator(gradients)
|
|
1561
|
+
bending_all = []
|
|
1562
|
+
shear_all = []
|
|
1563
|
+
shapes = []
|
|
1564
|
+
# UHM/CE/02-02 equations 2.23 and NASA A.1--A.4. This is the
|
|
1565
|
+
# source-authorized MIN3 operator that was independently accepted by
|
|
1566
|
+
# V5A/V5B; it deliberately replaces the historical V2A DKMT
|
|
1567
|
+
# elimination rather than modifying that preserved implementation.
|
|
1568
|
+
a = np.empty(3, dtype=np.float64)
|
|
1569
|
+
b = np.empty(3, dtype=np.float64)
|
|
1570
|
+
for node, first, second in ((0, 1, 2), (1, 2, 0), (2, 0, 1)):
|
|
1571
|
+
a[node] = local[second, 0] - local[first, 0]
|
|
1572
|
+
b[node] = local[first, 1] - local[second, 1]
|
|
1573
|
+
for xi, eta in HAMMER_POINTS:
|
|
1574
|
+
shape = np.asarray((1.0 - xi - eta, xi, eta), dtype=np.float64)
|
|
1575
|
+
dl = np.empty((2, 3), dtype=np.float64)
|
|
1576
|
+
dm = np.empty((2, 3), dtype=np.float64)
|
|
1577
|
+
for node, first, second in ((0, 1, 2), (1, 2, 0), (2, 0, 1)):
|
|
1578
|
+
for axis in range(2):
|
|
1579
|
+
derivative = gradients[:, axis]
|
|
1580
|
+
dl[axis, node] = 0.5 * (
|
|
1581
|
+
derivative[node]
|
|
1582
|
+
* (b[second] * shape[first] - b[first] * shape[second])
|
|
1583
|
+
+ shape[node]
|
|
1584
|
+
* (
|
|
1585
|
+
b[second] * derivative[first]
|
|
1586
|
+
- b[first] * derivative[second]
|
|
1587
|
+
)
|
|
1588
|
+
)
|
|
1589
|
+
dm[axis, node] = 0.5 * (
|
|
1590
|
+
derivative[node]
|
|
1591
|
+
* (a[first] * shape[second] - a[second] * shape[first])
|
|
1592
|
+
+ shape[node]
|
|
1593
|
+
* (
|
|
1594
|
+
a[first] * derivative[second]
|
|
1595
|
+
- a[second] * derivative[first]
|
|
1596
|
+
)
|
|
1597
|
+
)
|
|
1598
|
+
bending = np.zeros((3, 18), dtype=np.float64)
|
|
1599
|
+
shear = np.zeros((2, 18), dtype=np.float64)
|
|
1600
|
+
for node, (derivative_x, derivative_y) in enumerate(gradients):
|
|
1601
|
+
base = 6 * node
|
|
1602
|
+
bending[0, base + 4] = derivative_x
|
|
1603
|
+
bending[1, base + 3] = -derivative_y
|
|
1604
|
+
bending[2, base + 3] = -derivative_x
|
|
1605
|
+
bending[2, base + 4] = derivative_y
|
|
1606
|
+
shear[0, base + 2] = derivative_x
|
|
1607
|
+
shear[0, base + 3] = -dl[0, node]
|
|
1608
|
+
shear[0, base + 4] = dm[0, node] + shape[node]
|
|
1609
|
+
shear[1, base + 2] = derivative_y
|
|
1610
|
+
shear[1, base + 3] = -dl[1, node] - shape[node]
|
|
1611
|
+
shear[1, base + 4] = dm[1, node]
|
|
1612
|
+
bending_all.append(bending)
|
|
1613
|
+
shear_all.append(shear)
|
|
1614
|
+
shapes.append(shape)
|
|
1615
|
+
return {
|
|
1616
|
+
"B_m": membrane,
|
|
1617
|
+
"B_b": np.asarray(bending_all, dtype=np.float64),
|
|
1618
|
+
"B_s": np.asarray(shear_all, dtype=np.float64),
|
|
1619
|
+
"shape": np.asarray(shapes, dtype=np.float64),
|
|
1620
|
+
"C": self._pl_constraint(gradients),
|
|
1621
|
+
"phi": np.zeros(3, dtype=np.float64),
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
compute_stiffness_components = _make_exact_component_compute_wrapper()
|
|
1625
|
+
|
|
1626
|
+
def compute_stiffness_matrix(
|
|
1627
|
+
self,
|
|
1628
|
+
mesh: FEMesh,
|
|
1629
|
+
material: Material,
|
|
1630
|
+
) -> np.ndarray:
|
|
1631
|
+
return np.asarray(
|
|
1632
|
+
self.compute_stiffness_components(mesh, material)["total"],
|
|
1633
|
+
dtype=np.float64,
|
|
1634
|
+
).copy()
|
|
1635
|
+
|
|
1636
|
+
def compute_internal_forces(
|
|
1637
|
+
self,
|
|
1638
|
+
mesh: FEMesh,
|
|
1639
|
+
displacements: np.ndarray,
|
|
1640
|
+
material: Material,
|
|
1641
|
+
) -> np.ndarray:
|
|
1642
|
+
vector = self._get_element_displacements(mesh, displacements)
|
|
1643
|
+
if vector.shape != (18,) or not np.all(np.isfinite(vector)):
|
|
1644
|
+
raise ValueError(
|
|
1645
|
+
"strict-flat S3 V2 displacements must resolve to a finite 18-vector"
|
|
1646
|
+
)
|
|
1647
|
+
return self.compute_stiffness_matrix(mesh, material) @ vector
|
|
1648
|
+
|
|
1649
|
+
def compute_variational_resultants(
|
|
1650
|
+
self,
|
|
1651
|
+
mesh: FEMesh,
|
|
1652
|
+
displacements: np.ndarray,
|
|
1653
|
+
material: Material,
|
|
1654
|
+
) -> Dict[str, Any]:
|
|
1655
|
+
geometry = self._geometry(mesh)
|
|
1656
|
+
constitutive = self._constitutive(material)
|
|
1657
|
+
operators = self._operators(geometry, constitutive)
|
|
1658
|
+
phi_squared = float(
|
|
1659
|
+
self.compute_stiffness_components(mesh, material)["phi_squared"]
|
|
1660
|
+
)
|
|
1661
|
+
vector = self._get_element_displacements(mesh, displacements)
|
|
1662
|
+
if vector.shape != (18,) or not np.all(np.isfinite(vector)):
|
|
1663
|
+
raise ValueError(
|
|
1664
|
+
"strict-flat S3 V2 displacements must resolve to a finite 18-vector"
|
|
1665
|
+
)
|
|
1666
|
+
local_vector = geometry["local_from_external"] @ vector
|
|
1667
|
+
membrane_strain = np.broadcast_to(
|
|
1668
|
+
operators["B_m"] @ local_vector,
|
|
1669
|
+
(3, 3),
|
|
1670
|
+
).copy()
|
|
1671
|
+
curvature = np.einsum("gij,j->gi", operators["B_b"], local_vector)
|
|
1672
|
+
transverse_shear = np.einsum(
|
|
1673
|
+
"gij,j->gi",
|
|
1674
|
+
operators["B_s"],
|
|
1675
|
+
local_vector,
|
|
1676
|
+
)
|
|
1677
|
+
membrane_resultants = membrane_strain @ constitutive["A"].T
|
|
1678
|
+
bending_resultants = curvature @ constitutive["D"].T
|
|
1679
|
+
shear_resultants = (
|
|
1680
|
+
phi_squared * transverse_shear @ constitutive["H"].T
|
|
1681
|
+
)
|
|
1682
|
+
# Operators are evaluated in the positive internal D3 chart. Restore
|
|
1683
|
+
# station rows to the external connectivity order so the canonical
|
|
1684
|
+
# Hammer coordinates below always describe the returned row. The
|
|
1685
|
+
# component frame remains the explicitly reported physical frame.
|
|
1686
|
+
order = tuple(int(value) for value in geometry["internal_order"])
|
|
1687
|
+
|
|
1688
|
+
def external_station_order(values: np.ndarray) -> np.ndarray:
|
|
1689
|
+
made = np.empty_like(values)
|
|
1690
|
+
for internal_index, external_index in enumerate(order):
|
|
1691
|
+
made[external_index] = values[internal_index]
|
|
1692
|
+
return made
|
|
1693
|
+
|
|
1694
|
+
membrane_strain = external_station_order(membrane_strain)
|
|
1695
|
+
curvature = external_station_order(curvature)
|
|
1696
|
+
transverse_shear = external_station_order(transverse_shear)
|
|
1697
|
+
membrane_resultants = external_station_order(membrane_resultants)
|
|
1698
|
+
bending_resultants = external_station_order(bending_resultants)
|
|
1699
|
+
shear_resultants = external_station_order(shear_resultants)
|
|
1700
|
+
barycentric = np.column_stack(
|
|
1701
|
+
(
|
|
1702
|
+
1.0 - HAMMER_POINTS[:, 0] - HAMMER_POINTS[:, 1],
|
|
1703
|
+
HAMMER_POINTS[:, 0],
|
|
1704
|
+
HAMMER_POINTS[:, 1],
|
|
1705
|
+
)
|
|
1706
|
+
)
|
|
1707
|
+
physical_stations = barycentric @ np.asarray(
|
|
1708
|
+
geometry["external_coordinates"], dtype=np.float64
|
|
1709
|
+
)
|
|
1710
|
+
area = float(geometry["area"])
|
|
1711
|
+
return {
|
|
1712
|
+
"recovery_scope": RESULTANT_POLICY_ID,
|
|
1713
|
+
"qualified_recovery": False,
|
|
1714
|
+
"physical_stress_available": False,
|
|
1715
|
+
"numerical_fields_excluded": True,
|
|
1716
|
+
"membrane_strain": membrane_strain,
|
|
1717
|
+
"curvature": curvature,
|
|
1718
|
+
"transverse_shear_strain": transverse_shear,
|
|
1719
|
+
"membrane_resultants": membrane_resultants,
|
|
1720
|
+
"bending_resultants": bending_resultants,
|
|
1721
|
+
"transverse_shear_resultants": shear_resultants,
|
|
1722
|
+
"min3_relaxation_phi_squared": phi_squared,
|
|
1723
|
+
"relaxation_authority_sha256": RELAXATION_AUTHORITY_SHA256,
|
|
1724
|
+
"hammer_points": np.asarray(HAMMER_POINTS, dtype=np.float64).copy(),
|
|
1725
|
+
"external_barycentric_coordinates": barycentric,
|
|
1726
|
+
"physical_station_coordinates": physical_stations,
|
|
1727
|
+
"internal_order": order,
|
|
1728
|
+
"physical_weights": np.full(3, area / 3.0, dtype=np.float64),
|
|
1729
|
+
"frame": np.asarray(geometry["frame"], dtype=np.float64).copy(),
|
|
1730
|
+
"resultant_policy_id": RESULTANT_POLICY_ID,
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
def compute_dead_transverse_pressure_load(
|
|
1734
|
+
self,
|
|
1735
|
+
mesh: FEMesh,
|
|
1736
|
+
pressure: Any,
|
|
1737
|
+
) -> np.ndarray:
|
|
1738
|
+
geometry = self._geometry(mesh)
|
|
1739
|
+
area = float(geometry["area"])
|
|
1740
|
+
if isinstance(pressure, (bool, np.bool_)):
|
|
1741
|
+
raise TypeError("strict-flat S3 V2 pressure must be real")
|
|
1742
|
+
if isinstance(pressure, (int, float, np.integer, np.floating)):
|
|
1743
|
+
value = _real_scalar(pressure, "pressure")
|
|
1744
|
+
else:
|
|
1745
|
+
raise StrictFlatLinearCapabilityError(
|
|
1746
|
+
"strict-flat S3 V2B source authority admits uniform pressure only"
|
|
1747
|
+
)
|
|
1748
|
+
local = np.asarray(geometry["local_coordinates"], dtype=np.float64)
|
|
1749
|
+
a = np.empty(3, dtype=np.float64)
|
|
1750
|
+
b = np.empty(3, dtype=np.float64)
|
|
1751
|
+
for node, first, second in ((0, 1, 2), (1, 2, 0), (2, 0, 1)):
|
|
1752
|
+
a[node] = local[second, 0] - local[first, 0]
|
|
1753
|
+
b[node] = local[first, 1] - local[second, 1]
|
|
1754
|
+
load_local = np.zeros(18, dtype=np.float64)
|
|
1755
|
+
for xi, eta in HAMMER_POINTS:
|
|
1756
|
+
shape = np.asarray((1.0 - xi - eta, xi, eta), dtype=np.float64)
|
|
1757
|
+
for node, first, second in ((0, 1, 2), (1, 2, 0), (2, 0, 1)):
|
|
1758
|
+
l_value = 0.5 * shape[node] * (
|
|
1759
|
+
b[second] * shape[first] - b[first] * shape[second]
|
|
1760
|
+
)
|
|
1761
|
+
m_value = 0.5 * shape[node] * (
|
|
1762
|
+
a[first] * shape[second] - a[second] * shape[first]
|
|
1763
|
+
)
|
|
1764
|
+
base = 6 * node
|
|
1765
|
+
weight = value * area / 3.0
|
|
1766
|
+
load_local[base + 2] += weight * shape[node]
|
|
1767
|
+
load_local[base + 3] -= weight * l_value
|
|
1768
|
+
load_local[base + 4] += weight * m_value
|
|
1769
|
+
transform = np.asarray(geometry["local_from_external"], dtype=np.float64)
|
|
1770
|
+
return transform.T @ load_local
|
|
1771
|
+
|
|
1772
|
+
# Explicitly seal every inherited mechanics or persistence route not
|
|
1773
|
+
# authorized by the Stage-1 equation map.
|
|
1774
|
+
def compute_mass_matrix(self, mesh: FEMesh, material: Material, **kwargs: Any) -> np.ndarray:
|
|
1775
|
+
self._unsupported("consistent_mass")
|
|
1776
|
+
|
|
1777
|
+
def compute_geometric_stiffness_matrix(
|
|
1778
|
+
self,
|
|
1779
|
+
mesh: FEMesh,
|
|
1780
|
+
material: Material,
|
|
1781
|
+
state: Optional[Any] = None,
|
|
1782
|
+
) -> np.ndarray:
|
|
1783
|
+
self._unsupported("geometric_stiffness")
|
|
1784
|
+
|
|
1785
|
+
def init_nonlinear_state(self, num_layers: int) -> Dict[str, Any]:
|
|
1786
|
+
self._unsupported("nonlinear_state")
|
|
1787
|
+
|
|
1788
|
+
def compute_nonlinear_response(
|
|
1789
|
+
self,
|
|
1790
|
+
mesh: FEMesh,
|
|
1791
|
+
material: Material,
|
|
1792
|
+
u_elem: np.ndarray,
|
|
1793
|
+
state: Optional[Any] = None,
|
|
1794
|
+
num_layers: int = 5,
|
|
1795
|
+
tangent: bool = True,
|
|
1796
|
+
) -> Tuple[np.ndarray, Optional[np.ndarray], Optional[Any]]:
|
|
1797
|
+
self._unsupported("nonlinear_geometry")
|
|
1798
|
+
|
|
1799
|
+
def compute_stresses(
|
|
1800
|
+
self,
|
|
1801
|
+
mesh: FEMesh,
|
|
1802
|
+
displacements: np.ndarray,
|
|
1803
|
+
material: Material,
|
|
1804
|
+
return_global: bool = False,
|
|
1805
|
+
) -> Dict[str, Any]:
|
|
1806
|
+
self._unsupported("qualified_recovery")
|
|
1807
|
+
|
|
1808
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
1809
|
+
self._unsupported("serialization")
|
|
1810
|
+
|
|
1811
|
+
@classmethod
|
|
1812
|
+
def from_dict(
|
|
1813
|
+
cls,
|
|
1814
|
+
payload: Mapping[str, Any],
|
|
1815
|
+
) -> "StrictFlatLinearE4PLS3V2BShellElement":
|
|
1816
|
+
cls._unsupported("serialization")
|
|
1817
|
+
|
|
1818
|
+
def __getstate__(self) -> Dict[str, Any]:
|
|
1819
|
+
self._unsupported("restart")
|
|
1820
|
+
|
|
1821
|
+
def __setstate__(self, state: Mapping[str, Any]) -> None:
|
|
1822
|
+
self._unsupported("restart")
|
|
1823
|
+
|
|
1824
|
+
def __reduce_ex__(self, protocol: int) -> Any:
|
|
1825
|
+
self._unsupported("restart")
|
|
1826
|
+
|
|
1827
|
+
def __copy__(self) -> "StrictFlatLinearE4PLS3V2BShellElement":
|
|
1828
|
+
self._unsupported("restart")
|
|
1829
|
+
|
|
1830
|
+
def __deepcopy__(
|
|
1831
|
+
self, memo: Dict[int, Any]
|
|
1832
|
+
) -> "StrictFlatLinearE4PLS3V2BShellElement":
|
|
1833
|
+
del memo
|
|
1834
|
+
self._unsupported("restart")
|
|
1835
|
+
|
|
1836
|
+
|
|
1837
|
+
# The live cache can be reached only through the reviewed public compute
|
|
1838
|
+
# wrapper installed above; its construction primitive is not a module surface.
|
|
1839
|
+
del _make_exact_component_compute_wrapper
|
|
1840
|
+
|
|
1841
|
+
|
|
1842
|
+
_ALLOWED_INHERITED_CALLABLES = frozenset(
|
|
1843
|
+
{
|
|
1844
|
+
"_get_element_displacements",
|
|
1845
|
+
"get_dof_mapping",
|
|
1846
|
+
"get_node_coordinates",
|
|
1847
|
+
}
|
|
1848
|
+
)
|
|
1849
|
+
_SEALED_INHERITED_CALLABLES = frozenset(
|
|
1850
|
+
{
|
|
1851
|
+
"_bending_compression_samples",
|
|
1852
|
+
"_build_drilling_b_matrix",
|
|
1853
|
+
"_build_shell_b_matrices",
|
|
1854
|
+
"_center_frame",
|
|
1855
|
+
"_compute_3node_shape_functions",
|
|
1856
|
+
"_compute_4node_shape_functions",
|
|
1857
|
+
"_compute_6node_shape_functions",
|
|
1858
|
+
"_compute_8node_shape_functions",
|
|
1859
|
+
"_compute_generalized_section_results",
|
|
1860
|
+
"_fallback_edge_direction",
|
|
1861
|
+
"_generalized_section_in_frame",
|
|
1862
|
+
"_generalized_section_nonlinear_response",
|
|
1863
|
+
"_hourglass_stabilization_matrix",
|
|
1864
|
+
"_local_dof_transform",
|
|
1865
|
+
"_local_frame_and_derivatives",
|
|
1866
|
+
"_material_angle",
|
|
1867
|
+
"_membrane_compression_from_state",
|
|
1868
|
+
"_membrane_compression_samples",
|
|
1869
|
+
"_mitc4_shear_b_matrix",
|
|
1870
|
+
"_mitc4_shear_samples",
|
|
1871
|
+
"_nonlinear_geometry",
|
|
1872
|
+
"_normalize",
|
|
1873
|
+
"_reference_center",
|
|
1874
|
+
"_resultant_samples",
|
|
1875
|
+
"_rigid_body_mode_matrix",
|
|
1876
|
+
"_stress_second_moment_samples",
|
|
1877
|
+
"_tri3_assumed_shear_b_matrix",
|
|
1878
|
+
"compute_jacobian",
|
|
1879
|
+
"compute_shape_functions",
|
|
1880
|
+
}
|
|
1881
|
+
)
|
|
1882
|
+
|
|
1883
|
+
|
|
1884
|
+
def _sealed_inherited_operator(name: str) -> Any:
|
|
1885
|
+
def sealed(self: StrictFlatLinearE4PLS3V2BShellElement, *args: Any, **kwargs: Any) -> Any:
|
|
1886
|
+
del args, kwargs
|
|
1887
|
+
self._unsupported(f"legacy_inherited_operator:{name}")
|
|
1888
|
+
|
|
1889
|
+
sealed.__name__ = name
|
|
1890
|
+
sealed.__qualname__ = f"StrictFlatLinearE4PLS3V2BShellElement.{name}"
|
|
1891
|
+
return sealed
|
|
1892
|
+
|
|
1893
|
+
|
|
1894
|
+
for _sealed_name in _SEALED_INHERITED_CALLABLES:
|
|
1895
|
+
setattr(
|
|
1896
|
+
StrictFlatLinearE4PLS3V2BShellElement,
|
|
1897
|
+
_sealed_name,
|
|
1898
|
+
_sealed_inherited_operator(_sealed_name),
|
|
1899
|
+
)
|
|
1900
|
+
|
|
1901
|
+
|
|
1902
|
+
type.__setattr__(
|
|
1903
|
+
StrictFlatLinearE4PLS3V2BShellElement,
|
|
1904
|
+
"_strict_flat_v2_class_frozen",
|
|
1905
|
+
True,
|
|
1906
|
+
)
|
|
1907
|
+
_initialize_final_class_authority(StrictFlatLinearE4PLS3V2BShellElement)
|
|
1908
|
+
|
|
1909
|
+
|
|
1910
|
+
__all__ = [
|
|
1911
|
+
"BLOCKED_OPERATIONS",
|
|
1912
|
+
"CBMIN3",
|
|
1913
|
+
"CAPABILITY_MATRIX",
|
|
1914
|
+
"DIRECTOR_POLICY_ID",
|
|
1915
|
+
"EQUATION_MAP_SHA256",
|
|
1916
|
+
"FORMULATION_ID",
|
|
1917
|
+
"FORMULATION_SCHEMA",
|
|
1918
|
+
"HAMMER_POINTS",
|
|
1919
|
+
"HAMMER_REFERENCE_WEIGHTS",
|
|
1920
|
+
"IMPLEMENTATION_ID",
|
|
1921
|
+
"PL_COMPLETION_POLICY_ID",
|
|
1922
|
+
"PRIMARY_SOURCE_SHA256",
|
|
1923
|
+
"QUADRATURE_AUTHORITY_ID",
|
|
1924
|
+
"RELAXATION_AUTHORITY_SHA256",
|
|
1925
|
+
"RESULTANT_POLICY_ID",
|
|
1926
|
+
"SECTION_POLICY_ID",
|
|
1927
|
+
"SELECTOR",
|
|
1928
|
+
"_ALLOWED_INHERITED_CALLABLES",
|
|
1929
|
+
"_SEALED_INHERITED_CALLABLES",
|
|
1930
|
+
"SHEAR_CORRECTION",
|
|
1931
|
+
"SOURCE_CONTRACT_SCHEMA",
|
|
1932
|
+
"SOURCE_CONTRACT_SHA256",
|
|
1933
|
+
"SUPPORTED_OPERATIONS",
|
|
1934
|
+
"StrictFlatLinearCapabilityError",
|
|
1935
|
+
"StrictFlatLinearE4PLS3V2BShellElement",
|
|
1936
|
+
]
|